/* ════════════════════════════════════════════════════════════════════
   LV8 TECH — the work reel. Acts 1–2 preview.

   The protagonist is THE WORK: an invoice arrives, the system reads it.
   Chosen against the content audit — the buyer is a sceptical owner-
   operator who must recognise their own Monday in the first frame, so the
   footage is a real back office, not an object on a plinth.

   The film is scroll-scrubbed (scroll position IS the playhead) and the
   two clips are frame-chained: clip 2 was generated from the literal last
   frame of clip 1, so the boundary is the same frame twice and the scrub
   crosses it with no dissolve. That is what "no joins" means here.

   Palette deliberately echoes the homepage tokens (warm porcelain, ink,
   champagne gold) so this route can be promoted to the homepage without a
   re-grade.
   ════════════════════════════════════════════════════════════════════ */

:root {
  --ink:      #17140E;                  /* homepage --lx-ink */
  --ink64:    rgba(23, 20, 14, .64);
  --ink38:    rgba(23, 20, 14, .38);
  --ink16:    rgba(23, 20, 14, .16);
  --porcelain:#F7F5EF;                  /* homepage --lx-bg */

  --w:   #FFFFFF;
  /* Raised from .72/.45/.22. Those were tuned by eye against still frames and
     measured 1.05-1.25:1 against live footage — the hierarchy was intact but
     the bottom of it had fallen out of legibility. */
  --w72: rgba(255,255,255,.86);
  --w45: rgba(255,255,255,.66);
  --w22: rgba(255,255,255,.34);

  --gold:     #E8B54D;                  /* brand gold, tuned for dark footage */
  --gold-ink: #7A5D00;                  /* homepage --lx-gold-text, on porcelain */
  --on-gold:  #221A00;

  --sans: Inter, "Helvetica Neue", system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { background: #141009; color: var(--w); font-family: var(--sans);
       font-weight: 400; -webkit-font-smoothing: antialiased; overflow-x: hidden; }

/* ─── type: light weight, tight — measured off the reference ─── */
.t  { font-weight: 400; letter-spacing: -.028em; line-height: 1.07; text-wrap: balance; }
.t1 { font-size: clamp(34px, 4.4vw, 66px); }
.t2 { font-size: clamp(26px, 3.2vw, 46px); }
.bd { font-size: clamp(15px, 1.05vw, 17px); line-height: 1.72; }
.mn { font-family: var(--mono); font-size: 10.5px; font-weight: 500;
      letter-spacing: .18em; text-transform: uppercase; }
.cap { font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
       text-transform: uppercase; color: var(--w45); }

.wrap { width: 100%; max-width: 1280px; margin: 0 auto; padding: 0 26px; }
@media (min-width: 900px) { .wrap { padding: 0 56px; } }

/* ═══════ THE FILM ═══════
   Two full-bleed videos: the whole 72s take, and the whole 72s take backwards.
   They are not alternatives — the reverse one stacks OVER the forward one while
   you scroll up and lifts off again when you stop. */
#film { position: fixed; inset: 0; z-index: 0; background: #141009; overflow: hidden; }
#film video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0;
  /* No CSS filter here: a per-frame saturate/contrast on a full-screen video
     layer re-rasterises on every seek, and the footage already carries its
     own grade. */
}
/* The act joints used to be here: nine files meant eight swaps, and this
   transition was tuned to absorb the one joint (p8->p9) whose generation had
   advanced the camera past its seed. The acts are now stream-copied into one
   file, so the joints are inside the stream and there is nothing to dissolve.
   What is left is the reverse proxy lifting on and off, which is exactly what a
   short opacity fade is for. Opacity only: no layout, no filter, no repaint. */
#film video { transition: opacity .22s linear; }
#film video.on { opacity: 1; }
/* STACKING IS DOM ORDER — deliberately, with no z-index anywhere in this layer.
   The reverse film sits after the forward film and before .floor/.grain in the
   markup, so it paints OVER the forward picture and UNDER the grade. That
   matters: the forward film keeps opacity 1 the whole time the proxy is up, so
   the exchange is one opaque picture covering another and there is no
   cross-fade dip to the backdrop. Do not reorder these children.
   The mobile film is inserted by work.js immediately before .floor, which puts
   it in the same place in the stack. */
