/* ==========================================================================
   pum.me v3 — the film (landing page only; subpages use site.css)
   PORTED from proto/proto.css 2026-07-25. The proto remains the lab: iterate
   there with the URL knobs, re-port deliberately. Doctrine docs:
   docs/workorders/marketing-website-design-elevation/ (manifest + brief),
   copy: docs/marketing/website-copy-constitution.md.
   ========================================================================== */

/* ==========================================================================
   pum.me hero prototype — mobile composed first (workorder:
   marketing-website-design-elevation, north star refs/2073).
   Isolated from site.css on purpose: this page is a laboratory.
   ========================================================================== */

@font-face {
  font-family: 'Satoshi';
  src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Satoshi';
  src: url('/fonts/Satoshi-Medium.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --ink: #2A2848;
  --ink-soft: #504F6A;
  --primary: #14AFC3;
  --primary-deep: #0F8A9E;
  --signal: #FF4D88;
  --amber: #D99A2B;
  --glass: rgba(255, 255, 255, 0.55);
}

/* Safari 26 (Liquid Glass): at scrollY=0 the status area shows the ROOT
   background-color, never page pixels. So (a) root bg = the plate's sampled
   top-edge colour (#AED6E6) as an honest fallback, and (b) the scroll-runway
   below starts the page at scrollY=62 so Safari composites REAL plate pixels
   behind the island. See 1ar.io/updates/safari-26-liquid-glass-web. */
:root { --runway: 62px; }
html {
  -webkit-text-size-adjust: 100%;
  background-color: #AED6E6;
  min-height: 100%;
  overflow-y: scroll;
  overscroll-behavior: none;
}
body {
  font-family: 'Satoshi', system-ui, sans-serif;
  font-weight: 400;
  color: var(--ink);
  /* transparent ON PURPOSE: an opaque body background paints OVER the
     fixed z:-1 caustic canvas (html carries the root fallback colour;
     it propagates to the root layer, which sits BEHIND the canvas) */
  background-color: transparent;
  overflow-x: hidden;
}
/* (runway spacer removed — see .hero fade overlay instead) */

/* ---------- the material ---------- */
#caustics {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
}

/* ---------- nav ---------- */
.pnav {
  position: fixed;
  top: calc(12px + env(safe-area-inset-top, 0px));
  left: calc(12px + env(safe-area-inset-left, 0px));
  right: calc(12px + env(safe-area-inset-right, 0px));
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 10px 10px 10px 22px;
  border-radius: 999px;
  /* NO background / backdrop-filter here — Safari 26 samples fixed elements
     for its chrome tint; the glass lives on .pnav-glass (absolute child) */
  background: transparent;
}
.pnav-glass {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.42);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.55),
    0 8px 28px rgba(15, 138, 158, 0.10);
}
.pnav-brand {
  display: block;
  width: 62px;
  height: 24px;
  margin-right: auto; /* brand left, cta + burger right */
  background: var(--ink);
  -webkit-mask: url('/assets/wordmark.svg') left center / contain no-repeat;
  mask: url('/assets/wordmark.svg') left center / contain no-repeat;
}
/* burger — [pum · (get pum) · ☰] */
.pnav-burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4.5px;
  width: 42px;
  height: 42px;
  padding: 0 10px;
  border: none;
  border-radius: 999px;
  background: none;
  cursor: pointer;
}
.pnav-burger span {
  display: block;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease;
}
/* burger → X while the sheet is open */
html.menu-open .pnav-burger span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
html.menu-open .pnav-burger span:nth-child(2) { opacity: 0; }
html.menu-open .pnav-burger span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

/* ---------- burger sheet ----------
   Below the nav (z 40) so the burger stays tappable to close. Safari-26
   shape: transparent fixed wrapper, the sheet on an absolute child. */
