/* =====================================================================
   FORVR — shared editorial system
   One centred column, one alignment, one easing curve, across every page.

   Loaded by every page so the "sin" is identical site-wide. Page-specific
   overrides stay inline in that page and must be ID-scoped to win, because
   this file is linked in <head> and page <style> blocks come after it.
   ===================================================================== */

:root {
  /* The centred editorial column every page aligns to. */
  --col: 1080px;
  --col-narrow: 760px;
  --gut: 24px;
  /* One curve. Everything that moves on this site uses it. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 0.35s;
  --dur: 0.85s;
}
@media (min-width: 768px) { :root { --gut: 40px; } }

/* ---------------------------------------------------------------------
   The column
   --------------------------------------------------------------------- */
.ed {
  width: 100%;
  max-width: var(--col);
  margin-inline: auto;
  padding-inline: var(--gut);
  text-align: center;
}
.ed-narrow { max-width: var(--col-narrow); margin-inline: auto; }

/* ---------------------------------------------------------------------
   A chapter: one idea, one movement of the scroll
   --------------------------------------------------------------------- */
/* Vertical rhythm is deliberately tight. An earlier pass used
   clamp(96px, 14vh, 180px) and the homepage ran to 11.7 viewports, which
   Rory read as too much scrolling. Whitespace is doing the same job here at
   two thirds the cost. */
.chapter {
  position: relative;
  padding: clamp(64px, 9vh, 112px) 0;
  border-top: 1px solid var(--line, #e5e5e5);
}
.chapter.is-dark { background: #070707; border-top-color: rgba(255,255,255,0.08); }
.chapter.is-dark, .chapter.is-dark * { color: #f2f2f2; }
.chapter.is-dark .dot, .chapter.is-dark .ch-mark::before { color: var(--accent, #5BC2E7); }
.chapter.is-dark .ch-body { color: rgba(242,242,242,0.72); }

/* Chapter marker — the running index. Same device, same place, every chapter. */
.ch-mark {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--muted, #6a6a6a);
  margin-bottom: clamp(20px, 2.6vh, 32px);
}
.ch-mark::before {
  content: ''; width: 7px; height: 7px; background: var(--accent, #5BC2E7);
  display: block; flex: 0 0 auto;
}
.ch-mark .ch-no { color: var(--accent, #5BC2E7); }

.ch-h {
  font-family: var(--display); font-weight: 900; text-transform: uppercase;
  line-height: 0.9; letter-spacing: -0.02em;
  font-size: clamp(40px, 6.4vw, 104px);
  max-width: 16ch; margin-inline: auto;
}
.ch-h.is-wide { max-width: 22ch; }

.ch-lede {
  font-size: clamp(19px, 2vw, 28px);
  line-height: 1.45; letter-spacing: -0.01em;
  max-width: 30ch; margin: clamp(20px, 2.8vh, 32px) auto 0;
  color: rgba(10,10,10,0.88);
}
.chapter.is-dark .ch-lede { color: rgba(242,242,242,0.9); }

.ch-body {
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.72; letter-spacing: 0.01em;
  max-width: 62ch; margin: clamp(18px, 2.4vh, 28px) auto 0;
  color: rgba(10,10,10,0.66);
}
.ch-body + .ch-body { margin-top: 1.1em; }

/* Structured fact rows — how retired sections' information is kept without
   giving each point an animated screen of its own. */
.ch-facts {
  /* Tighter gap when the columns stack on a phone: three or four stacked
     facts at a 32px gap was adding most of a viewport per chapter. */
  display: grid; gap: 22px;
  grid-template-columns: 1fr;
  margin-top: clamp(32px, 4.5vh, 56px);
  text-align: center;
}
@media (min-width: 760px) {
  .ch-facts { gap: clamp(32px, 4vw, 56px); }
  .ch-facts.cols-2 { grid-template-columns: repeat(2, 1fr); }
  .ch-facts.cols-3 { grid-template-columns: repeat(3, 1fr); }
  .ch-facts.cols-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1080px) { .ch-facts.cols-4 { grid-template-columns: repeat(4, 1fr); } }
.ch-fact-n {
  display: block; font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.22em; color: var(--accent, #5BC2E7); margin-bottom: 14px;
}
.ch-fact-h {
  font-family: var(--display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(20px, 2vw, 27px); line-height: 1.02; letter-spacing: -0.01em;
  margin-bottom: 12px;
}
.ch-fact-p {
  font-size: 14.5px; line-height: 1.65; color: rgba(10,10,10,0.62);
  max-width: 34ch; margin-inline: auto;
}
.chapter.is-dark .ch-fact-p { color: rgba(242,242,242,0.66); }

.ch-cta { margin-top: clamp(30px, 4vh, 48px); display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }

/* ---------------------------------------------------------------------
   Fact cards that swipe an image in on hover.

   Add .has-media to a .ch-facts row, then give each .ch-fact a --media
   image and a data-dir of top | bottom | left | right. The panel slides in
   from that edge and the text flips to light over it. Alternating the
   direction across a row makes the cards read as one choreographed move
   rather than four identical fades.
   --------------------------------------------------------------------- */
/* Rory (2026-07-30): "little boxes and outlines just to give them a bit more
   life and depth" — every fact card is a bordered box at rest, lifting with
   an accent edge on hover. */
.ch-facts .ch-fact {
  border: 1px solid var(--line, #e5e5e5);
  transition: transform 0.45s var(--ease), border-color 0.45s var(--ease), box-shadow 0.45s var(--ease);
  /* Idle float — a slow breathe so the cards have life before anyone touches
     them (Rory 2026-07-30). Small travel on purpose: the hover lift replaces
     the animation, and a big float would make that handover visibly snap.
     Durations and negative delays are varied per position so the row never
     bobs in unison. */
  animation: factFloat 6.4s ease-in-out infinite;
}
.ch-facts .ch-fact:nth-child(2n)   { animation-duration: 7.3s; animation-delay: -2.1s; }
.ch-facts .ch-fact:nth-child(3n)   { animation-duration: 6.9s; animation-delay: -3.4s; }
.ch-facts .ch-fact:nth-child(4n+1) { animation-delay: -4.6s; }
@keyframes factFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}
.ch-facts .ch-fact:hover { animation: none; }
.ch-facts .ch-fact:hover {
  transform: translateY(-4px);
  border-color: rgba(91, 194, 231, 0.65);
  box-shadow: 0 16px 36px rgba(10, 10, 12, 0.10);
}
@media (prefers-reduced-motion: reduce) {
  .ch-facts .ch-fact, .ch-facts .ch-fact:hover { transform: none; transition: none; box-shadow: none; animation: none; }
}

.ch-facts.has-media .ch-fact {
  position: relative;
  overflow: hidden;
  /* Tight on phones, where the panel is an always-on backdrop rather than a
     hover reveal, and seven padded cards cost most of a viewport. */
  padding: 16px 14px;
  isolation: isolate;
}
@media (min-width: 760px) {
  .ch-facts.has-media .ch-fact { padding: clamp(22px, 3vh, 34px) 18px; }
}
.ch-facts.has-media .ch-fact::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  /* --media must be a ROOT-relative url(). A relative one resolves against
     THIS stylesheet (/static/), not the page, so `_proto/...` silently became
     `/static/_proto/...` and 404'd. */
  background-color: #101012;   /* dark ground even if the image fails */
  background-image: var(--media);
  background-size: cover;
  background-position: center;
  /* Scrim baked into the same layer, so the panel arrives already legible
     instead of needing a second element to fade in behind the text. */
  box-shadow: inset 0 0 0 100vmax rgba(6,6,6,0.52);
  transition: transform 0.62s var(--ease);
  will-change: transform;
}
.ch-facts.has-media .ch-fact[data-dir="top"]::before    { transform: translate3d(0, -101%, 0); }
.ch-facts.has-media .ch-fact[data-dir="bottom"]::before { transform: translate3d(0, 101%, 0); }
.ch-facts.has-media .ch-fact[data-dir="left"]::before   { transform: translate3d(-101%, 0, 0); }
.ch-facts.has-media .ch-fact[data-dir="right"]::before  { transform: translate3d(101%, 0, 0); }

.ch-facts.has-media .ch-fact:hover::before,
.ch-facts.has-media .ch-fact:focus-within::before { transform: translate3d(0, 0, 0); }

.ch-facts.has-media .ch-fact-n,
.ch-facts.has-media .ch-fact-h,
.ch-facts.has-media .ch-fact-p { transition: color 0.4s var(--ease); }
.ch-facts.has-media .ch-fact:hover .ch-fact-h,
.ch-facts.has-media .ch-fact:focus-within .ch-fact-h { color: #fff; }
.ch-facts.has-media .ch-fact:hover .ch-fact-p,
.ch-facts.has-media .ch-fact:focus-within .ch-fact-p { color: rgba(255,255,255,0.82); }

/* Touch and reduced-motion get the image as a quiet always-on backdrop
   rather than a hover trick they can never trigger.
   The selector MUST carry [data-dir]: the per-direction park rules above
   include that attribute, so a plain class selector loses on specificity and
   the panel stays translated off-screen — which shipped as invisible white
   text on the live mobile view. */
@media (hover: none) {
  .ch-facts.has-media .ch-fact[data-dir]::before { transform: none; opacity: 1; }
  .ch-facts.has-media .ch-fact-n { color: #9adcf5; }
  .ch-facts.has-media .ch-fact-h { color: #fff; }
  .ch-facts.has-media .ch-fact-p { color: rgba(255,255,255,0.86); }
}
@media (prefers-reduced-motion: reduce) {
  .ch-facts.has-media .ch-fact::before { transition: none; }
}

/* Wordless transition band — full bleed, zero words. The page's punctuation. */
.band {
  position: relative; width: 100%; overflow: hidden;
  background: #070707; border-top: 1px solid rgba(255,255,255,0.08);
}
/* A band that pins its own contents cannot clip them: overflow:hidden on an
   ancestor silently disables position:sticky inside it. Bands that pin opt
   out of the clip and let their inner frame do the clipping instead. */
.band.pins { overflow: visible; }

/* ---------------------------------------------------------------------
   PAGE AXIS — opt in with <body class="page-axis">

   The inner pages were each built on their own 12-column layout with their
   own left edge, which is why the site read as a stack of unrelated pages.
   These rules pull the shared furniture (section heads, display headlines,
   body copy, meta rows, CTAs) onto the same centred axis as the homepage,
   without touching page-specific functional layouts like the work index or
   the contact form, which stay where they are and keep working.

   Opt-in rather than blanket so a page is only converted once it has been
   looked at.
   --------------------------------------------------------------------- */
body.page-axis .section-head,
body.page-axis .usp-header,
body.page-axis .outcomes-head {
  text-align: center;
}
body.page-axis .section-head > *,
body.page-axis .usp-header > * {
  grid-column: 1 / -1 !important;
}
/* Display headlines centre and stop running to the viewport edge. */
body.page-axis h1.d,
body.page-axis h2.d,
body.page-axis .section-head h2,
body.page-axis .usp-header h2 {
  text-align: center;
  max-width: 20ch;
  margin-inline: auto;
}
/* Standing body copy gets the shared measure, centred. */
body.page-axis .copy,
body.page-axis .lede,
body.page-axis .section-head p,
body.page-axis .usp-header p {
  text-align: center;
  max-width: var(--col-narrow);
  margin-inline: auto;
}
body.page-axis .copy p { text-align: center; }
/* Buttons and meta rows sit on the axis too. */
body.page-axis .section-head .btn,
body.page-axis .usp-header .btn { margin-inline: auto; }
/* Utility rows (location, links, legal) range left across the site. Only the
   things meant to be READ sit on the centre axis. */
body.page-axis .meta-strip { justify-content: flex-start; text-align: left; }
body.page-axis footer,
body.page-axis .footer,
body.page-axis nav,
body.page-axis .nav-links,
body.page-axis .foot-legal { text-align: left; }

/* Pull quotes are display headlines here, not prose, so they centre with the
   other headlines. (.quote was originally in the left-ranged list below and
   that left the studio page's biggest line flush to the viewport edge.) */
body.page-axis .pull-quote,
body.page-axis .pull-quote-large { text-align: center; }
body.page-axis .pull-quote .quote,
body.page-axis .pull-quote-large .quote {
  text-align: center; max-width: 18ch; margin-inline: auto;
}

/* Long-form prose (case-study bodies, FAQ answers, form labels) stays ranged
   left inside its centred column: centring a six-line paragraph hurts to
   read. The block itself is still centred, so the axis holds.
   :not(.pull-quote *) because the studio page's biggest line is marked up as
   a blockquote and this rule was flattening it to the left edge. */
body.page-axis .faq-body,
body.page-axis form,
body.page-axis label,
body.page-axis .prose { text-align: left; }
body.page-axis blockquote:not(.pull-quote blockquote) { text-align: left; }
body.page-axis .pull-quote blockquote,
body.page-axis .pull-quote blockquote > * { text-align: center; }

/* The inner pages put the section eyebrow in columns 1-2 and the headline in
   3-12, which is what makes them read as a different page family. Inside a
   page-axis section, the eyebrow and the display headline each take the full
   row and centre, so the head stacks on the axis exactly like a .ch-mark and
   a .ch-h do on the homepage. */
/* .ch-mark is included here because the inner pages' eyebrows were converted
   to it, and the old rule keyed only on .m.m-accent, which left every new
   chapter marker ranged left while its headline sat centred. */
/* The marker centres within whatever column holds it, rather than depending on
   its parent's text-align. Some of these sit inside split hero layouts whose
   left column is not centred, and inline-flex left them ranged against it. */
body.page-axis .ch-mark { display: flex; justify-content: center; width: 100%; }
body.page-axis section > .grid > .ch-mark,
body.page-axis section > .ch-mark,
body.page-axis .section-head > .ch-mark,
body.page-axis section > .grid > .m.m-accent,
body.page-axis section > .grid > h1.d,
body.page-axis section > .grid > h2.d,
body.page-axis section > .grid > p.d {
  grid-column: 1 / -1 !important;
  text-align: center;
  margin-inline: auto;
}
body.page-axis section > .grid > .m.m-accent { margin-bottom: 20px; }
/* Standing paragraphs directly in a section grid (ledes, sign-offs) take the
   shared measure and centre. Scoped to DIRECT children so the two-column
   media-and-text blocks on /process and /work, which are functional layouts
   rather than page furniture, are left alone. */
body.page-axis section > .grid > p:not(.d):not(.m) {
  grid-column: 1 / -1 !important;
  text-align: center;
  max-width: var(--col-narrow);
  margin-inline: auto;
}

/* Give the inner pages the same column the homepage has. Without this the
   content is centre-ALIGNED but still spans the full 1440, so the measure
   is wrong and it does not read as the same page family. */
body.page-axis section > .grid,
body.page-axis section > .section-head,
body.page-axis section > .usp-header,
body.page-axis .pull-quote > *,
body.page-axis .stat-block > * {
  max-width: var(--col);
  margin-inline: auto;
}

/* ---------------------------------------------------------------------
   The through-line: chapter rail + scroll progress
   Two fixed marks that persist across every chapter of every page, so the
   scroll reads as one continuous document rather than a stack of pages.
   --------------------------------------------------------------------- */
.scroll-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px;
  z-index: 80; pointer-events: none;
  background: transparent;
}
.scroll-progress i {
  display: block; height: 100%; width: 0%;
  background: var(--accent, #5BC2E7);
  transform-origin: 0 50%;
  transition: width 0.12s linear;
}

.chapter-rail {
  position: fixed; top: 50%; right: 18px; transform: translateY(-50%);
  z-index: 55; display: none; flex-direction: column; gap: 14px;
  pointer-events: auto;
}
@media (min-width: 1100px) { .chapter-rail { display: flex; } }
.chapter-rail a {
  display: block; width: 7px; height: 7px;
  background: rgba(10,10,10,0.22);
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.chapter-rail a:hover { transform: scale(1.5); background: rgba(10,10,10,0.5); }
.chapter-rail a.is-on { background: var(--accent, #5BC2E7); transform: scale(1.5); }
/* Over a dark chapter the rail inverts so it never disappears. */
.chapter-rail.on-dark a { background: rgba(255,255,255,0.28); }
.chapter-rail.on-dark a:hover { background: rgba(255,255,255,0.6); }
.chapter-rail.on-dark a.is-on { background: var(--accent, #5BC2E7); }

/* ---------------------------------------------------------------------
   One reveal system. Everything that enters the viewport does the same
   thing, with the same curve and the same stagger.
   --------------------------------------------------------------------- */
.ed [class*="reveal"] { will-change: opacity, transform; }

@media (prefers-reduced-motion: reduce) {
  .scroll-progress i { transition: none; }
  .chapter-rail a { transition: none; }
  /* Reveal primitives start at low opacity and are raised by an observer.
     If anything ever stops that observer firing, a reduced-motion reader is
     left looking at invisible copy, so pin them visible outright rather than
     depending on JS to undo a decorative starting state. */
  .reveal, .reveal-line, .clip-reveal,
  .split-text .word .inner {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
}

/* =====================================================================
   REVEAL KIT
   A small, shared vocabulary of entrance animations used across every
   page, so the site animates in one accent instead of per-page one-offs.

   Usage:
     <h2 data-reveal="mask">          one element
     <div data-stagger>               children reveal one by one
       <div data-reveal="pop">…</div>

   Rules this kit follows, from the reference sites and from Rory's brief
   ("don't overload with scrolling, needs to be quick and easy to use"):
     - Nothing adds page height. These are entrances, not scroll budgets.
     - Nothing gates reading. Text primitives start partly visible, and the
       slowest entrance is 0.7s with a 60ms stagger.
     - One curve, --ease, shared with everything else on the site.
   ===================================================================== */

[data-reveal] {
  --rd: 0.62s;                                  /* duration */
  --rs: 60ms;                                   /* per-item stagger */
  transition-delay: calc(var(--i, 0) * var(--rs));
}

/* up — the workhorse. Fade and rise. Starts faintly visible so a failed
   observer can never hide copy outright. */
[data-reveal="up"] {
  opacity: 0.1; transform: translateY(18px);
  transition: opacity var(--rd) var(--ease), transform var(--rd) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs));
}
[data-reveal="up"].is-revealed { opacity: 1; transform: none; }

/* mask — a line of type wiped upward behind its own edge. The premium
   headline move on both reference sites. */
[data-reveal="mask"] {
  clip-path: inset(0 0 110% 0);
  transform: translateY(0.18em);
  transition: clip-path 0.7s var(--ease), transform 0.7s var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs));
}
[data-reveal="mask"].is-revealed { clip-path: inset(-20% -10% -20% -10%); transform: none; }

/* pop — squares of information arriving one by one. Scale plus lift, which
   reads as placement rather than fade. */
[data-reveal="pop"] {
  /* Longer stagger than the default 60ms: this is the "squares of information
     arriving one by one" case, and at 60ms a six-tile grid reads as one fade.
     85ms x 6 is still only ~0.5s of choreography. */
  --rs: 85ms;
  opacity: 0; transform: translateY(26px) scale(0.955);
  transition: opacity var(--rd) var(--ease), transform var(--rd) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs));
}
[data-reveal="pop"].is-revealed { opacity: 1; transform: none; }

/* scan — THE image reveal, approved by Rory off the motion lab and applied
   site-wide: the image wipes in on the house curve with an accent hairline
   travelling at the exposure edge, like a scan head passing. One duration,
   one curve, every image, no exceptions (Obys discipline, our signature). */
[data-reveal="scan"] {
  position: relative; overflow: hidden;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.8s var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs, 60ms));
}
[data-reveal="scan"].is-revealed { clip-path: inset(0 0 0 0); }
[data-reveal="scan"]::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  background: var(--accent, #5BC2E7); opacity: 0; pointer-events: none;
}
[data-reveal="scan"].is-revealed::after {
  animation: scanEdge 0.8s var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * var(--rs, 60ms));
}
@keyframes scanEdge {
  0% { left: 0; opacity: 1; }
  92% { opacity: 1; }
  100% { left: calc(100% - 2px); opacity: 0; }
}

/* wipe — a panel uncovering itself. For images and media cards. Direction
   set with data-from. */
[data-reveal="wipe"] {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.72s var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs));
}
[data-reveal="wipe"][data-from="left"]   { clip-path: inset(0 100% 0 0); }
[data-reveal="wipe"][data-from="right"]  { clip-path: inset(0 0 0 100%); }
[data-reveal="wipe"][data-from="top"]    { clip-path: inset(100% 0 0 0); }
[data-reveal="wipe"].is-revealed { clip-path: inset(0 0 0 0); }

/* line — a rule drawing itself across. Cheap, and it makes a list feel
   assembled rather than dumped. */
[data-reveal="line"] {
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.6s var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rs));
}
[data-reveal="line"].is-revealed { transform: scaleX(1); }

/* chars — per-character rise. Reserve for SHORT headlines; the JS refuses
   to split anything long enough to look silly. */
[data-reveal="chars"] .rc {
  display: inline-block;
  opacity: 0; transform: translateY(0.5em);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
[data-reveal="chars"].is-revealed .rc { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* Entrances are decoration. Under reduced motion the content is simply
     present, with no dependency on an observer firing to make it visible. */
  [data-reveal],
  [data-reveal="chars"] .rc {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  [data-reveal="scan"]::after { animation: none !important; opacity: 0 !important; }
}

/* ---------------------------------------------------------------------
   Genie surfaces. The panel keeps opacity 1 and lets the clip do the
   reveal, otherwise the old opacity transition fights the animation.
   Closed state hides via visibility, which JS holds 'visible' while the
   close animation plays out.
   --------------------------------------------------------------------- */
.ask-fab-panel {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
  clip-path: polygon(0 100%, 0 100%, 0 100%);
  visibility: hidden;
}
.ask-fab.open .ask-fab-panel { visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .ask-fab-panel { clip-path: none; }
}

/* ---------------------------------------------------------------------
   ROUND-TWO ROLLOUT (all four approved off /motion-lab)
   --------------------------------------------------------------------- */

/* Frame count — digits roll up on drums like the status-strip counter.
   Mark any stat with data-count; non-digit characters stay static. */
[data-count] .fcnt-digit { display: inline-block; height: 1em; overflow: hidden; vertical-align: top; }
[data-count] .fcnt-col { display: flex; flex-direction: column; transform: translateY(0); }
[data-count] .fcnt-col span { height: 1em; display: block; line-height: 1; }

/* Drift — the image travels a beat slower than the page inside its frame.
   The scale gives it headroom so edges never show. Container must clip;
   the scan wipe's overflow:hidden already does on case figures. */
img[data-drift] { will-change: transform; }
.drift-img { scale: 1.12; }

/* Genie'd FAQ answers — the approved restyle: page-coloured, one accent
   rule, no box. Applies wherever details.faq-item is used (home FAQ,
   studio rows). */
details.faq-item .faq-body {
  border-left: 2px solid var(--accent, #5BC2E7);
  /* The rule must end level with the last line of text. Bottom padding sits
     INSIDE the border box, so any of it drags the rule past the copy into
     white space — spacing below is margin, never padding. */
  padding: 0 0 0 18px !important;
  margin-bottom: 26px;
  overflow: hidden;
}

/* The pass — page transitions. Injected by forvr-scroll.js. */
#forvrPass { position: fixed; inset: 0; z-index: 300; pointer-events: none; visibility: hidden; }
#forvrPass .sheet { position: absolute; inset: 0; background: var(--bg, #fafafa); transform: translateX(-101%); }
#forvrPass .edge { position: absolute; top: 0; bottom: 0; width: 3px; background: var(--accent, #5BC2E7); left: 0; opacity: 0; }

/* Nav popup rides the genie, same contract as the Ask AI panel. */
.floating-nav .nav-menu {
  transition: none !important;
  transform: none !important;
  opacity: 1 !important;
  clip-path: polygon(0 100%, 0 100%, 0 100%);
  visibility: hidden;
}
.floating-nav.open .nav-menu { visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .floating-nav .nav-menu { clip-path: none; }
  .drift-img { scale: 1; }
}