/* THE FLOOR IS NOW MOOD, NOT LEGIBILITY. Those are two different jobs and this
   layer was doing both badly.

   It used to be the only thing keeping copy readable, and it could not be: it is
   anchored to the VIEWPORT bottom and fades out at 62%, so it darkens the lower
   38% of the frame and nothing above that. The copy blocks are tall, and measured
   across five scenes and five viewports their top edge lands anywhere between 50%
   and 71% of viewport height. Coverage was therefore luck. The `moves` scene sits
   at 50%, right where this gradient has thinned to about .15, and the ground
   behind its eyebrow measured 0.216 luminance against 0.003 for the landing
   scene — a 70x spread across scenes that are supposed to look alike. That is the
   "type mixed into the background" complaint, and no amount of text-shadow fixes
   a scrim that is not there.

   Legibility now belongs to `.wrap::before`, which is anchored to the copy itself
   (see SCENES below). So this can go back to being what its own comment always
   said it was: a soft floor, lighter than before, which stops crushing footage
   the earlier versions kept crushing. */
#film .floor {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top,
    rgba(12, 9, 5, .72) 0%, rgba(12, 9, 5, .56) 14%, rgba(12, 9, 5, .34) 30%,
    rgba(12, 9, 5, .14) 45%, transparent 60%);
}
/* NO BLEND MODE. `mix-blend-mode: overlay` was costing the whole page its
   frame rate. A blend mode makes the compositor read the backdrop back, blend
   per pixel, and re-composite; over a video texture that changes every frame,
   full-screen, at dpr 1.75, that is a full readback every frame on integrated
   graphics. Measured on an Intel Iris Xe against production, same scroll:

       with blend      52fps  p90 33.2ms  28 frames >33ms  10% video frames DROPPED
       blend removed   60fps  p90 16.8ms   0 frames >33ms   0% dropped

   Removing either grain layer on its own barely helped (58-59fps, p90 still
   33ms) — one blended layer is enough to force the slow path, so both had to
   go. Opacity retuned from .08 so the grain survives the change: measured
   texture energy against the old look is 0.91 versus 0.96, and the black point
   lifts 2.4/255, which is the price of alpha compositing being unable to
   subtract light. */
#film .grain { position: absolute; inset: 0; opacity: .04;
               pointer-events: none; }

/* ─── the chapter timeline ───
   Was a single filling hairline, which told you how far through you were but
   nothing about the structure. Now one segment per chapter, sized to that
   chapter's real scroll length: position, proportion and what is left, in 2px. */
#prog { position: fixed; left: 0; right: 0; top: 0; height: 2px; z-index: 90;
        display: flex; gap: 2px; pointer-events: auto; }
#prog .pseg { position: relative; height: 100%; padding: 0; border: 0;
  background: rgba(255, 255, 255, .16); cursor: pointer; overflow: hidden;
  transition: background .3s; }
#prog.onlite .pseg { background: rgba(23, 20, 14, .16); }
#prog .pseg i { position: absolute; inset: 0; transform-origin: left;
  transform: scaleX(0);
  background: linear-gradient(90deg, #FFD34D, var(--gold)); }
/* the hit area is taller than the line, so the bar is usable without being
   thick enough to read as a UI element */
#prog .pseg::before { content: ""; position: absolute; left: 0; right: 0;
  top: -7px; bottom: -9px; }
#prog .pseg:hover { background: var(--gold); }
#progLab { position: fixed; top: 12px; left: 0; z-index: 90;
  font-family: var(--mono); font-size: 8.5px; font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase; color: var(--gold);
  pointer-events: none; opacity: 0; transform: translateX(-50%);
  transition: opacity .22s ease; white-space: nowrap;
  background: rgba(10, 8, 4, .8); padding: 4px 9px; border-radius: 999px;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
#progLab.on { opacity: 1; }
/* On a phone this stops being a control and becomes purely an indicator. The
   fourteen segments are 7-35px wide in a 2px bar directly under the notch —
   too small to aim at, and close enough to the address bar that every hit
   would be an accident. The bar still shows where you are. */
@media (max-width: 900px) {
  #prog { pointer-events: none; }
  #prog .pseg { cursor: default; }
  #prog .pseg::before { display: none; }
  #progLab { display: none; }
}

/* ─── nav: floating pill, gold CTA to match the brand ─── */
/* Above the drawer, not under it: the pill carries the close button, and a
   menu you can only leave by guessing at Escape is not a menu. */