.pmenu {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 26px;
  background: transparent;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
html.menu-open .pmenu { opacity: 1; pointer-events: auto; }
.pmenu-sheet {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(233, 246, 249, 0.94);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  backdrop-filter: blur(22px) saturate(1.2);
}
.pmenu-links {
  display: flex;
  flex-direction: column;
}
.pmenu-links a {
  padding: 12px 0;
  font-weight: 500;
  font-size: clamp(28px, 8vw, 40px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}
.pmenu-cta {
  align-self: flex-start;
  margin-top: 30px;
  padding: 14px 26px;
  border-radius: 999px;
  font-weight: 500;
  font-size: 16px;
  color: #fff;
  text-decoration: none;
  background: linear-gradient(118deg,
    #1FB5C9 0%, #55CBDB 26%, #9D8BE0 60%, #F784B0 100%);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.55),
    0 4px 18px rgba(20, 175, 195, 0.45);
}
/* instagram chip — desktop nav only (mobile nav is [pum · get pum · ☰],
   no room; the footer icon covers mobile) */
.pnav-ig { display: none; }
.pnav-cta {
  position: relative;
  overflow: hidden;
  padding: 11px 21px;
  border-radius: 999px;
  font-weight: 500;
  font-size: 15px;
  /* pinned: nav geometry must not inherit each page's body line-height
     (film body = normal, site body = 1.55 → the bar was ~2px taller on
     subpages and read as a shift crossing pages; 2026-07-27) */
  line-height: 1.4;
  color: #fff;
  text-shadow: 0 1px 6px rgba(15, 138, 158, 0.45);
  text-decoration: none;
  /* richer iridescence: saturated aqua -> violet -> pink, glossed */
  background: linear-gradient(118deg,
    #1FB5C9 0%, #55CBDB 26%, #9D8BE0 60%, #F784B0 100%);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.55),
    inset 0 -8px 14px rgba(110, 66, 170, 0.28),
    0 4px 18px rgba(20, 175, 195, 0.45),
    0 2px 8px rgba(247, 132, 176, 0.25);
}
/* gloss cap: a soft top highlight riding the pill */
.pnav-cta::after {
  content: '';
  position: absolute;
  left: 8%; right: 8%; top: 7%;
  height: 42%;
  border-radius: 999px;
  background: linear-gradient(to bottom, rgba(255,255,255,0.55), rgba(255,255,255,0));
  pointer-events: none;
}
.pnav-cta::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 22%,
    rgba(255,255,255,0.25) 40%, rgba(255,255,255,0.95) 50%,
    rgba(255,255,255,0.25) 60%, transparent 78%);
  transform: translateX(-110%);
  animation: cta-sheen 9s ease-in-out 3s infinite;
  pointer-events: none;
}
@keyframes cta-sheen {
  0% { transform: translateX(-110%); }
  13% { transform: translateX(110%); }
  100% { transform: translateX(110%); }
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden; /* the dial bleeds; the page must not scroll sideways */
}

/* ---------- plate mode: raster rosette over the live field ---------- */
.plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 104svh;
  object-fit: cover;
  object-position: left top;
  /* feather the plate's baked water into the live caustic field below.
     Long dissolve (58->96%): a short fade between two different water
     textures reads as a splice (seen on device). Definitive fix at
     upscale time: regenerate the plate with a calm, featureless bottom
     third so smooth hands off to smooth. */
  -webkit-mask-image: linear-gradient(to bottom,
    #000 58%, rgba(0,0,0,0.55) 78%, transparent 96%);
  mask-image: linear-gradient(to bottom,
    #000 58%, rgba(0,0,0,0.55) 78%, transparent 96%);
  animation: breathe 9s ease-in-out infinite;
}
.plated .rosette { display: none; }
/* Safari-26 zero-state fade: at scrollY=0 the status area shows only the
   root colour (#AED6E6, sampled from the plate). This overlay continues
   that exact colour into the scene so the rest state reads as designed,
   not as a bug. Scrolled states composite real plate pixels behind the
   island natively. CSS overlay, NOT baked into the asset — survives
   regeneration and upscaling. */
.plated .hero::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 140px;
  z-index: 1;
  background: linear-gradient(to bottom,
    #AED6E6 0%, rgba(174, 214, 230, 0.6) 45%, rgba(174, 214, 230, 0) 100%);
  pointer-events: none;
}
/* Plate-mode geometry is WIDTH-anchored (vw), not height-anchored (svh):
   object-fit:cover pins the image to the viewport's left-top at full width,
   so every landmark in the plate sits at a fixed multiple of vw regardless
   of the phone's aspect ratio. This is what "enforced on all mobile screen
   sizes" means mechanically. (Plate: 704x1530, disc centre ~= 100vw down.) */
.plated .hero-copy {
  position: absolute;
  left: 0; right: 0;
  margin-top: 0;
  /* 86vw ratified 2026-07-20; raised to 80vw after Lewis flagged the
     nav->headline gap (2026-07-21). Tune live via ?ct=<vw>. */
  top: 80vw;
}
.plated .float-card { top: 154vw; right: 12px; }
.plated .plate { height: min(106svh, 214vw); }
@media (min-width: 900px) {
  /* desktop plate (2079): dial bleeding off the LEFT, calm lavender water
     right — the copy column sits over the calm zone, 2076's composition
     mirrored onto the hero-object thesis */
  .plated .plate {
    height: 100svh;
    object-position: left center;
    -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 98%);
    mask-image: linear-gradient(to bottom, #000 70%, transparent 98%);
  }
  .plated .hero-copy {
    top: 30svh;
    left: 50vw;
    right: 6vw;
    padding: 0;
  }
  .plated .float-card {
    top: 68svh;
    right: 10vw;
    left: auto;
  }
}

/* the hero object — page scale, bleeding off the left edge (refs/2073) */
.rosette {
  position: absolute;
  top: 2svh;
  left: -34vw;
  width: min(124vw, 720px);
  aspect-ratio: 1;
  animation: breathe 9s ease-in-out infinite;
}
@keyframes breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.012); }
}

