/* ==========================================================================
   THE WORLD STAGE  ·  2026-09-11

   Founder: "the portfolio preview is so small, how will one feel the
   experience if you keep it in a rectangle box?"

   So the box is gone. A design opens EDGE TO EDGE — 100vw × 100dvh, no border,
   no radius, no fake browser bar, no "Full screen" button, because there is
   nothing left to go full screen from. The world is the page; the only chrome
   is one slim bar laid over the top of it, which fades away when the pointer
   rests and comes back the moment it moves.

   ONE component, three callers:
     · /portfolio-designs        — a card opens the stage in place
     · /portfolio-designs/<id>   — the same stage IS the page
     · /cover-letter-designs     — the same stage holding paper, not a website

   Namespace `.ws*`. Nothing here matches `.pf-stage`, so the landing hero's
   `<div class="pf-stage">` (conservatory.css) can never be reached from this
   file — the leak that cost the hero 20px in August cannot happen again.
   ========================================================================== */

.folio-v5 dialog.ws {
  position: fixed;
  inset: 0;
  z-index: 60;
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  overflow: hidden;
  background: var(--cx-cons-bone);
  color: var(--cx-ink);
}
/* an author `display` beats the user agent's `dialog:not([open]){display:none}`
   at any specificity, so the closed state is restated here rather than left to
   the browser (the bug that once laid out a whole closed sheet at the foot of
   step 04). */
.folio-v5 dialog.ws:not([open]) { display: none; }
.folio-v5 dialog.ws[open] { display: block; }
.folio-v5 .ws::backdrop { background: var(--cx-ink); }

/* --------------------------------------------------------------- the world */
.folio-v5 .ws-world {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--cx-cons-paper);
  transform-origin: top left;
}
.folio-v5 .ws-frames { position: absolute; inset: 0; }
.folio-v5 .ws-frames iframe {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--cx-cons-paper);
  opacity: 0;
  transition: opacity 420ms ease;
}
.folio-v5 .ws-frames iframe[data-ws-live="1"] { opacity: 1; }
.folio-v5 .ws-frames iframe[data-ws-ahead="1"] {
  /* the design one step ahead is loading, not showing: it is kept out of the
     reader's way and out of the accessibility tree until they step to it. */
  opacity: 0;
  pointer-events: none;
}

/* ----------------------------------------------------------- the skeleton
   Paper first, then the design's own poster, then the real page. The poster
   giving way to the page it is a photograph OF is the one dissolve the brand
   allows: it is the same picture arriving at full resolution. */