#nav { position: fixed; top: 20px; left: 0; right: 0; z-index: 88;
       display: flex; justify-content: center; pointer-events: none; }
/* NO BACKDROP BLUR OVER THE FILM.
   The note further down this file says the nav pill and the drawer "keep theirs —
   they sit over content that is not repainting every frame". That is measurably
   wrong for the pill. #film is position: fixed at z-index 0 and the pill is fixed
   at top: 20px, so the pill's band is y=20..64 — while the only thing between them
   is the letterbox matte, which is 14px tall. For the entire film the pill's
   backdrop IS live video, and blur(18px) saturate(1.3) over a texture that changes
   every frame is exactly the compositor slow path this file already removed
   mix-blend-mode for (52fps -> 60fps, 10% dropped video frames -> 0%).

   The opacity is raised from .68 to .82 to carry the panel without the blur, which
   is the same trade the two readout cards took. Over the porcelain scenes the
   ground is static and a blur costs nothing, so `on-lite` puts it back — one
   selector, and the only place it can be afforded. */
#nav .pill { pointer-events: auto; display: flex; align-items: center; gap: 26px;
  padding: 8px 9px 8px 17px; border-radius: 12px;
  background: rgba(16, 12, 7, .82);
  border: 1px solid rgba(255,255,255,.1);
  /* one treatment on every ground, so nothing cross-fades */ }
:root.on-lite #nav .pill {
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  background: rgba(16, 12, 7, .68); }
/* 11px: the gap between the mark and its strapline, matching the artwork's own
   spacing rather than the tighter gap a wordmark suffix wanted. */
#nav .br { position: relative; display: flex; align-items: center; gap: 11px;
           text-decoration: none; }
/* the wordmark's own box is 92x21, well under a thumb; grow the hit area
   without growing the mark */
#nav .br::after { content: ""; position: absolute; inset: -12px -8px; }
/* The mark is the brand's own artwork now, so it carries no plate behind it —
   the gold-gradient tile that used to be here was holding a chevron someone
   drew, and a logo does not need a badge to sit in. Height-driven, because the
   lockup's aspect is fixed and only the cap height has to agree with the
   wordmark beside it. */
#nav .br .mk { height: 26px; width: auto; display: block; }
/* Set against the mark's cap, not the master lockup's ratio: 23px puts each
   strapline row at a 7px cap, which is the register the rest of this page's
   mono chrome already uses. At the artwork's own proportion it would be 2.4px. */
#nav .br .strap { height: 23px; width: auto; display: block; opacity: .92; }
#nav .br .nm { font-size: 14.5px; font-weight: 500; letter-spacing: -.02em;
               color: var(--w); }
#nav .lk { display: none; gap: 24px; }
@media (min-width: 1000px) { #nav .lk { display: flex; } }
#nav .lk a { font-size: 13px; text-decoration: none; color: #FFF;
             letter-spacing: -.01em; transition: color .25s;
             text-shadow: 0 1px 8px rgba(8, 6, 3, .6); }
#nav .lk a:hover { color: var(--w); }
#nav .cta { font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; text-decoration: none;
  background: linear-gradient(180deg, #FFD34D 0%, var(--gold) 60%, #C9930A 100%);
  color: var(--on-gold); padding: 11px 18px; border-radius: 8px;
  white-space: nowrap; }
/* the label has two lengths; only one is ever in the layout */
#nav .cta .ct-s { display: none; }

/* ─── the menu button ───
   Two rules that become an X. Only below 1000px, where .lk is hidden. */
.mnu { display: none; position: relative; width: 34px; height: 34px; padding: 0;
  border: 1px solid rgba(255,255,255,.16); border-radius: 9px; cursor: pointer;
  background: rgba(255,255,255,.05); align-content: center; justify-items: center;
  gap: 6px; }
/* 44px of thumb over a 34px control */
.mnu::after { content: ""; position: absolute; inset: -5px; }
.mnu i { display: block; width: 15px; height: 1.5px; border-radius: 2px;
  background: var(--w); transition: transform .32s cubic-bezier(.22,1,.36,1),
                                    opacity .2s linear; }
.mnu[aria-expanded="true"] i:first-child { transform: translateY(3.75px) rotate(45deg); }
.mnu[aria-expanded="true"] i:last-child  { transform: translateY(-3.75px) rotate(-45deg); }

/* ─── the drawer ───
   Full frame, dark glass over the live film rather than a flat sheet: the page
   is a film and the menu should not step out of it. */