/* real glass: caustic light passes through the instrument */
.disc {
  position: absolute;
  inset: 4.2%;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%,
    rgba(255, 255, 255, 0.88) 0%,
    rgba(255, 255, 255, 0.66) 55%,
    rgba(236, 248, 250, 0.55) 100%);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  backdrop-filter: blur(10px) saturate(1.15);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.75),
    inset 0 -18px 42px rgba(20, 175, 195, 0.10),
    0 30px 80px rgba(15, 138, 158, 0.22);
}
.dial { position: absolute; inset: 0; width: 100%; height: 100%; }

.dial text { font-family: 'Satoshi', system-ui, sans-serif; }
.t-day-label { font-size: 24px; letter-spacing: 7px; fill: #9BA6B4; }
.t-day { font-size: 128px; fill: var(--ink); }
.t-phase { font-size: 25px; letter-spacing: 6px; fill: var(--amber); }
.t-until { font-size: 31px; fill: var(--ink-soft); }
.confidence rect { fill: rgba(255, 255, 255, 0.6); stroke: rgba(20, 175, 195, 0.25); stroke-width: 1; }
.t-conf { font-size: 17px; letter-spacing: 3px; fill: var(--primary-deep); }

/* escaped data card */
.float-card {
  position: absolute;
  z-index: 3;
  top: 28svh;
  right: 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 18px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.5);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.6),
    0 10px 30px rgba(15, 138, 158, 0.14);
  animation: card-drift 11s ease-in-out infinite;
}
@keyframes card-drift {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
.fc-icon {
  flex-shrink: 0;
  width: 36px; height: 36px;
  padding: 8px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 32%, #3EC3D4 0%, #14AFC3 75%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.55);
}
.fc-icon svg { display: block; width: 100%; height: 100%; }
.fc-body { display: flex; flex-direction: column; max-width: 200px; }
.fc-kicker {
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary-deep);
}
.fc-title { font-weight: 500; font-size: 15px; line-height: 1.25; color: var(--ink); margin: 2px 0; }
.fc-sub { font-size: 13px; color: var(--ink-soft); }

/* headline overlaps the instrument — the one enormous thing */
.hero-copy {
  position: relative;
  z-index: 4;
  padding: 0 20px;
  margin-top: 58svh;
}
.hero-copy h1 {
  font-weight: 500;
  font-size: clamp(44px, 13vw, 108px);
  line-height: 1.0;
  letter-spacing: -0.015em; /* overridable live via ?ls= (see proto.js) */
  max-width: 8em;
}
.hero-copy h1 em { font-style: italic; }

/* official Apple badge artwork (white/outline variant — lighter visual
   mass, doesn't compete with the headline); we size it, never restyle it */