.folio-v5 .ws-skeleton {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--cx-cons-paper);
  opacity: 1;
  transition: opacity 460ms ease, visibility 0s linear 0s;
  /* FIX 2026-09-11 (founder: "scrolling is not happening when I click any
     portfolio design") — the skeleton dissolved to opacity 0 but stayed a
     full-size layer above the world, so every wheel and touch landed on it.
     A skeleton never needs input, and once gone it must not exist for the
     pointer at all. */
  pointer-events: none;
}
.folio-v5 .ws-skeleton.is-gone { opacity: 0; visibility: hidden; transition: opacity 460ms ease, visibility 0s linear 460ms; }
.folio-v5 .ws-poster {
  position: absolute;
  inset: 0;
  background-position: top center;
  background-size: cover;
  background-repeat: no-repeat;
  opacity: .5;
  filter: saturate(.72);
}
.folio-v5 .ws-skeleton > i {
  position: absolute;
  top: 0;
  left: 0;
  height: 2px;
  width: 24%;
  background: var(--cx-cons-vermilion);
  animation: ws-thread 1400ms cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes ws-thread {
  0% { left: -24%; }
  100% { left: 100%; }
}

/* ------------------------------------------------------------- the paper
   The letter's world: no website, one sheet, centred at reading size on the
   same full-bleed ground. Same component, same bar. */
.folio-v5 .ws[data-ws-kind="letter"] .ws-world {
  display: block;
  overflow: auto;
  padding: 74px clamp(14px, 4vw, 46px) clamp(40px, 6vw, 78px);
  background:
    linear-gradient(rgba(23, 23, 20, .04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(23, 23, 20, .04) 1px, transparent 1px),
    var(--cx-cons-bone);
  background-size: 26px 26px;
}
/* The sheet is SCALED, not restyled. A letter design decides its own type
   sizes, and the stage may not argue with them — so the whole paper is shown
   larger, exactly as authored, the way holding a page closer works. 12px of
   body type becomes 14.4px on screen and every rule and margin grows with it. */
.folio-v5 .ws-paper {
  position: relative;
  width: min(700px, 100%);
  margin: 0 auto;
  zoom: 1.2;
}
.folio-v5 .ws-paper > .pf-tape { top: -11px; left: 50%; margin-left: -37px; --tr: -3deg; }
.folio-v5 .ws-paper .letter-paper {
  width: 100%;
  margin: 0;
  border: 1px solid var(--cx-cons-hair);
  box-shadow: var(--cx-shadow-raise);
}
.folio-v5 .ws-quiet {
  display: block;
  width: min(760px, 100%);
  margin: 16px auto 0;
  color: var(--cx-cons-quiet);
  font: 500 11.5px/1.5 var(--cx-font-ui);
  text-align: center;
}

/* ------------------------------------------------------------ phone view
   A phone is a device, not a narrower rectangle: the world keeps its own
   width and stands in a frame on a dimmed ground. */
.folio-v5 .ws[data-ws-device="phone"] { background: color-mix(in srgb, var(--cx-ink) 58%, var(--cx-cons-bone)); }
.folio-v5 .ws[data-ws-device="phone"] .ws-world {
  inset: auto;
  top: 50%;
  left: 50%;
  /* 390 × 844 is the VIEWPORT the design has to answer, so the frame is that
     plus its own 8px rim: the iframe inside measures exactly 390 × 844. */
  width: 406px;
  max-width: calc(100vw - 16px);
  height: 860px;
  max-height: calc(100dvh - 16px);
  transform: translate(-50%, -50%);
  transform-origin: center;
  border: 8px solid var(--cx-ink);
  border-radius: 34px;
  box-shadow: 0 42px 92px rgba(23, 23, 20, .42);
}
.folio-v5 .ws[data-ws-device="phone"][data-ws-kind="letter"] .ws-world { padding: 20px 14px 28px; }

/* ----------------------------------------------------------------- the bar
   One slim bar over the top of the world. It carries nothing the world needs
   and everything the reader does, and it is `pointer-events: none` apart from
   its own controls, so the first 48px of the world stay live underneath. */
.folio-v5 .ws-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 48px;
  padding: 0 clamp(10px, 1.4vw, 18px);
  border-bottom: 1px solid color-mix(in srgb, var(--cx-ink) 16%, transparent);
  background: color-mix(in srgb, var(--cx-cons-paper) 86%, transparent);
  -webkit-backdrop-filter: blur(12px) saturate(1.06);
  backdrop-filter: blur(12px) saturate(1.06);
  pointer-events: none;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 320ms ease, transform 320ms ease;
}
.folio-v5 .ws-bar a,
.folio-v5 .ws-bar button,
.folio-v5 .ws-bar .ws-device { pointer-events: auto; }
.folio-v5 .ws[data-ws-barstate="rest"] .ws-bar {
  opacity: 0;
  transform: translateY(-8px);
}

/* THE TOP EDGE IS ALWAYS LIVE. While the bar rests, a 6px strip across the
   very top of the stage stays listening: a pointer reaching for the bar the
   way a reader reaches for a menu — up, to the edge — wakes it even if the
   world underneath swallowed every move on the way there. It is invisible, it
   is only live while the bar is away, and it sits above the bar so the wake
   happens before the bar's own controls arrive under the pointer. */
.folio-v5 .ws-hot {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 6;
  display: block;
  height: 6px;
  pointer-events: none;
}
.folio-v5 .ws[data-ws-barstate="rest"] .ws-hot { pointer-events: auto; }

.folio-v5 .ws-back {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  min-height: 32px;
  padding: 0 12px 0 9px;
  cursor: pointer;
  border: 1px solid var(--cx-cons-hair);
  border-radius: 999px;
  background: var(--cx-cons-paper);
  color: var(--cx-ink);
  font: 500 11.5px/1 var(--cx-font-ui);
  text-decoration: none;
  white-space: nowrap;
}
.folio-v5 .ws-back:hover { border-color: var(--cx-ink); }

.folio-v5 .ws-id {
  display: flex;
  align-items: baseline;
  gap: 9px;
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
}
.folio-v5 .ws-id h2 {
  margin: 0;
  color: var(--cx-ink);
  font: 400 19px/1 var(--cx-font-display);
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.folio-v5 .ws-note {
  min-width: 0;
  color: var(--cx-cons-quiet);
  font: 500 11px/1.2 var(--cx-font-ui);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.folio-v5 .ws-position {
  flex: 0 0 auto;
  color: var(--cx-cons-faint);
  font: 500 10.5px/1 var(--cx-font-ui);
  letter-spacing: .12em;
  white-space: nowrap;
}

.folio-v5 .ws-controls {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
}
.folio-v5 .ws-step {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  cursor: pointer;
  border: 1px solid var(--cx-cons-hair);
  border-radius: 50%;
  background: var(--cx-cons-paper);
  color: var(--cx-ink);
  font-size: 14px;
  line-height: 1;
}
.folio-v5 .ws-step:hover { border-color: var(--cx-ink); }
.folio-v5 .ws-device {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--cx-cons-hair);
  border-radius: 999px;
  background: var(--cx-cons-paper);
}
.folio-v5 .ws-device button {
  min-height: 26px;
  padding: 0 11px;
  cursor: pointer;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--cx-cons-char);
  font: 500 11px/1 var(--cx-font-ui);
}
.folio-v5 .ws-device button.on {
  background: var(--cx-ink);
  color: var(--cx-cons-paper);
}
.folio-v5 .ws-use { min-height: 32px; }
.folio-v5 .ws-close {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  cursor: pointer;
  border: 1px solid var(--cx-cons-hair);
  border-radius: 50%;
  background: var(--cx-cons-paper);
  color: var(--cx-ink);
  font-size: 17px;
  line-height: 1;
}
.folio-v5 .ws-close:hover { border-color: var(--cx-ink); }

/* the button that opens a stage from a page that is not a showcase (04 ·
   Check asks for the letter full size). It replaces .pf-stage-fullbtn. */
.folio-v5 .ws-openbtn {
  min-height: 34px;
  padding: 8px 14px;
  cursor: pointer;
  border: 1px solid var(--cx-cons-hair);
  border-radius: 999px;
  background: var(--cx-cons-paper);
  color: var(--cx-ink);
  font: 500 11.5px/1 var(--cx-font-ui);
}
.folio-v5 .ws-openbtn:hover { border-color: var(--cx-ink); }

/* the stage owns the viewport while it is open: the page behind it must not
   keep its own scrollbar or scroll under the world. */
body.folio-v5.ws-open { overflow: hidden; }

/* ----------------------------------------------------------------- phone */
@media (max-width: 720px) {
  .folio-v5 .ws-bar { gap: 6px; padding: 0 8px; }
  /* law 10 · every target here is 40×40 or more, and none of them may be
     squeezed below it by the flex row — the title is what gives way. */
  .folio-v5 .ws-back { flex: 0 0 auto; min-width: 40px; min-height: 40px; padding: 0; justify-content: center; }
  .folio-v5 .ws-back b { display: none; }
  .folio-v5 .ws-note,
  .folio-v5 .ws-device { display: none; }
  .folio-v5 .ws-id h2 { font-size: 15px; }
  .folio-v5 .ws-position { display: none; }
  .folio-v5 .ws-step { flex: 0 0 auto; width: 40px; min-width: 40px; height: 40px; }
  .folio-v5 .ws-close { flex: 0 0 auto; width: 40px; min-width: 40px; height: 40px; }
  .folio-v5 .ws-use { min-height: 40px; padding: 0 11px; font-size: 11px; white-space: nowrap; }
  .folio-v5 .ws[data-ws-kind="letter"] .ws-world { padding: 60px 10px 34px; }
  /* a phone has no room to hold the page closer: the sheet takes the width it
     has and keeps its authored type. */
  .folio-v5 .ws-paper { zoom: 1; width: 100%; }
  .folio-v5 .ws-quiet { width: 100%; margin-top: 14px; }
}

/* a short window is not a phone, but the bar still has to fit one row */
@media (max-height: 560px) {
  .folio-v5 .ws-note { display: none; }
}

/* --------------------------------------------------------------- reduced
   motion is a PERFORMANCE, and a performance stops when it is asked to. */
@media (prefers-reduced-motion: reduce) {
  .folio-v5 .ws-world,
  .folio-v5 .ws-bar,
  .folio-v5 .ws-skeleton,
  .folio-v5 .ws-frames iframe {
    transition: none !important;
    animation: none !important;
  }
  .folio-v5 .ws-skeleton > i { display: none; }
}