#drawer { position: fixed; inset: 0; z-index: 85; display: flex;
  flex-direction: column; justify-content: flex-end;
  padding: 96px 22px calc(30px + env(safe-area-inset-bottom));
  background: rgba(9, 7, 4, .9);
  -webkit-backdrop-filter: blur(22px) saturate(1.25);
  backdrop-filter: blur(22px) saturate(1.25);
  /* it fills the viewport, so every touch lands here — `contain` stops the
     page behind it from scrolling without locking the body, which on a page
     built out of sticky pins is not a safe thing to do */
  overflow-y: auto; overscroll-behavior: contain;
  opacity: 0; transition: opacity .28s ease; }
#drawer[hidden] { display: none; }
#drawer.on { opacity: 1; }
.dnav { display: flex; flex-direction: column; }
.dnav a { display: flex; align-items: baseline; gap: 14px; padding: 17px 2px;
  text-decoration: none; color: var(--w); font-size: 25px; letter-spacing: -.025em;
  border-bottom: 1px solid rgba(255,255,255,.09);
  opacity: 0; transform: translateY(14px);
  transition: opacity .4s ease, transform .4s cubic-bezier(.22,1,.36,1); }
.dnav a:first-child { border-top: 1px solid rgba(255,255,255,.09); }
#drawer.on .dnav a { opacity: 1; transform: none; }
#drawer.on .dnav a:nth-child(1) { transition-delay: .05s }
#drawer.on .dnav a:nth-child(2) { transition-delay: .10s }
#drawer.on .dnav a:nth-child(3) { transition-delay: .15s }
#drawer.on .dnav a:nth-child(4) { transition-delay: .20s }
.dnav a em { font-family: var(--mono); font-style: normal; font-size: 10.5px;
  font-weight: 600; letter-spacing: .18em; color: var(--gold); }
.dfoot { margin-top: 30px; opacity: 0; transition: opacity .4s ease .24s; }
#drawer.on .dfoot { opacity: 1; }
.dcta { width: 100%; }
.dnote { margin-top: 14px; text-align: center; letter-spacing: .08em; }

/* Below 1000px the pill spans the bar instead of hugging its content: the
   wordmark anchors left, the action and the menu sit right, and nothing
   depends on the document being exactly as wide as the viewport. */