.store-badge { display: inline-block; margin-top: 34px; }
.store-badge img {
  display: block;
  height: 50px;
  width: auto;
  filter: drop-shadow(0 8px 22px rgba(42, 40, 72, 0.18));
}
.sb-apple {
  width: 26px; height: 26px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath d='M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath d='M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.sb-text { display: flex; flex-direction: column; line-height: 1.15; }
.sb-small { font-size: 12px; opacity: 0.85; }
.sb-big { font-size: 20px; font-weight: 500; letter-spacing: 0.01em; }

/* (hero sting removed 2026-07-20 — headline + badge carry the hero alone) */

/* ==========================================================================
   THE FILM — one idea per viewport-height beat, composed for 390px first.
   Copy sits directly on the material (the Law: the light touches what it
   grounds); only *objects* (cards, specimens, frames) get glass.
   ========================================================================== */
.beat {
  position: relative;
  min-height: 100svh; /* a true frame: one idea per viewport (Lewis, 2026-07-21) */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 34px;
  padding: 8svh 26px;
}
/* the verse sits in the plate's fade zone but no longer PULLS into the
   hero: the old -14svh overlap collided the lines with the pattern card
   once the film existed below (Lewis on device, 2026-07-21) */
.beat-verse {
  min-height: 78svh;
  align-items: flex-start;
}
/* desktop: the stanza joins the shared content column instead of hugging
   the viewport edge (left-seat is a mobile composition; at 1000px+ it
   read as a layout accident, Lewis 2026-07-27). Ragged-left is kept —
   only the column is centered. */
@media (min-width: 768px) {
  /* full frame on desktop: the scroll cue lands here, and at 78svh the
     next band peeked in below (Lewis 2026-07-27). 78svh stays a mobile
     tightening only. */
  .beat-verse { align-items: center; min-height: 100svh; }
  .verse { width: 100%; max-width: 34em; }
}
/* a stanza, not a paragraph: the dismissals recede, the turn lands at
   full h2 scale — a size class ABOVE them, after a longer breath */
.verse {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
/* double-class selectors: these are <p>s inside a .beat, and `.beat p`
   (class+type) outranks a lone class — the stanza must win the cascade */
.verse .verse-line {
  font-size: clamp(23px, 6.6vw, 36px);
  line-height: 1.18;
  letter-spacing: -0.015em;
  /* faded via colour alpha, NOT opacity — the reveal system owns opacity */
  color: rgba(80, 79, 106, 0.74);
  max-width: 14em;
}
.verse .verse-turn {
  margin-top: 14px; /* the breath before the punch */
  font-size: clamp(29px, 8.4vw, 50px);
  line-height: 1.08;
  color: var(--ink);
  font-weight: 500;
}
/* staggered entrance: dismissal, dismissal… turn */
.js-reveal .verse .verse-line:nth-child(2) { transition-delay: 0.14s; }
.js-reveal .verse .verse-line:nth-child(3) { transition-delay: 0.3s; }

/* ---------- beat typography ---------- */
.beat-inner {
  width: 100%;
  max-width: 34em;
}
.kicker {
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--primary-deep);
  margin-bottom: 14px;
}
.beat h2 {
  font-weight: 500;
  font-size: clamp(30px, 8.4vw, 54px);
  line-height: 1.06;
  letter-spacing: -0.015em;
  margin-bottom: 22px;
  max-width: 11em;
}
.beat p {
  font-size: clamp(16px, 4.4vw, 18px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 30em;
}
.beat p + p { margin-top: 14px; }
.beat-link {
  display: inline-block;
  margin-top: 26px;
  padding: 12px 22px;
  border-radius: 999px;
  font-weight: 500;
  font-size: 15px;
  color: var(--primary-deep);
  text-decoration: none;
  background: rgba(255, 255, 255, 0.44);
  -webkit-backdrop-filter: blur(12px) saturate(1.25);
  backdrop-filter: blur(12px) saturate(1.25);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.6),
    0 6px 20px rgba(15, 138, 158, 0.10);
}

/* ---------- definition beat + the story pull ---------- */
.story-pull {
  width: 100%;
  max-width: 30em;
  padding-left: 20px;
  border-left: 2.5px solid var(--signal);
}
.story-pull p {
  font-size: clamp(18px, 4.9vw, 23px);
  line-height: 1.5;
  color: var(--ink);
}
.story-pull footer { margin-top: 14px; }
.story-pull footer a {
  font-weight: 500;
  font-size: 14px;
  color: var(--primary-deep);
  text-decoration: none;
}

/* ---------- escaped cards (crisp DOM, real shipped content) ---------- */
.esc-cluster {
  width: 100%;
  max-width: 34em;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}
.esc-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 18px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.5);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.6),
    0 10px 30px rgba(15, 138, 158, 0.14);
  animation: card-drift 11s ease-in-out infinite;
}
/* stagger the drift so clusters breathe out of phase */
.esc-card-b { animation-delay: -3.5s; margin-left: 8vw; }
.esc-card-c { animation-delay: -7s; }
.esc-row {
  width: min(100%, 340px);
  justify-content: space-between;
  margin-left: 0;
}
.esc-row .fc-title { font-size: 16px; }
.esc-count {
  flex-shrink: 0;
  font-weight: 500;
  font-size: 13px;
  color: var(--primary-deep);
}
.fc-icon-pink {
  background: radial-gradient(circle at 32% 32%, #FF6B9E 0%, #FF4D88 75%);
}
.fc-icon-phase {
  background: radial-gradient(circle at 32% 32%, #C6B4EE 0%, #9D8BE0 75%);
}
.conf-dots-inline {
  display: inline-block;
  width: 52px; height: 8px;
  background:
    radial-gradient(circle 4px at 4px 4px, var(--primary) 98%, transparent),
    radial-gradient(circle 4px at 15px 4px, var(--primary) 98%, transparent),
    radial-gradient(circle 4px at 26px 4px, var(--primary) 98%, transparent),
    radial-gradient(circle 4px at 37px 4px, var(--primary) 98%, transparent),
    radial-gradient(circle 4px at 48px 4px, rgba(20,175,195,0.25) 98%, transparent);
}

/* ---------- specimen placeholders (pixery briefs until assets exist) ---------- */
.specimen {
  width: min(100%, 360px);
  aspect-ratio: 4 / 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 6px;
  padding: 18px;
  border-radius: 24px;
  border: 1.5px dashed rgba(15, 138, 158, 0.4);
  background: rgba(255, 255, 255, 0.32);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
/* a specimen carrying its real macro: the dashed brief-frame styling drops
   away, the image fills the card. AFTER the base rule on purpose — equal
   specificity, so source order is the override. */
.specimen-live {
  padding: 0;
  border: none;
  background: none;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(15, 138, 158, 0.22);
}
.specimen-live img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sp-serial {
  align-self: flex-start;
  margin-bottom: auto;
  padding: 3px 10px;
  border-radius: 999px;
  font-weight: 500;
  font-size: 13px;
  color: #fff;
  background: var(--primary-deep);
}
.sp-tag {
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--primary-deep);
}
.sp-brief {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* ---------- journey beat ---------- */
.journey {
  width: 100%;
  max-width: 34em;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}
/* real captures landed 2026-07-27: the dashed capture-brief placeholder
   becomes a phone bezel (phone-shell's material at journey scale) with
   crossfading frames driven by the jstep mechanism in film.js */
.device-frame {
  position: relative;
  width: min(56vw, 235px);
  aspect-ratio: 9 / 19.5;
  padding: 6px;
  border-radius: 34px;
  background: linear-gradient(160deg, #3E3D4C 0%, #232230 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.16),
    0 24px 60px rgba(15, 138, 158, 0.22);
  overflow: hidden;
}
.jframe {
  position: absolute;
  inset: 6px;
  width: calc(100% - 12px);
  height: calc(100% - 12px);
  object-fit: cover;
  border-radius: 28px;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.jframe.active { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .jframe { transition: none; }
}
.journey-steps {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.jstep {
  padding: 9px 16px;
  border: none;
  border-radius: 999px;
  font-family: inherit;
  font-weight: 500;
  font-size: 13.5px;
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.38);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
  cursor: pointer;
  transition: background 0.3s ease, color 0.3s ease;
}
.jstep.active {
  color: #fff;
  background: var(--primary);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.35),
    0 4px 14px rgba(20, 175, 195, 0.35);
}

/* ---------- FAQ ---------- */
.beat-faq { align-items: flex-start; }
.beat-faq .beat-inner, .faq { max-width: 34em; margin: 0 auto; }
.faq {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.faq details {
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.42);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  backdrop-filter: blur(12px) saturate(1.2);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55);
  overflow: hidden;
}
.faq summary {
  padding: 16px 20px;
  font-weight: 500;
  font-size: 16px;
  cursor: pointer;
  list-style: none;
  position: relative;
  padding-right: 44px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+';
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 20px;
  color: var(--primary-deep);
  transition: transform 0.25s ease;
}
.faq details[open] summary::after { transform: translateY(-50%) rotate(45deg); }
.faq details p {
  padding: 0 20px 18px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.faq details a { color: var(--primary-deep); }

/* ---------- closer ---------- */
.beat-closer { text-align: center; min-height: 80svh; }
.beat-closer .beat-inner { display: flex; flex-direction: column; align-items: center; }
.closer-h {
  font-weight: 500;
  font-size: clamp(40px, 12vw, 84px);
  letter-spacing: -0.015em;
  line-height: 1.0;
  margin-bottom: 18px;
}
.beat-closer p { max-width: 24em; }
.beat-closer .store-badge { margin-top: 30px; }
.beat-closer .store-badge img { height: 54px; }
.ship-log {
  margin-top: 34px;
  font-size: 13px;
  opacity: 0.65;
}

/* ---------- footer ---------- */
.pfooter {
  padding: 40px 26px calc(34px + env(safe-area-inset-bottom, 0px));
  text-align: center;
}
.pfooter-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 22px;
  margin-bottom: 20px;
}
.pfooter-links a {
  font-weight: 500;
  font-size: 14px;
  color: var(--ink-soft);
  text-decoration: none;
}
.pfooter-social {
  display: flex;
  justify-content: center;
  margin-bottom: 18px;
}
.pfooter-social a {
  display: inline-flex;
  padding: 6px;
  color: var(--ink-soft);
}
.pfooter-social svg { width: 22px; height: 22px; display: block; }
.pfooter-meta {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: var(--ink-soft);
  opacity: 0.75;
}

/* ---------- scroll-entrance reveals ---------- */
/* Gated on .js-reveal (set by proto.js) so content is never hidden if JS
   fails; reduced-motion kills the translate but keeps the fade short. */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.js-reveal .reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

/* (sticky store CTA removed 2026-07-21 — cramped every beat, redundant
   with the persistent nav CTA. Its Safari-26 doctrine — fixed wrapper
   transparent, ink on an absolute child — lives on in the nav.) */

/* ---------- wider viewports: preview the film, don't fight it ----------
   The prototype is the mobile composition. On desktop the dial grows to its
   cap and the copy column widens; the real desktop hero (refs/2076) is a
   later derivation, not this file's job. */
@media (min-width: 900px) {
  .rosette { left: -160px; top: 6svh; }
  .hero-copy { margin-top: 0; position: absolute; z-index: 4; right: 8vw; top: 34svh; max-width: 46vw; }
  .hero-copy h1 { font-size: clamp(64px, 7.2vw, 118px); }
  .float-card { right: 10vw; top: 74svh; }
}

/* ==========================================================================
   VARIANT B — ?ground=flat  (2026-07-21, the material-budget recomposition)
   The lesson from iOS liquid glass, applied to the site: a special material
   reads as special only against a plain ground. So —
     · nav: solid, page-coloured, full-bleed (the two-chromes rule: CSS glass
       next to Safari 26's liquid glass invites a comparison we lose)
     · ground: flat for the whole film; the caustic material is confined to
       the hero (ladder of retreat: hero-animated → hero-static → fully flat)
     · hero: the real app, framed, cropped at the fold — for a pure app the
       UI is the product shot (Flighty grammar; NC's ring-in-flowers is a
       hardware privilege)
   A/B against the plate composition on device before ratifying.
   ========================================================================== */

/* B-only hero elements, hidden in variant A */
.hero-sub { display: none; }
.hero-phone { display: none; }

/* the flat ground: root carries it (Safari tints its chrome from this,
   nav matches it, so island / url-bar / page read as one surface) */
html.ground-flat { background-color: #E6F4F7; }

/* the material, demoted from ground to moment: hero-only, dissolving into
   the flat ground before the verse. proto.js REPARENTS the canvas into the
   hero under flat mode, so inset:0 tracks the hero's real height — a canvas
   pinned to 100svh ends in a hard line the moment the hero outgrows one
   viewport (seen on Lewis's phone, 2026-07-22). */
.ground-flat #caustics {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  -webkit-mask-image: linear-gradient(to bottom,
    #000 50%, rgba(0,0,0,0.5) 76%, transparent 96%);
  mask-image: linear-gradient(to bottom,
    #000 50%, rgba(0,0,0,0.5) 76%, transparent 96%);
}

/* nav: solid bar, colour = page colour (Natural Cycles pattern — the bar
   caps the page; the hero material bleeds out from under it) */
.ground-flat .pnav {
  top: 0;
  left: 0;
  right: 0;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 14px 10px 20px;
  border-radius: 0;
}
.ground-flat .pnav-glass {
  border-radius: 0;
  background: #E6F4F7;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: 0 1px 0 rgba(42, 40, 72, 0.07);
}

/* ---------- desktop nav: the floating glass pill returns (Lewis 2026-07-25)
   The solid full-width bar + burger are a MOBILE Safari-26 concession (two
   competing liquid-glass chromes; ours loses). Desktop browsers have no such
   chrome to clash with, so we restore the original floating pill with its
   visible link row and retire the burger. Link row is display:none on mobile
   (the burger owns nav there); it appears only at this breakpoint. */
.pnav-bar-links { display: none; }
@media (min-width: 768px) {
  /* Geometry mirrors site.css's desktop .pnav exactly so the pill never
     jumps between landing and subpages. BOTH selectors are required:
     film.js force-adds .ground-flat at runtime (flat is ratified), so a
     lone unscoped .pnav loses to the mobile solid-bar rule
     `.ground-flat .pnav { top: 0 }` on specificity — that pinned the
     desktop pill to the viewport top (caught 2026-07-27). */
  .pnav,
  .ground-flat .pnav {
    top: 16px;
    left: 0; right: 0;
    width: min(1080px, calc(100% - 48px));
    margin: 0 auto;
    padding: 9px 10px 9px 26px;
    border-radius: 999px;
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    align-items: center;
    gap: 20px;
  }
  .pnav-ig {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-right: -8px; /* tuck against the cta; grid gap reads too loose */
    border-radius: 999px;
    color: var(--ink-soft);
    justify-self: end;
  }
  .pnav-ig:hover { color: var(--ink); }
  .pnav-ig svg { width: 21px; height: 21px; }
  .ground-flat .pnav-glass {
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.55);
    -webkit-backdrop-filter: blur(24px) saturate(1.8);
    backdrop-filter: blur(24px) saturate(1.8);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.6),
      0 1px 3px rgba(42, 40, 72, 0.06),
      0 12px 32px rgba(42, 40, 72, 0.10);
  }
  .pnav-brand { margin-right: 0; justify-self: start; }
  .pnav-bar-links {
    display: flex;
    justify-self: center;
    align-items: center;
    gap: 30px;
  }
  .pnav-bar-links a {
    font-size: 16px;
    font-weight: 500;
    line-height: 1.4; /* pinned — see .pnav-cta note */
    color: var(--ink-soft);
    text-decoration: none;
  }
  .pnav-bar-links a:hover,
  .pnav-bar-links a[aria-current="page"] { color: var(--ink); }
  .pnav-cta { justify-self: end; }
  .pnav-burger { display: none; }
}
.ground-flat .pmenu-sheet { background: rgba(230, 244, 247, 0.96); }

/* hero B: headline → sub → badge → framed app cropped at the fold */
.ground-flat .rosette,
.ground-flat .plate,
.ground-flat .hero > .float-card { display: none; }
.ground-flat .hero {
  display: flex;
  flex-direction: column;
  /* bottom padding gives the caustic fade room to finish BELOW the phone */
  padding: calc(84px + env(safe-area-inset-top, 0px)) 26px 11svh;
}
.ground-flat .hero::before { display: none; } /* plated zero-state overlay */
.ground-flat .hero-copy {
  position: static;
  margin-top: 4svh;
  padding: 0;
}
.ground-flat .hero-copy h1 { font-size: clamp(40px, 11.4vw, 92px); }
.ground-flat .hero-sub {
  display: block;
  margin-top: 16px;
  font-size: clamp(16px, 4.6vw, 19px);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 24em;
}
.ground-flat .store-badge { margin-top: 24px; }
/* white badge variant sits on the caustic hero in B too — keep it */

.ground-flat .hero-phone {
  display: block;
  position: relative;
  align-self: center;
  /* no forced fold-crop: the hero is taller than one viewport, so at load
     the SCREEN crops the phone (the honest Flighty grammar) and the full
     phone reveals on scroll — never a chopped bezel mid-film */
  margin-top: 7svh;
  width: min(66vw, 280px);
}
.phone-shell {
  padding: 9px;
  border-radius: 46px;
  background: linear-gradient(160deg, #3E3D4C 0%, #232230 100%);
  box-shadow:
    inset 0 0 0 1.5px rgba(255, 255, 255, 0.16),
    0 30px 70px rgba(15, 138, 158, 0.30);
}
.phone-shell img {
  display: block;
  width: 100%;
  border-radius: 37px;
}
/* ghost notifications: heavily faded, BEHIND the phone (Flighty grammar).
   They poke off both edges; the hero's overflow:hidden clips them without
   creating sideways scroll. */
.hero-ghosts { position: absolute; inset: 0; z-index: 0; }
.phone-shell { position: relative; z-index: 1; }
.ghost {
  position: absolute;
  opacity: 0.42;
  transform: scale(0.92);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6); /* no heavy drop — ghosts sit IN the water, not on it */
}
.ghost .fc-body { max-width: 176px; }
.ghost-a { left: -30vw; top: 9%; }
.ghost-b { right: -34vw; top: 36%; animation-delay: -3.5s; }
.ghost-c { left: -26vw; top: 66%; animation-delay: -7s; }

/* glass calms down on a flat ground: blur has nothing to refract, so the
   cards become soft solids (translucency stays for tint, filter goes) */
.ground-flat .esc-card,
.ground-flat .faq details,
.ground-flat .beat-link,
.ground-flat .specimen,
.ground-flat .jstep:not(.active) {
  background: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* ---------- chapter bands (Lewis 2026-07-22, from v2's alternation) ----------
   Rich static mesh for the punchy enlarged beats — verse, privacy, circle,
   closer — near-white for the lengthy body chapters. Hard edges on purpose:
   the cut IS the chapter break (v2 evidence). Static gradient, never the
   animated caustic: the material budget spends motion on the hero only. */
.ground-flat .band-rich {
  background:
    radial-gradient(110% 85% at 12% 16%, rgba(164, 224, 234, 0.9) 0%, rgba(164, 224, 234, 0) 60%),
    radial-gradient(95% 75% at 88% 84%, rgba(190, 176, 232, 0.75) 0%, rgba(190, 176, 232, 0) 62%),
    linear-gradient(135deg, #C4E8F0 0%, #C9CCEB 100%);
}
/* objects on a rich band get their glass back — there is finally something
   behind them worth refracting */
.ground-flat .band-rich .beat-link {
  background: rgba(255, 255, 255, 0.5);
  -webkit-backdrop-filter: blur(12px) saturate(1.25);
  backdrop-filter: blur(12px) saturate(1.25);
}

/* ---------- FAQ: return to v2's hairline list (Lewis 2026-07-22) ----------
   The glass cards cluttered; the v2 divider list is calmer and more
   readable. Rows on the ground itself, one hairline between each. */
.ground-flat .faq { gap: 0; }
.ground-flat .faq details {
  border-radius: 0;
  background: none;
  box-shadow: none;
  border-bottom: 1px solid rgba(42, 40, 72, 0.13);
  overflow: visible;
}
.ground-flat .faq summary { padding: 19px 44px 19px 0; }
.ground-flat .faq summary::after { right: 4px; }
.ground-flat .faq details p { padding: 0 0 20px; }

@media (min-width: 900px) {
  /* B on desktop: preview only (a real desktop derivation comes after the
     mobile verdict) — clamp the phone, restore side-by-side breathing room */
  .ground-flat .hero {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 6vw;
    /* padding-top 0 + the mobile 11svh bottom padding biased the flex
       centre UP ~5.5svh, on top of the nav eating the top of the view —
       "everything too high" (Lewis 2026-07-27). Bias down instead: clear
       the nav, and leave the bottom to the scroll cue. */
    padding-top: 14svh;
    padding-bottom: 6svh;
  }
  .ground-flat .hero-copy { margin-top: 0; position: static; max-width: 46vw; }
  /* balance the wrap: "a tracker that / gets endo" — never an orphaned
     "endo" on its own line (2026-07-27, Lewis: first view unbalanced) */
  .ground-flat .hero-copy h1 { max-width: 7em; }
  .ground-flat .hero-phone {
    align-self: center;
    margin-top: 0;
    /* svh term keeps the phone clear of the nav on short/wide windows */
    width: min(26vw, 348px, 34svh);
  }
  /* ghosts hug the phone on desktop — a loose notification orbit, not
     cards scattered across the copy column (the mobile vw offsets threw
     them into the headline zone on wide screens) */
  .ground-flat .ghost-a { left: -76%; right: auto; top: 11%; }
  /* no right-side ghost on desktop: the right margin varies too much with
     viewport width, so the card either clips mid-word or collapses (abs
     shrink-to-fit). Two legible cards on the copy-facing flank instead. */
  .ground-flat .ghost-b { display: none; }
  .ground-flat .ghost-c { left: -66%; right: auto; top: 72%; }

  /* scroll cue: glass chip, gentle bob, fades once scrolling starts */
  .ground-flat .scroll-cue {
    display: flex;
    position: absolute;
    left: 50%;
    bottom: 3.5svh;
    z-index: 5;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 999px;
    color: var(--ink-soft);
    background: rgba(255, 255, 255, 0.5);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    backdrop-filter: blur(14px) saturate(1.3);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.6),
      0 10px 30px rgba(15, 138, 158, 0.14);
    animation: cue-bob 2.6s ease-in-out infinite;
    transition: opacity 0.5s ease;
  }
  .ground-flat .scroll-cue svg { width: 20px; height: 20px; }
  html.scrolled .scroll-cue { opacity: 0; pointer-events: none; }
}
/* mobile: no cue — the fold-cropped phone already implies more below.
   The markup also carries a bare `hidden` attribute: that is the failsafe,
   not belt-and-braces. If this stylesheet is ever stale or unreachable
   (2026-07-29: nginx served css/js `immutable` for 7d with no fingerprint,
   so iPhone Safari had new HTML + week-old CSS) the cue renders as a raw,
   default-blue, natural-size SVG mid-page. `hidden` makes the unstyled
   state invisible; the desktop rule above is an author rule and beats the
   UA sheet, so the cue still appears at ≥900px. */
.scroll-cue { display: none; }
@keyframes cue-bob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 7px); }
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .rosette, .float-card, .esc-card, .plate, .scroll-cue { animation: none; }
  .pnav-cta::before { animation: none; }
  .pmenu, .pnav-burger span { transition: none; }
  .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
  .jstep { transition: none; }
}