@media (max-width: 999px) {
  #nav { top: 12px; padding: 0 14px; }
  #nav .pill { width: 100%; justify-content: space-between; gap: 10px;
               padding: 7px 7px 7px 14px; }
  .mnu { display: grid; }
  /* The compact bar carries an action and a menu button as well, so the mark
     steps down — but the strapline stays as long as it fits. */
  #nav .br .mk { height: 22px; }
  #nav .br .strap { height: 20px; }
  /* 44px tall, so the pill's own actions clear a thumb too */
  #nav .cta { font-size: 11px; letter-spacing: .13em; padding: 15px 16px; }
}
@media (min-width: 1000px) { #drawer { display: none !important; } }
/* the long label stops fitting alongside a menu button around here */
@media (max-width: 599px) {
  #nav .cta .ct-l { display: none; }
  #nav .cta .ct-s { display: inline; }
  #nav .br .nm { font-size: 14px; }
}
/* Below this the bar cannot hold the lockup, the action and the menu button at
   once — measured, not guessed. The mark alone is what the lockup reduces to. */
@media (max-width: 405px) { #nav .br .strap { display: none; } }
@media (max-width: 359px) { #nav .cta { display: none; } }

/* ─── buttons ─── */
.b { display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; text-decoration: none;
  padding: 15px 24px; border-radius: 9px; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: transform .3s cubic-bezier(.22,1,.36,1), background .25s,
              border-color .25s; }
.b-gold { background: linear-gradient(180deg, #FFD34D 0%, var(--gold) 60%, #C9930A 100%);
          color: var(--on-gold);
          box-shadow: 0 14px 34px -12px rgba(232,181,77,.55); }
.b-gold:hover { transform: translateY(-1px); }
/* Also over live footage — this is the secondary CTA inside the opening act, so
   it sits on the picture from the first frame. Plate raised from .3 to .52 in
   place of the blur, for the same reason as the readout cards below. */
.b-line { background: rgba(20,16,9,.52); border-color: var(--w22); color: var(--w); }
.b-line:hover { border-color: var(--w); transform: translateY(-1px); }
.b-ink { background: var(--ink); color: var(--porcelain); }
.b-ink:hover { transform: translateY(-1px); }

/* ═══════ SCENES over the film ═══════ */
/* 340vh, not 280. Two things come out of the extra 60vh and both matter.
   It buys the clear window below without shortening how long anyone has to read
   the copy — the copy holds for the same absolute travel it always did, and the
   room is added after it. And because the film's mapping is derived from live
   layout (spanA = film-end.offsetTop - vh), a longer act means the same 8s of
   footage is spread over more scroll: 3.08s of film per viewport becomes 2.53s.
   The beats land slower, which is the point of a film, and the decode demand for
   any given gesture drops by 18% for free. */
.scene { position: relative; z-index: 2; height: 340vh; }
.scene .hold { position: sticky; top: 0; height: 100svh; display: flex;
               align-items: flex-end; padding-bottom: clamp(48px, 7vh, 88px); }

/* ─── LET THE PICTURE BREATHE ───
   The copy was pinned for the whole of every act, and it does not travel alone:
   .wrap::before bleeds 70% below the copy block and 16% above it, so between the
   scrim, the type and #film .floor there was a permanent band of shadow across
   the bottom of the frame from the first act to the last. The footage has beats
   in it — the paper settling, the trays working through, the wall assembling —
   and none of them were ever on screen by themselves.

   So each act now ends with the copy gone. `.clear` is toggled from the scrub in
   work.js on the scene's OWN sticky progress, and because it lands on the holder
   the scrim leaves with the type rather than lingering as an unexplained shadow.

   It is a transition rather than a scrubbed value on purpose: this is the same
   idiom [data-flip] and .pin already use, it costs one class toggle instead of a
   style write per frame, and a fade that runs on its own clock reads as the copy
   choosing to leave rather than as something being dragged off. */
.scene .hold { transition: opacity .5s ease, transform .5s cubic-bezier(.22, 1, .36, 1); }
.scene .hold.clear { opacity: 0; transform: translateY(-22px); pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .scene .hold.clear { transform: none; } }

/* ─── THE SCRIM TRAVELS WITH THE COPY ───
   The one structural fix. A scrim anchored to the viewport cannot cover copy whose
   height and position change per scene, which is why the old floor missed the
   `moves` eyebrow entirely. This one is anchored to the copy block, so it grows
   when the copy grows and sits where the copy sits, at every scene and every
   viewport, with no per-scene tuning to keep in sync.

   Full-bleed horizontally on purpose: the section already reads as a wide band of
   shadow across the frame, and a scrim that stopped at the 1280px column would
   draw a visible vertical edge down the footage. Vertical is where the work is.

   Bleeds well past the copy on every side so there is no hard edge anywhere: the
   top fade is long and gentle, which is what lets it darken bright footage without
   reading as a box drawn on the film. */
/* CLIP THE BLEED, DON'T LET IT EXTEND THE DOCUMENT. The ±50vw below is what
   buys the full-bleed, and it also made the scrim 780px wide on a 390px
   viewport — right edge at 585, which was exactly documentElement.scrollWidth
   at every size tested. Nothing scrolled sideways (body's overflow-x: hidden
   propagates to the viewport) but fixed chrome centres on the scroll width,
   not the viewport, so #nav computed against 585 and the pill landed at
   x 108..477 on a 390 screen with the CTA cut off the right edge.

   `clip` and not `hidden`: hidden would make this a scroll container, and an
   overflow scroll container broke the sticky pin inside #process once already.
   overflow-x: clip also leaves the y axis genuinely visible, which hidden
   cannot — and the vertical bleed is the half of this that does the work. */
.scene .hold { overflow-x: clip; }
.scene .hold .wrap { position: relative; }
.scene .hold .wrap::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  left: -50vw; right: -50vw; top: -16%; bottom: -70%;
  background: linear-gradient(to top,
    rgba(10, 8, 4, .90) 34%, rgba(10, 8, 4, .78) 56%,
    rgba(10, 8, 4, .52) 74%, rgba(10, 8, 4, .24) 88%, transparent 100%);
}

/* Copy sits over live footage that can swing bright (the act-3 gold flow
   drowned the eyebrow entirely). A soft wide shadow keeps type legible over
   any frame without re-grading the film itself. Kept, but it is now a second
   line of defence rather than the only one. */
.scene .hold .mn, .scene .hold h1, .scene .hold h2,
.scene .hold .bd, .scene .hold .cap, .scene .hold .stats {
  /* two tight shadows carry the glyph edge, one wide one lifts it off the
     frame — measured against the act-3 tray footage, which peaks near-white */
  text-shadow: 0 1px 2px rgba(10, 8, 4, .9), 0 2px 8px rgba(10, 8, 4, .75),
               0 1px 30px rgba(10, 8, 4, .7);
}

/* ═══════ THE OPENING ═══════
   One sentence at a time, at the scale the reference uses (~11% of viewport
   height per line versus the 7% this had). The statements stack in the same
   place and cross-fade on the scrub, so the frame never holds more than one
   idea and the film underneath is never competing with a block of copy. */
/* NOT position:relative — that overrides the inherited `position: sticky` and
   the whole opening frame scrolls away with the section. Sticky is itself a
   positioned ancestor, so .scrollcue anchors to it regardless. */
#open .hold { align-items: flex-end; }
#open .wrap { padding-bottom: clamp(20px, 3vh, 44px); }

.roll { position: relative; min-height: 2.34em; font-size: clamp(30px, 5.6vw, 104px); }
.roll .rl { position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  font-size: 1em; font-weight: 400; letter-spacing: -.035em; line-height: 1.06;
  color: var(--w); text-wrap: balance;
  /* the type sits ON the desk, so it carries the act shadow at full strength */
  text-shadow: 0 2px 3px rgba(10, 8, 4, .85), 0 3px 14px rgba(10, 8, 4, .7),
               0 2px 40px rgba(10, 8, 4, .6);
  opacity: 0; will-change: opacity, transform; }
.roll .rl.on { opacity: 1; }

/* the CTAs are held back until the act has earned them */
.opencta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 14px;
  margin-top: clamp(26px, 4vh, 46px); opacity: 0; transform: translateY(14px);
  transition: opacity .7s ease, transform .7s ease; pointer-events: none; }
.opencta.on { opacity: 1; transform: none; pointer-events: auto; }
.opencta .oclab { flex: 1 1 20em; min-width: 16em; max-width: 30em;
  letter-spacing: .08em; line-height: 1.7; }

/* a scroll cue, because the opening frame now has nothing else to act on */
/* anchored to the sticky frame, not the 280vh scene — against the section it
   sat almost three screens below the fold */
.scrollcue { position: absolute; right: clamp(20px, 4vw, 56px);
  bottom: clamp(18px, 3vh, 34px); display: flex; flex-direction: column;
  align-items: center; gap: 9px; opacity: .85; transition: opacity .5s ease; }
.scrollcue.gone { opacity: 0; }
.scrollcue .mn { font-size: 8.5px; letter-spacing: .42em; color: var(--w72); }
.scrollcue i { display: block; width: 1px; height: 34px;
  background: linear-gradient(180deg, var(--gold), transparent);
  animation: cue 1.9s ease-in-out infinite; transform-origin: top; }
@keyframes cue { 0%,100% { transform: scaleY(.35); opacity: .5 }
                 50%     { transform: scaleY(1);   opacity: 1 } }

@media (max-width: 700px) {
  .roll { min-height: 3.3em; }
  .opencta .oclab { flex-basis: 100%; }
  .opencta .b { flex: 1 1 auto; }
}

/* Type floors for touch-sized screens. Held at 900px, the same edge the rest
   of the mobile layout uses — an iPad in portrait is 768 and was falling
   through a 700px cutoff into the desktop sizes. */
@media (max-width: 900px) {
  /* On a phone these are the primary actions, not chrome. 9.5px mono at .16em
     is a caption size; it was chosen to sit quietly next to display type on a
     1440 screen, and on a 390 screen it just reads as small. */
  .b { font-size: 11px; letter-spacing: .13em; padding: 15px 20px; }
  /* 9px mono was picked to sit quietly beside display type on a 1440 screen.
     .cap carries real sentences — the honesty labels, the free-call line — and
     at arm's length on a phone 9px is not a reading size. .mn stays where it
     is: those are one-word chrome tags, not prose. */
  .cap { font-size: 10.5px; letter-spacing: .11em; }
  /* the letterbox stays; its label is a decorative frame credit and a small
     screen has no width to spend on it */
  #mattes .mlabel { display: none; }
}

/* ─── the readout: the system acting on the invoice ───
   Crisp DOM, never baked into the footage — generated video cannot render
   legible UI text, and these strings must be real. */
/* NO BACKDROP BLUR ON THE TWO CARDS THAT SIT OVER LIVE VIDEO. backdrop-filter
   is the same mechanism as a blend mode — read the backdrop, filter it,
   composite — and these two are the only glass panels that sit on top of a
   playing clip rather than a still page. Removing every backdrop-filter on the
   page moved dropped video frames from 19% to 8% and p99 from 150ms to 67ms
   mid-film; these two are the ones over video, so they are the ones worth
   taking. The blur was buying nothing anyway: the backdrop here is already
   dark and already out of focus. Opacity raised to carry the panel on its own.

   The nav pill USED to be excused here on the grounds that it "sits over content
   that is not repainting every frame". It does not — see the note on #nav .pill
   above: its band clears the 14px letterbox matte, so its backdrop is the film
   for the whole film. It has been taken too, and now only gets its blur back over
   the porcelain scenes.

   The drawer keeps its blur, but the film pauses while the drawer is open
   (work.js, drawerLock), so there is no repainting texture under it either. */
.readout { display: inline-flex; align-items: center; gap: 22px;
  padding: 15px 21px; border-radius: 8px;
  background: rgba(18, 14, 8, .74);
  border: 1px solid rgba(232, 181, 77, .32);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.7); }
.readout .id { color: var(--w72); }
.readout .act { color: var(--gold); }
.readout .tick { width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold); flex: none;
  animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity:.35 } 50% { opacity:1 } }

/* ─── the console card: act 3's agent log ─── */
.console { width: min(100%, 30em); border-radius: 10px; overflow: hidden;
  background: rgba(18, 14, 8, .76);
  border: 1px solid rgba(232, 181, 77, .3);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.7); }
.console .chead { display: flex; justify-content: space-between; align-items: center;
  padding: 11px 16px; border-bottom: 1px solid rgba(255,255,255,.09); }
.console .live { color: var(--gold); display: inline-flex; align-items: center; gap: 7px; }
.console .live::before { content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold); animation: pulse 2.2s ease-in-out infinite; }
.console .cline { padding: 15px 16px 0; font-size: 14px; color: var(--w);
  line-height: 1.5; min-height: 3.1em; }
/* the caret: present while a line is streaming, blinking once it lands */
.console .caret { display: block; width: 7px; height: 14px; margin: 2px 16px 15px;
  background: var(--gold); opacity: .9; }
.console .caret.blink { animation: cblink 1.1s steps(1) infinite; }
@keyframes cblink { 0%, 49% { opacity: .9 } 50%, 100% { opacity: 0 } }

/* ─── stat chips over the film ─── */
.stats { display: flex; gap: 26px; flex-wrap: wrap; }
.stats .s .v { font-size: clamp(24px, 2.2vw, 32px); font-weight: 400; letter-spacing: -.03em; }
.stats .s .k { margin-top: 6px; }

/* ─── closing panel: porcelain, echoing the homepage ─── */
.panel { position: relative; z-index: 3; background: var(--porcelain);
         color: var(--ink); padding: clamp(104px,14vh,180px) 0; }
.panel .t { color: var(--ink); }
/* the reference's chamfered notch join, top edge only */
.notch { clip-path: polygon(0 14px, 17% 14px, 18.6% 0, 81.4% 0, 83% 14px,
                            100% 14px, 100% 100%, 0 100%); }

/* the five service lines, listed plainly on porcelain */
.svc { display: grid; grid-template-columns: 1fr; gap: 5px; text-align: left;
       padding: 17px 0; border-top: 1px solid var(--ink16); }
.svc:last-of-type { border-bottom: 1px solid var(--ink16); }
@media (min-width: 720px) { .svc { grid-template-columns: 200px 1fr; gap: 24px; } }
.svc .k { font-size: 14px; font-weight: 500; color: var(--ink); }
.svc .k em { font-style: normal; font-family: var(--mono); font-size: 8.5px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gold-ink);
  margin-left: 8px; }
.svc .v { font-size: 14px; line-height: 1.65; color: var(--ink64); }

.ctr { text-align: center } .mxa { margin-left: auto; margin-right: auto }
.m12{margin-top:12px} .m18{margin-top:18px} .m26{margin-top:26px}
.m36{margin-top:36px} .m64{margin-top:64px}
.mw18{max-width:18em} .mw24{max-width:24em} .mw30{max-width:30em}
.row{display:flex} .g12{gap:12px} .wf{flex-wrap:wrap}

/* ═══════ SYSTEM LAYER ═══════
   The chrome that rides the film. Everything here is driven per-frame from
   work.js off the same scrub value as the footage, so system state and film
   state can never disagree. */

/* letterbox mattes; --cover 0..1 thickens them into a full beat */
#mattes { position: fixed; inset: 0; z-index: 3; pointer-events: none; }
/* A CONSTANT frame. Earlier cuts closed these bars to a 48vh band at every
   clip joint to carry an interstitial quote; that read as the picture opening
   and shutting while you scrolled, and the quote landed on top of the act copy
   underneath it. The letterbox is now fixed: it frames the film and never
   moves. */
#mattes .matte { position: absolute; left: 0; right: 0; background: #0B0906;
  height: 14px; }
#mattes .matte.top { top: 0; }
#mattes .matte.bot { bottom: 0; }
#mattes .mlabel { position: absolute; right: 18px; top: 7px;
  transform: translateY(-50%); font-family: var(--mono); font-size: 8.5px;
  letter-spacing: .22em; text-transform: uppercase; color: var(--w45); }
/* the pipeline rider and step rail were removed in the declutter pass:
   both restated, in a permanent sidebar, information the page delivers as
   real content later. Their JS bindings no-op on the missing nodes. */

/* act slate */
#slate { position: fixed; left: 26px; bottom: 24px; z-index: 4; display: flex;
  align-items: center; gap: 10px; pointer-events: none; opacity: 0; }
#slate .pill { font-family: var(--mono); font-size: 9px; font-weight: 600;
  letter-spacing: .1em; color: var(--on-gold); padding: 4px 8px; border-radius: 999px;
  background: linear-gradient(180deg, #FFD34D, var(--gold)); }
#slate .mn { color: var(--w72); }

/* the flipping before→after readout, lifted from SceneTransform's chip */
.flip { display: inline-flex; align-items: center; gap: 12px; padding: 12px 18px;
  border-radius: 8px; border: 1px solid rgba(232, 181, 77, .3);
  background: rgba(18, 14, 8, .55);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; }
.flip .ffrom { color: var(--w); transition: color .45s, opacity .45s; }
.flip .farr { width: 26px; height: 8px; color: var(--w45); flex: none;
  transition: color .45s; }
.flip .fto { color: var(--w45); transition: color .45s, text-shadow .45s; }
.flip.on .ffrom { color: var(--w45); opacity: .75; }
.flip.on .farr { color: var(--gold); }
.flip.on .fto { color: var(--gold); text-shadow: 0 0 18px rgba(232,181,77,.5); }
/* progressive reveal inside a flip ("3", ", 5", ", 10") */
.flip .rv { font-style: normal; opacity: 0; transition: opacity .4s; }
.flip .rv.on { opacity: 1; }

/* pinned callouts with leader lines */
.pin { position: absolute; z-index: 3; display: flex; align-items: center;
  gap: 0; opacity: 0; transform: translateY(6px);
  transition: opacity .5s ease, transform .5s ease; pointer-events: none; }
.pin.on { opacity: 1; transform: translateY(0); }
.pin .lead { display: block; width: 64px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold));
  transform-origin: right; transform: scaleX(0); transition: transform .6s ease .1s; }
.pin.on .lead { transform: scaleX(1); }
.pin .mn { padding: 8px 12px; border-radius: 6px; color: var(--w72);
  background: rgba(18, 14, 8, .6); border: 1px solid rgba(232,181,77,.26);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.pin .mn b { color: var(--gold); font-weight: 600; }
.pin .mn em { font-style: normal; color: var(--gold-ink); font-size: 8px;
  letter-spacing: .1em; margin-left: 6px; color: var(--gold); opacity: .75; }

/* act-5 gauges, the ClosingBand rows */
.gauges { display: flex; flex-direction: column; gap: 8px; }
.gauges .g { color: var(--w72); display: inline-flex; align-items: center; gap: 9px; }
.gauges .g i { font-style: normal; color: var(--gold); }

/* the chrome belongs to the film; it leaves when the film does */
#rider, #rail, #slate { opacity: var(--sysfade, 1); transition: opacity .35s ease; }

@media (max-width: 1100px) {
  #rider, #rail, #slate, .pin { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important;
    animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
