/* Tyll: chrome, layout, sections. Every value comes from tokens.css. */
*,*::before,*::after{box-sizing:border-box}
/* NO scroll-snap, tried twice now and reverted twice. Round 8 put y proximity
   on two sections and measured seven consecutive notches at 0px. This round
   put it on every section top with scroll-snap-align:none on the sticky frames
   so a scrubbing pin would have no target of its own - and real WheelEvents
   still measured 0px at four positions: across the hero boundary, in the
   problem pin, in the how pin and in the getting started pin. Snap and a
   scroll-scrubbed pin cannot share a scroller. The half-way state is handled
   by geometry instead: the hero's exit is reclaimed and every pin is shorter. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;background:var(--paper)}
body{margin:0;background:var(--paper);color:var(--navy);font-family:var(--sans);font-size:var(--fs-body);line-height:1.62;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
/* ---------- type ---------- */
h1,h2,h3,h4{font-family:var(--display);font-weight:500;color:var(--navy);line-height:1.08;letter-spacing:-.012em;margin:0;text-wrap:balance;font-variation-settings:"opsz" 48,"SOFT" 0,"WONK" 0}
h1{font-size:var(--fs-stat);line-height:1.04}
h2{font-size:var(--fs-h2)}
h3{font-size:var(--fs-lede);line-height:1.22;font-weight:600;font-variation-settings:"opsz" 20,"SOFT" 0,"WONK" 0}
h4{font-size:1rem;font-weight:600;font-variation-settings:"opsz" 14}
p{margin:0}
.lede{font-size:var(--fs-lede);line-height:1.6;color:var(--grey);max-width:58ch}
/* before a component               */
.kicker{font-family:var(--sans);font-size:.7rem;font-weight:600;letter-spacing:.17em;text-transform:uppercase;color:var(--grey);display:flex;align-items:center;gap:.75em;margin:0 0 1.35rem}
.kicker::before{content:"";height:1px;width:clamp(18px,2.6vw,34px);background:var(--accent);flex:none;transform:scaleX(0);transform-origin:left;transition:transform var(--t-reveal) var(--ease) .05s}
.in .kicker::before,.kicker.in::before{transform:scaleX(1)}
a{color:var(--accent);text-underline-offset:.22em;text-decoration-thickness:1px}
a:hover{color:var(--accent-hover)}
/* ---------- layout ---------- */
.wrap{width:min(1280px,100% - var(--gut)*2);margin-inline:auto}
.narrow{width:min(860px,100% - var(--gut)*2);margin-inline:auto}
section{padding-block:var(--sec-pad);position:relative;scroll-margin-top:var(--nav-h,76px)}
.alt{background-color:var(--paper-alt)}
/* ---------- night ----------
   (class is .dark: .night is already the calendar square in .nights)
   The hero's footage dissolves into flat --navy and this section is the same
   --navy, with no border and no padding between them. The reader never sees a
   section boundary — the page simply keeps being night while the problem is
   described. Nothing here may use --accent: blue on navy is 2.26:1. */
.dark{background-color:var(--paper-alt);color:var(--grey);border:0}
.dark h1,.dark h2,.dark h3,.dark h4,.dark strong{color:var(--navy)}
.dark p,.dark li,.dark .body-text{color:var(--grey)}
.dark .kicker{color:var(--grey-2)}
.dark .kicker::before{background:var(--line)}
.dark hr,.dark .hr{background:var(--line)}
/* ---------- the section header ----------
   Every section from the Problem down opens the same way: a mono-caps
   eyebrow and a serif display headline on the left, sans body on the right,
   with a hairline above and below. The serif/sans split is the whole look —
   it is not decoration and it does not vary per section. */
/* The right column begins level with the HEADLINE's first line, not with the
   eyebrow above it. The offset is the eyebrow's own line box plus its margin,
   so the two columns start together whatever the headline is. One rule for
   every two-column section header. */
.sec-head > .sec-body{padding-top:calc(1.125rem + var(--sp-3))}
.sec-head{display:grid;grid-template-columns:1.05fr .95fr;gap:var(--sp-col);align-items:start;padding-block:var(--sec-gap);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.dark .sec-head{border-color:var(--line)}
.eyebrow{display:flex;align-items:center;gap:.7rem;margin:0 0 var(--sp-3);font-family:var(--mono);font-size:.6875rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;color:var(--grey)}
.dark .eyebrow{color:var(--grey-2)}
.sec-head h2{margin:0;font-family:var(--display);font-size:var(--fs-h2);font-weight:400;line-height:1.1;letter-spacing:-.015em;color:var(--navy)}
.dark .sec-head h2{color:var(--navy)}
.sec-head .sec-body{margin:0;display:grid;gap:1.1rem}
.sec-head .sec-body p{margin:0;font-size:1rem;line-height:1.65;color:var(--grey)}
.dark .sec-head .sec-body p{color:var(--grey)}
@media (max-width:820px){
  .sec-head{grid-template-columns:1fr;gap:1.6rem}
}
/* .dark p is (0,1,1) and would otherwise win over .sec-claim (0,1,0) */
.dark p.sec-claim{color:var(--navy)}
/* .daybreak used to carry padding-top:min(46vh,400px) - a beat between the
   hero releasing and the next pin engaging. It never applied, because #how's
   padding-block:0 outranked it; deleting that rule in Task 5 switched 400px of
   dead scroll on. The beat it was written for belongs to whichever section
   follows the hero, and that is the problem section now, so it is gone rather
   than moved: --sec-pad is the only section padding on the page. */
.hr{height:1px;background:var(--line);border:0;margin:0}
/* ---------- buttons ---------- */
.btn{font-family:var(--sans);font-size:.9375rem;font-weight:500;display:inline-flex;align-items:center;justify-content:center;gap:.55em;padding:.8em 1.45em;border-radius:var(--r);border:1px solid transparent;text-decoration:none;cursor:pointer;transition:background-color var(--t-section) var(--ease),color var(--t-section) var(--ease),
             border-color var(--t-section) var(--ease),transform var(--t-section) var(--ease);letter-spacing:0;min-height:46px}
.btn-primary{background:var(--accent);color:var(--paper);border-color:var(--accent)}
.btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--paper);transform:translateY(-1px)}
.btn-line{background:transparent;color:var(--accent);border-color:var(--line)}
.btn-line:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px)}
.btn .arw{transition:transform var(--t-section) var(--ease)}
.btn:hover .arw{transform:translateX(3px)}
@media (pointer:coarse){.btn{min-height:44px;display:inline-flex;align-items:center;justify-content:center}}
/* fixed, not absolute: an absolutely positioned skip link sits at the top of
   the DOCUMENT, so once the reader has scrolled it focuses off screen */
.skip{position:fixed;left:-9999px;top:0;z-index:99;background:var(--navy);color:var(--paper);padding:.8em 1.2em;text-decoration:none}
.skip:focus{left:8px;top:8px}
/* focus never lands under the fixed bar */
a,button,input,summary,[tabindex]{scroll-margin-top:calc(var(--nav-h,80px) + 16px);scroll-margin-bottom:24px}
/* ---------- nav ----------
   Frosted glass that fades out downward and has no edge at all. See the
   .nav::before block below: the bar itself is transparent and carries only
   the contents. */
.nav{position:fixed;top:0;left:0;right:0;z-index:50;display:flex;align-items:center;justify-content:space-between;gap:1.4rem;padding:1rem var(--gut);border:0;background:transparent}
.lockup .logo{height:32px;width:auto;display:block}
.nav-links{display:none;align-items:center;gap:var(--sp-2)}
.nav-links a{font-family:var(--sans);font-size:.8125rem;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:color-mix(in srgb,var(--paper) 75%,transparent);text-decoration:none;position:relative;padding-block:.3em;transition:color var(--t-section) var(--ease);min-height:44px;display:inline-flex;align-items:center}
.nav-links a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--paper);transform:scaleX(0);transform-origin:right;transition:transform var(--t-section) var(--ease)}
.nav-links a:hover{color:var(--paper)}
.nav-links a:hover::after{transform:scaleX(1);transform-origin:left}
.nav-right{display:flex;align-items:center;gap:.8rem}
@media (min-width:1000px){.nav-links{display:flex}}
/* the one button. Over the film it is the flat accent on dark footage; once
   the bar is navy the flat accent would be 2.26:1, so it lifts. */
.nav .btn-primary{background:var(--accent);color:var(--paper);border-color:var(--accent);font-family:var(--sans);font-size:.8125rem;letter-spacing:.06em}
.nav .btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--paper)}
.nav.grounded .btn-primary{background:var(--accent);color:var(--paper);border-color:var(--accent)}
.nav.grounded .btn-primary:hover{background:color-mix(in srgb,var(--accent-lift) 82%,var(--paper));border-color:color-mix(in srgb,var(--accent-lift) 82%,var(--paper));color:var(--navy)}
.nav-toggle{display:inline-flex;flex-direction:column;justify-content:center;align-items:flex-end;gap:5px;width:44px;height:44px;background:none;border:0;cursor:pointer;color:var(--paper);padding:0}
.nav-toggle i{display:block;height:1px;width:21px;background:currentColor;transition:transform var(--t-section) var(--ease),opacity var(--t-section) var(--ease)}
body.menu-open .nav-toggle i:nth-child(1){transform:translateY(3px) rotate(45deg)}
body.menu-open .nav-toggle i:nth-child(2){opacity:0}
body.menu-open .nav-toggle i:nth-child(3){transform:translateY(-3px) rotate(-45deg)}
@media (min-width:1000px){.nav-toggle{display:none}}
@media (max-width:999px){
  .nav-right > .btn{display:none}
  /* THE PANEL. Full screen, solid navy, links stacked with room to breathe.
     It slides on transform, never height, and sits under the bar so the close
     control stays reachable. z-index was -1, which put it BEHIND the page. */
  .nav-links{
    position:fixed;inset:0;z-index:48;display:flex;flex-direction:column;
    align-items:stretch;justify-content:flex-start;
    gap:0;padding:calc(var(--nav-h,76px) + var(--sp-4)) var(--gut) calc(var(--sp-4) + env(safe-area-inset-bottom));
    height:100dvh;background:var(--paper);overflow-y:auto;
    transform:translateY(-100%);transition:transform var(--t-reveal) var(--ease);
    visibility:hidden;will-change:transform;
  }
  body.menu-open .nav-links{transform:translateY(0);visibility:visible}
  .nav-links a{display:flex;align-items:center;min-height:56px;
    font-size:1.5rem;letter-spacing:.01em;text-transform:none;font-family:var(--sans);
    color:var(--navy);border-bottom:1px solid var(--line)}
  .nav-links a::after{display:none}
  /* language and the call, at the foot of the panel */
  .menu-foot{margin-top:auto;padding-top:var(--sp-4);display:flex;
    flex-direction:column;align-items:stretch;gap:var(--sp-3)}
  .menu-foot .lang-menu{justify-content:flex-start}
  .menu-foot .lang-menu a{min-height:44px;border-bottom:0;font-size:.8125rem;
    font-family:var(--mono);letter-spacing:.14em;color:var(--grey)}
  .nav-links .in-menu{display:flex;justify-content:center;min-height:52px;
    font-size:1rem;border-bottom:0;color:var(--paper)}
  body.menu-open{overflow:hidden;position:fixed;width:100%}
}
@media (min-width:1000px){.nav-links .in-menu,.menu-foot{display:none}}
/* Entrances are gone. .rise and .stagger were already dead markup; the
   sec-head rise, the claims rise and the claims draw-on were not. Content
   that is on the page is simply on the page. */
@media (max-width:900px){.split{grid-template-columns:1fr;gap:1.6rem}.split h2{max-width:20ch}}
@media (max-width:820px){.steps{grid-template-columns:1fr;gap:1.6rem}}
@media (max-width:760px){.recon-row{grid-template-columns:2.1rem 1fr;row-gap:.35rem}.recon-row p{grid-column:2}}
.months .m{aspect-ratio:1/2.1;background:var(--paper);border:1px solid var(--line);border-radius:var(--r);position:relative;overflow:hidden}
.months .m::after{content:"";position:absolute;left:0;right:0;bottom:0;height:100%;background:var(--accent);opacity:.82;/* scaleY, not height:the fill composites instead of relaying out */
  transform:scaleY(var(--fill,0));transform-origin:bottom;transition:transform var(--t-count) var(--ease)}
.months .m.on span{color:var(--paper)}
@media (max-width:700px){.months{grid-template-columns:repeat(6,1fr)}}
@keyframes nightIn{from{opacity:0;transform:scale(.72)}to{opacity:1;transform:none}}
@media (max-width:620px){.nights{grid-template-columns:repeat(7,1fr)}}
/* the sliders: --line track, --accent fill and thumb */
/* ---------- FAQ ---------- */
.faq{border-top:1px solid var(--line);margin-top:var(--sp-5)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{display:flex;gap:1.2rem;align-items:baseline;justify-content:space-between;padding:var(--sp-2) 0;cursor:pointer;list-style:none;font-family:var(--display);font-size:var(--fs-lede);font-weight:600;line-height:1.26;color:var(--navy);font-variation-settings:"opsz" 20;transition:color var(--t-section) var(--ease)}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--accent)}
.faq .pm{flex:none;width:11px;height:11px;position:relative;margin-top:.42em}
.faq .pm::before,.faq .pm::after{content:"";position:absolute;background:var(--grey);transition:transform var(--t-section) var(--ease),background-color var(--t-section) var(--ease)}
.faq .pm::before{left:0;right:0;top:5px;height:1px}
.faq .pm::after{top:0;bottom:0;left:5px;width:1px}
.faq details[open] .pm::after{transform:scaleY(0)}
.faq details[open] .pm::before{background:var(--accent)}
.faq .ans{padding:0 0 var(--sp-3);color:var(--navy);max-width:62ch}
.faq .ans p + p{margin-top:.85em}
/* ---------- footer ---------- */
.foot .lockup .logo{height:34px}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:.6rem;font-size:.9rem}
.foot a{color:var(--grey);text-decoration:none;transition:color var(--t-section) var(--ease)}
.foot a:hover{color:var(--accent)}
@media (max-width:760px){.foot-top{grid-template-columns:1fr 1fr}.foot-top > :first-child{grid-column:1/-1}}
/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;transition-delay:0s !important}
  .kicker::before{transform:scaleX(1) !important}
  @media (max-width:999px){
    .nav-links{transform:translateY(-100%)}
    body.menu-open .nav-links{transform:translateY(0)}
  }
}
body.paused *,body.paused *::before,body.paused *::after{animation-play-state:paused !important}
/* visually hidden, still announced */
@media (max-width:820px){.rollup{grid-template-columns:1fr}}
.hero-top .lede{max-width:56ch}
.acts{display:flex;flex-wrap:wrap;gap:.8rem;justify-content:center;margin-top:2.1rem}
/* On a phone the five columns cannot coexist, and horizontal scrolling
   hides Total and Status, which are the two that matter. Stack instead. */
@media (max-width:680px){
  .rtable table{min-width:0}
  .rtable thead{display:none}
  .rtable tbody tr{
    display:grid;
    grid-template-columns:auto auto 1fr;
    grid-template-areas:"chk chk amt" "tbl tnd st";
    gap:.3rem .5rem;align-items:baseline;
    padding:.9rem var(--sp-2);
    border-bottom:1px solid var(--line);
  }
  .rtable tbody tr:last-child{border-bottom:0}
  .rtable td{border:0;padding:0}
  .rtable td.chk{grid-area:chk;font-size:.92rem}
  .rtable td.amt{grid-area:amt;text-align:right;font-weight:500}
  .rtable td.tbl{grid-area:tbl;font-size:.82rem}
  .rtable td.tbl::before{content:"Table "}
  .rtable td.tnd{grid-area:tnd;font-size:.82rem}
  .rtable td.tnd::before{content:"· "}
  .rtable td.st{grid-area:st;text-align:right}
  .rtable tr.reason{display:block;padding-top:0}
  .rtable tr.reason td{display:block}
}
/* ---------- how it works: the pinned progressive list ----------
   Each item carries its own --lit, a 0-1 number the engine interpolates from
   scroll position. Nothing toggles at a threshold, so between two steps both
   are partially lit and the change reads as a crossfade, not a slide change. */
/* 170vh gave 630px of pin and 900px of exit. The pin is fully used - the
   icons, the lit states and the film all run to its end - so the only
   shortenable part is the pin; the exit is a sticky frame travelling its own
   height and cannot be removed without unpinning the section. */
.hiw{--hiw-scroll:175vh;position:relative;height:var(--hiw-scroll)}
.hiw-frame{position:sticky;top:var(--nav-h,76px);height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;padding-block:var(--sec-pad);display:flex;align-items:flex-start}
.hiw-body{display:grid;grid-template-columns:46% 54%;align-items:stretch;
  /* fills the room the frame actually has, measured rather than guessed */
  /* the panel's own min-height sizes this row now; the rem constant here
     was tuned for the old film and forced the body past the frame */
  min-height:0}
.hiw-list{position:relative;border-bottom:1px solid var(--line)}

/* the thin progress rule down the left of the list */
.hiw-progress{display:none}
.hiw-progress i{/* Navy, not accent:a progress rule is neither a primary action nor an
     exception in the data, so by the accent rule it is decoration. */
  position:absolute;inset:0;background:var(--navy);transform:scaleY(var(--hiw-fill,0));transform-origin:top}
.hiw-ico *{stroke-dasharray:var(--len,120);stroke-dashoffset:calc(var(--len,120) * (1 - var(--lit,0)));transition:stroke-dashoffset var(--t-section) var(--ease)}
/* The film is absolutely placed so it takes no part in sizing the row. Left
   in flow it sat at its intrinsic 720px and inflated the whole section. The
   row's height comes from the list; the film fills whatever that is. */
/* ---- no pin: every step open and fully lit, diagrams stacked ---- */
@media (max-width:760px){
  .hiw{--hiw-scroll:auto;height:auto}
  .hiw-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .hiw-body{grid-template-columns:1fr;min-height:0}
  .hiw-list{border-right:0;padding-right:0}
  .hiw-static-ico{display:block}
  .hiw-item{--lit:1}
  .hiw-ico *{stroke-dashoffset:0}
  .hiw-progress{display:none}
  .hiw-stage{display:contents}
}
@media (prefers-reduced-motion:reduce){
  .hiw{--hiw-scroll:auto;height:auto}
  .hiw-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .hiw-body{grid-template-columns:1fr;min-height:0}
  .hiw-list{border-right:0;padding-right:0}
  .hiw-static-ico{display:block}
  .hiw-item{--lit:1}
  .hiw-ico,.hiw-item h3,.hiw-item p{transition:none}
  .hiw-ico *{stroke-dashoffset:0;transition:none}
  .hiw-progress i{transform:scaleY(1)}
  .hiw-stage{display:contents}
}
/* the stage diagrams: schematic, four colours, 1px and 1.5px strokes only */
/* ---------- micro-labels ----------
   Mono caps, letterspaced, small, grey. Used everywhere as the quiet
   annotation layer. */
/* ---------- operations ----------
   The header holds while the cards scroll up past it; it releases when the
   last card clears. Cards arrive once, left to right, and each one's single
   accent element animates a beat after its frame.

   Four cards side by side are only as tall as one card, so the sticky header
   had barely 390px of travel — the hold was technically correct and visually
   invisible. The diagrams are square rather than 4:3 and the grid carries a
   min-height, which buys the header roughly a screen of hold without
   inventing a pin or restacking the specced layout. */
.ops-head{position:relative;z-index:1;flex:none;padding-bottom:var(--sp-3)}
.ops-grid{display:grid;grid-template-columns:repeat(4,1fr);margin-top:var(--sp-4);min-height:0;align-content:stretch;margin-inline:calc(var(--sp-2) * -1);flex:0 1 auto;
  /* label / diagram / heading / body. Subgrid so the four columns share these
     four rows: without it each column absorbed a different amount of slack
     into its diagram and the headings stopped lining up. */
  grid-template-rows:auto auto minmax(0,1fr) auto}
/* every column keeps the same horizontal padding so the four diagram panels
   come out identical; the grid is pulled out by that padding so the section
   still sits flush to its frame */
/* Every tile is present and legible from the start, drawn in --line and
   --grey. Scrolling does not bring them in; it LIGHTS them, one after
   another, left to right, and each stays lit. */
.ops-col{padding:0 var(--sp-2);border-left:1px solid var(--rule,var(--line));
  display:grid;grid-template-rows:subgrid;grid-row:span 4;min-height:0;
  transform:translateY(calc(var(--on,0) * -14px));
  transition:transform .72s cubic-bezier(.16,.84,.33,1)}
/* Each tile arrives entirely in --line and --grey. Its single accent element
   is drawn in currentColor and starts grey, so "colouring up" is one colour
   transition on one property, 300ms after that tile has landed. */
.ops-accent{color:var(--grey);transition:color .5s var(--ease)}
.ops-col.lit .ops-accent{color:var(--accent)}
.ops-col.lit{--on:1}
@media (max-width:980px){
  .ops-grid{grid-template-columns:repeat(2,1fr);row-gap:2.4rem;min-height:0}
  .ops-col:nth-child(3){border-left-color:transparent}
}
@media (max-width:860px){
  .ops{height:auto}
  .ops-frame{position:static;height:auto;display:block;overflow:visible;padding-block:var(--sec-pad)}
  .ops-col{transform:none!important}
  .ops-accent{color:var(--accent)!important}
  .ops-head{position:static}
  .ops-grid{grid-template-columns:1fr;row-gap:2.4rem;min-height:0;margin-inline:0}
  /* stacked, the square panel would otherwise be as tall as the viewport */
  .ops-dgm{max-width:320px}
  .ops-col{border-left:0;padding:0;opacity:1;transform:none;transition:none}
  .ops-accent{color:var(--accent);transition:none}
}
@media (prefers-reduced-motion:reduce){
  .hiw-item h3{transform:none}
  /* Operations was the one pin reduced motion never released: every other
     section unpins through a query that includes this one, this did not. */
  .ops{height:auto}
  .ops-frame{position:static;height:auto;display:block;overflow:visible;padding-block:var(--sec-pad)}
  /* the four shapes side by side, so the panel still says what it says */
  .ops-head{position:static}
  .ops-col{opacity:1;transform:none;transition:none;transition-delay:0s}
  .ops-accent{color:var(--accent);transition:none;transition-delay:0s}
}
/* "4 in 5" is text: it fades, it does not count */
/* the seven lands last, in accent, a touch larger */
/* Not accent: four blue squares on one screen is decoration, and blue
   on this page marks a primary action or an exception in data. */
/* ---------- how we start ----------
   Pinned, and the highlight is continuous: every row carries a --lit the
   engine interpolates, so between two steps both are partially lit. The
   connector fills like a progress bar — that is what the eye follows.
   Navy ground, so nothing here may use --accent (2.26:1). */
.starter{--starter-scroll:130vh;position:relative;height:var(--starter-scroll)}
.starter-frame{position:sticky;top:var(--nav-h,76px);height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;padding-block:var(--sec-pad);display:flex;flex-direction:column;justify-content:flex-start;min-height:0}
.starter-frame > .steps-list{flex:0 1 auto;min-height:0}
.step-row{--n-size:46px;position:relative;display:grid;grid-template-columns:var(--n-size) 1fr;gap:1.5rem;
  padding-bottom:clamp(1rem,3.1vh,2.4rem)}
/* only the lit step's button is there to be pressed */
/* Held in the section frame, not in step 1, so it stays put for the whole pin.
   --accent-lift on --navy: flat --accent is 2.3:1 on this ground. */
/* Sits under step 1 and nowhere else: it does not float, does not follow the
   viewport and does not appear in any other section. Navy ground, so
   --accent-lift with --navy text; flat --accent is 2.3:1 here. */
.start-cta{display:inline-flex;margin-top:var(--sp-3);
  background:var(--accent);color:var(--paper);border-color:var(--accent)}
@media (max-width:760px){
  .starter{--starter-scroll:auto;height:auto}
  .starter-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .step-row{--lit:1;--snap:1;--seg:1}
}
@media (prefers-reduced-motion:reduce){
  .starter{--starter-scroll:auto;height:auto}
  .starter-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .step-row{--lit:1;--snap:1;--seg:1}
  .step-n,.step-row h3,.step-row p{transition:none}
}
/* ---------- the close: flat blue, one button ---------- */
/* ---------- footer ---------- */
/* ---------- security facts ---------- */
/* ---------- the three claims ----------
   Same line-art family as the step icon: 1.5px, --accent, 36px optical size.
   Each mark draws itself in by running its dash offset back to zero; the
   dasharray is set from the real path length in JS, so it is exact per shape. */
.sec-claims{list-style:none;margin:var(--space-block) 0 0;padding:0;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-col)}
.sec-claim{align-items:flex-start}
.sec-facts{margin-top:var(--space-block)}
.sec-claim{display:flex;align-items:flex-start;gap:var(--sp-2)}
.sec-claim h3{margin:0 0 .4rem;font-family:var(--sans);font-size:1.0625rem;font-weight:600;color:var(--navy)}
.sec-claim p{margin:0;font-size:.9375rem;line-height:1.55;color:var(--grey)}
/* The detail is simply shown. The hover-to-expand from last round is gone:
   a disclosure that only opens on hover hides the thing that matters most. */
.sec-more{margin:.55rem 0 0;color:var(--grey)}
.sec-mark{flex:none;width:36px;height:36px;overflow:visible}

.sec-mark path,.sec-mark rect{fill:none;stroke:var(--accent);stroke-width:1.5;
  stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke;
  stroke-linecap:round}
@media (max-width:860px){
  .sec-claims{grid-template-columns:1fr;gap:var(--sp-3)}
}
.sec-facts{list-style:none;margin:var(--space-block) 0 0;padding:0;display:grid;grid-template-columns:repeat(4,1fr)}
.sec-facts li{display:flex;align-items:center;gap:.75rem;padding:0 var(--sp-2);border-left:1px solid var(--line);font-family:var(--mono);font-size:.6875rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--grey);line-height:1.5}
@media (max-width:900px){
  .sec-facts{grid-template-columns:1fr 1fr;row-gap:1.1rem}
  .sec-facts li:nth-child(3){border-left:0;padding-left:0}
}
@media (max-width:520px){
  .sec-facts{grid-template-columns:1fr;row-gap:1rem}
  .sec-facts li{border-left:0;padding:0}
}
/* ---------- AA on the banded ground ----------
   --grey is 4.67:1 on --paper but 4.41:1 on --paper-alt, which is under AA.
   Every grey that sits on a banded section steps down to --grey-deep. */
/* ---------- the FAQ page ---------- */
/* the bar has no film behind it here, so it never runs the transparent state */
/* the answers keep a readable measure now the page uses the full wrap */
/* ══════════════════════════════════════════════════════════════
   Apple-design pass
   ══════════════════════════════════════════════════════════════ */
/* §1 Response — feedback belongs on the press, not the release.
   Nothing on this page acknowledged pointer-down at all.

   These selectors are written at the same specificity as the :hover rules
   they have to beat — .nav .btn-primary:hover is (0,3,0), so a bare
   .btn:active at (0,2,0) loses no matter where it sits in the file. */
/* §10 Tap targets — several sat between 11px and 41px tall */
/* §12 Materials — the bar carried a 14px blur behind a fully opaque fill,
   which costs GPU and buys nothing. It is now a real translucent layer with
   content passing beneath it. Still navy, never light. */
/* the sticky Operations header hid content behind a hard opaque edge */
/* §14 Reduced transparency — frostier means solid, not blurred */
@media (prefers-reduced-transparency: reduce){
  .nav::before{--nav-op:100%;
    -webkit-mask-image:none;mask-image:none;bottom:0}
  .ops-head{background:var(--paper);backdrop-filter:none}
  .cover-scrim{background:var(--navy);opacity:.86}
}
/* §14 Increased contrast — near-solid grounds and defined edges */
@media (prefers-contrast: more){
  :root{
    --grey:#4B5361;          /* 7.4:1 on paper rather than 4.67:1 */
    --text-muted-alt:#3A414D;
    --line:#B9BDC6;
    --line-dark:#5A6A88;
  }
  .nav::before{--nav-op:100%;
    -webkit-mask-image:none;mask-image:none;bottom:0}
  .ops-head{background:var(--paper);backdrop-filter:none}
  .btn{border-width:2px}
  .hiw-item h3,.step-row h3,.step-row p,.hiw-ico{opacity:1 !important}
  .sec-head,.hiw-sticky{border-color:var(--line)}
}
/* §14 Reduced motion already covered above; the press feedback goes too */
@media (prefers-reduced-motion: reduce){
  .btn:active,.nav-links a:active,.cta-link:active,
  .cta-link-quiet:active,summary:active,.start-cta:active{transform:none}
}
/* the question is a heading, but it still reads as a summary row */
/* ---------- tonight: a night audit done by hand ----------
   Six scroll steps. The clock runs 23:00 -> 00:05, the elapsed counter
   climbs to 65:00, and the ledger is checked line by line. Four rows are
   wrong and are set IDENTICALLY to the other twelve: in a manual close the
   mismatches do not announce themselves. They only light at step 4, and
   blue arrives properly in the next section. */
/* A pinned section's own padding-block is dead scroll: it sits outside the pin,
   so it reads as empty travel before the frame catches and after it releases.
   The sticky frame centres its own contents, so the padding buys nothing. */
/* ---- the clock ---- */
/* ---- the captions: one visible at a time, crossfading ---- */
/* left AND right must be set, or the absolute box shrink-wraps to nothing */
/* ---- the ledger ---- */
/* Absolutely positioned, so showing and hiding it animates nothing but
   opacity and transform — animating its height reflowed the whole ledger on
   every frame of the step-2 handover. */
/* the checking bar that travels down the ledger */
/* a 1px rule that rides the frontier where rows are arriving, then makes the
   checking pass. Its glow is a separate, taller box so the line itself stays 1px. */
/* a faint tick once a row has been checked */
/* step 4 only: the four wrong rows and their plain-language reason */
/* the reason gets its own spanning row: a display:block td inside a
   table-row lays out at zero width */
/* Only opacity is animated. The height change happens once, in one reflow,
   rather than being tweened across every frame of the step. */
/* ---------- how it works: the pinned progressive list ----------
   Each item carries its own --lit, a 0-1 number the engine interpolates from
   scroll position. Nothing toggles at a threshold, so between two steps both
   are partially lit and the change reads as a crossfade, not a slide change. */
.hiw-sticky{border:0;padding:0}
/* the thin progress rule down the left of the list */
.hiw-item{padding:clamp(.6rem,1.4vh,1.05rem) 0;border-top:1px solid var(--line)}
.hiw-row{align-items:center}

.hiw-row{display:flex;align-items:center;gap:.8rem;margin-bottom:.45rem}
.hiw-ico{flex:none;width:28px;height:28px;/* the draw-on is the dash offset */
  color:color-mix(in srgb,var(--accent) calc(var(--lit,0) * 100%),var(--grey));opacity:calc(.55 + var(--lit,0) * .45);transition:color var(--t-section) var(--ease),opacity var(--t-section) var(--ease)}
.hiw-item h3{margin:0;font-family:var(--sans);font-size:1.25rem;font-weight:600;letter-spacing:-.005em;color:color-mix(in srgb,var(--accent) calc(var(--lit,0) * 100%),var(--navy));/* the brief's 32% floor composites to 2.97:1 on paper;this is the
     lowest floor that still clears AA while reading as stepped back */
  opacity:calc(.66 + var(--lit,0) * .34);
  /* the active title lifts 2px; interpolated, so between two steps both are
     part-lifted and part-lit — no threshold anywhere */
  transform:translateY(calc(var(--cur,0) * -2px));
  transition:color var(--t-section) var(--ease),opacity var(--t-section) var(--ease),transform var(--t-section) var(--ease)}
.hiw-item p{margin:0;font-size:1.0625rem;line-height:1.55;color:color-mix(in srgb,var(--navy) calc(var(--lit,0) * 100%),var(--grey));transition:color var(--t-section) var(--ease)}
/* ---- no pin: every step open and fully lit, diagrams stacked ---- */
@media (max-width:760px){
  .hiw{--hiw-scroll:auto;height:auto}
  .hiw-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .hiw-body{grid-template-columns:1fr;min-height:0}
  .hiw-list{border-right:0;padding-right:0}
  .hiw-static-ico{display:block}
  .hiw-item{--lit:1}
  .hiw-ico *{stroke-dashoffset:0}
  .hiw-progress{display:none}
  .hiw-stage{display:contents}
}
@media (prefers-reduced-motion:reduce){
  .hiw{--hiw-scroll:auto;height:auto}
  .hiw-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .hiw-body{grid-template-columns:1fr;min-height:0}
  .hiw-list{border-right:0;padding-right:0}
  .hiw-static-ico{display:block}
  .hiw-item{--lit:1}
  .hiw-ico,.hiw-item h3,.hiw-item p{transition:none}
  .hiw-ico *{stroke-dashoffset:0;transition:none}
  .hiw-progress i{transform:scaleY(1)}
  .hiw-stage{display:contents}
}
/* the stage diagrams: schematic, four colours, 1px and 1.5px strokes only */
.dgm{width:100%;height:100%;max-height:clamp(270px,38vh,420px);display:block}
/* ---------- micro-labels ----------
   Mono caps, letterspaced, small, grey. Used everywhere as the quiet
   annotation layer. */
.micro{margin:0;font-family:var(--mono);font-size:.625rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;color:var(--grey)}
.dark .micro{color:var(--grey-2)}
/* ---------- operations ----------
   The header holds while the cards scroll up past it; it releases when the
   last card clears. Cards arrive once, left to right, and each one's single
   accent element animates a beat after its frame.

   Four cards side by side are only as tall as one card, so the sticky header
   had barely 390px of travel — the hold was technically correct and visually
   invisible. The diagrams are square rather than 4:3 and the grid carries a
   min-height, which buys the header roughly a screen of hold without
   inventing a pin or restacking the specced layout. */
/* ---------- Operations: pinned while the four tiles arrive ----------
   The pin exists only so the four have room to land one at a time. Scroll
   input is never touched - scroll fast and you fly through, which is fine. */
.ops{position:relative;height:var(--ops-scroll,175vh)}
.ops-frame{position:sticky;top:var(--nav-h,76px);height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;padding-block:var(--sec-pad);
  display:flex;flex-direction:column;justify-content:flex-start;overflow:hidden}
.ops-head .sec-head{border-bottom:0}
/* The logo row. Each mark is an official SVG painted in currentColor, so one
   colour drives all of them; heights are set per mark because equal pixel
   heights do not read as equal size across different aspect ratios. */
.ops-systems li{font-family:var(--mono);font-size:.5625rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--grey);padding:0 .7rem;border-left:1px solid var(--line);line-height:1.9}
.ops-systems li.opl{display:flex;align-items:center;padding:0 .5rem;line-height:1}
.ops-systems li{display:flex;align-items:center;min-height:18px}
.ops-systems li:first-child{padding-left:0;border-left:0}
.ops-col:first-child{border-left-color:transparent}
/* the diagram is the one elastic thing in the column: it takes whatever the
   frame has left after the icon, the label, the heading and the body, so a
   768px screen shrinks the panel instead of pushing the text out of the pin */
/* It fills its grid track rather than claiming a square: with aspect-ratio
   driving the height the panel grew past its track and painted over the body
   text below it. The square-ish proportion survives as a max-height. */
.ops-dgm{margin:0;background:var(--paper-alt);
  height:100%;min-height:0;max-height:min(42vh,340px);
  display:grid;place-items:center;padding:.6rem;overflow:hidden}
/* the brief's separation between the title block and the panel; it lives on
   the title rather than the panel because a margin on a height:100% grid item
   overflows its track and paints over the row below */
.ops-tt{margin-bottom:var(--sp-3)}
.ops-body{margin-top:1rem}
.ops-dgm > .dgm{width:100%;height:100%;max-height:100%;object-fit:contain}
/* the Trends panel carries its own window row, as the product does, so it does
   not inflate the title row that all four tiles share */
.ops-dgm-win{grid-template-rows:auto 1fr;place-items:stretch}
.ops-dgm-win .ops-win{margin:0 0 .35rem;justify-content:flex-start}
.ops-dgm-win > .dgm{min-height:0}
/* ---- the tile icon: static, above the label, 32px ---- */
.ops-ico{height:32px;margin-bottom:.55rem;display:flex;align-items:flex-end}
/* until the files land, the slot is visibly empty rather than silently so */

.dgm-sm{width:100%;height:100%;display:block}
/* label, title and - on Trends - the window row, as ONE block above the
   panel. Keeping them in one grid cell is what lets the four columns stay
   aligned when only one of them carries a window row. */
.ops-tt{align-self:start}
.ops-col h3{margin:0;font-family:var(--sans);font-size:1rem;font-weight:600;color:var(--navy)}
.ops-body{font-size:.9rem;line-height:1.55;color:var(--grey)}
/* the one blue thing per card, arriving after the card */

@media (max-width:980px){
  .ops-grid{grid-template-columns:repeat(2,1fr);row-gap:2.4rem;min-height:0}
  .ops-col:nth-child(3){border-left-color:transparent}
}
@media (max-width:860px){
  .ops{height:auto}
  .ops-frame{position:static;height:auto;display:block;overflow:visible;padding-block:var(--sec-pad)}
  .ops-col{transform:none!important}
  .ops-accent{color:var(--accent)!important}
  .ops-head{position:static}
  .ops-grid{grid-template-columns:1fr;row-gap:2.4rem;min-height:0;margin-inline:0}
  /* stacked, the square panel would otherwise be as tall as the viewport */
  .ops-dgm{max-width:320px}
  .ops-col{border-left:0;padding:0;opacity:1;transform:none;transition:none}
  .ops-accent{color:var(--accent);transition:none}
}
@media (prefers-reduced-motion:reduce){
  .hiw-item h3{transform:none}
  /* Operations was the one pin reduced motion never released: every other
     section unpins through a query that includes this one, this did not. */
  .ops{height:auto}
  .ops-frame{position:static;height:auto;display:block;overflow:visible;padding-block:var(--sec-pad)}
  /* the four shapes side by side, so the panel still says what it says */
  .ops-head{position:static}
  .ops-col{opacity:1;transform:none;transition:none;transition-delay:0s}
  .ops-accent{color:var(--accent);transition:none;transition-delay:0s}
}
/* "4 in 5" is text: it fades, it does not count */
/* the seven lands last, in accent, a touch larger */
/* Not accent: four blue squares on one screen is decoration, and blue
   on this page marks a primary action or an exception in data. */
/* ---------- how we start ----------
   Pinned, and the highlight is continuous: every row carries a --lit the
   engine interpolates, so between two steps both are partially lit. The
   connector fills like a progress bar — that is what the eye follows.
   Navy ground, so nothing here may use --accent (2.26:1). */
/* the button now lives under step 1, so the heading owns the row */
.start-head{margin:0 0 var(--sp-5);font-family:var(--sans);font-size:clamp(2rem,3.6vw,3rem);font-weight:600;letter-spacing:-.015em;color:var(--navy)}
.steps-list{list-style:none;margin:0;padding:0;max-width:58ch;position:relative}
/* The connector used to be ONE absolute line down the whole column, drawn at
   x = half the box width, with the boxes transparent until they lit - so it
   ran visibly through all four numerals. It is now a segment per gap, from the
   bottom edge of one box to the top edge of the next. There is no line inside
   or behind a box because there is no line there to hide. ::before is the
   track, ::after the fill, so the progressive draw survives the change. */
.step-row:not(:last-child)::before,
.step-row:not(:last-child)::after{content:"";position:absolute;width:1px;
  left:calc(var(--n-size) / 2);top:calc(var(--n-size) + .45rem);bottom:.45rem}
.step-row:not(:last-child)::before{background:var(--line-dark)}
.step-row:not(:last-child)::after{background:var(--accent-lift);
  transform:scaleY(var(--seg,0));transform-origin:top}
.step-row:last-child{padding-bottom:0}
.step-n{position:relative;z-index:1;display:grid;place-items:center;
  width:var(--n-size);height:var(--n-size);aspect-ratio:1;line-height:1;
  font-variant-numeric:tabular-nums;padding-top:.08em;font-family:var(--mono);font-size:.9375rem;font-weight:600;background:color-mix(in srgb,var(--accent-lift) calc(var(--snap,0) * 100%),transparent);border:1px solid color-mix(in srgb,var(--accent-lift) calc(var(--snap,0) * 100%),var(--line-dark));/* THE DIGIT CROSSES ON ITS OWN, STEEPER CURVE. The chip ramps smoothly -
     that is what fixed the stutter - but the digit fading navy<->grey-lift at
     the same rate meant the two converged halfway and the numeral measured
     3.04:1 against its own chip. --flip is --snap pushed through a hard curve,
     so the digit is always on the correct side of its background. One property
     switching is imperceptible; three recomputing in a frame was the stutter. */
  --flip:clamp(0,calc((var(--snap,0) - .5) * 14 + .5),1);
  color:color-mix(in srgb,var(--navy) calc(var(--flip) * 100%),var(--grey-lift));
  opacity:1;transition:background-color var(--t-section) var(--ease),color var(--t-section) var(--ease),
             border-color var(--t-section) var(--ease),opacity var(--t-section) var(--ease)}
.step-row h3{margin:.5rem 0 .5rem;font-family:var(--sans);font-size:1.3rem;font-weight:600;color:color-mix(in srgb,var(--paper) calc(var(--lit,0) * 100%),var(--grey-lift));opacity:calc(.84 + var(--lit,0) * .16);transform:translateY(calc(var(--cur,0) * -2px));transition:color var(--t-section) var(--ease),opacity var(--t-section) var(--ease)}
.step-row p{margin:0;font-size:1.0625rem;line-height:1.6;color:var(--grey);opacity:calc(.84 + var(--lit,0) * .16);transition:opacity var(--t-section) var(--ease)}
/* only the lit step's button is there to be pressed */

.btn-lift:hover,.start-cta:hover{background:color-mix(in srgb,var(--accent-lift) 84%,var(--paper));border-color:color-mix(in srgb,var(--accent-lift) 84%,var(--paper));color:var(--navy)}
@media (max-width:760px){
  .starter{--starter-scroll:auto;height:auto}
  .starter-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .step-row{--lit:1;--snap:1;--seg:1}
}
@media (prefers-reduced-motion:reduce){
  .starter{--starter-scroll:auto;height:auto}
  .starter-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .step-row{--lit:1;--snap:1;--seg:1}
  .step-n,.step-row h3,.step-row p{transition:none}
}
/* ---------- the close: flat blue, one button ---------- */
.cta-blue{background-color:var(--paper)}
/* headline hard left, action hard right, centred against each other */
.cta-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-5);flex-wrap:wrap}
.cta-words{flex:1 1 32rem;min-width:0}
.cta-blue h2{margin:0;color:var(--navy);max-width:20ch}
.cta-lede{margin:var(--sp-3) 0 0;max-width:46ch;color:var(--grey)}
.cta-acts{flex:none;display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2)}
/* white on #39558C is 7.4:1 */
.btn-onblue{background:var(--accent);color:var(--paper);border-color:var(--accent)}
.btn-onblue:hover{background:color-mix(in srgb,var(--paper) 90%,var(--navy));
  border-color:color-mix(in srgb,var(--paper) 90%,var(--navy));color:var(--accent)}
@media (max-width:860px){
  .cta-row{flex-direction:column;align-items:stretch;gap:var(--sp-4)}
  .cta-acts{align-items:stretch}
  .cta-acts .btn{justify-content:center}
}
.cta-blue h2{margin:0;max-width:20ch;font-family:var(--display);font-size:var(--fs-h2);font-weight:600;line-height:1.08;letter-spacing:-.015em;color:var(--navy)}
/* This was written for the blue band the closing section used to be: a
   near-white blue on accent. The section is white now, so the same colour
   was 1.4:1 on its own background. */
.cta-lede{margin:var(--sp-3) 0 0;max-width:46ch;font-size:1.0625rem;line-height:1.6;color:var(--grey)}
.cta-acts{display:flex;align-items:center;gap:1.6rem;flex-wrap:wrap;margin-top:var(--sp-5)}
.btn-onblue{background:var(--accent);color:var(--paper);border-color:var(--accent)}
.btn-onblue:hover{background:color-mix(in srgb,var(--paper) 88%,var(--accent));border-color:color-mix(in srgb,var(--paper) 88%,var(--accent));color:var(--accent)}
.cta-link{color:var(--accent);text-decoration:underline;text-underline-offset:4px;font-size:.95rem}
.cta-link:hover{text-decoration-thickness:2px}
/* ---------- footer ---------- */
.foot{background:var(--paper);border-top:1px solid var(--line);padding-block:clamp(40px,5vh,60px)}
.foot-row{display:flex;align-items:center;justify-content:space-between;gap:1.6rem;flex-wrap:wrap}
.foot .lockup{color:var(--navy)}
.foot-links a,.foot-copy{font-family:var(--mono);font-size:.625rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--grey-2);text-decoration:none}
.foot-links a:hover{color:var(--accent)}
.foot-copy{margin:0}
/* ---------- security facts ---------- */
.sec-facts li:first-child{border-left:0;padding-left:0}
.sec-facts i{flex:none;width:6px;height:6px;background:var(--grey)}
@media (max-width:900px){
  .sec-facts{grid-template-columns:1fr 1fr;row-gap:1.1rem}
  .sec-facts li:nth-child(3){border-left:0;padding-left:0}
}
@media (max-width:520px){
  .sec-facts{grid-template-columns:1fr;row-gap:1rem}
  .sec-facts li{border-left:0;padding:0}
}
/* ---------- AA on the banded ground ----------
   --grey is 4.67:1 on --paper but 4.41:1 on --paper-alt, which is under AA.
   Every grey that sits on a banded section steps down to --grey-deep. */
.alt .eyebrow,
.alt .sec-body p,
.alt .micro{color:var(--text-muted-alt)}
/* ---------- the FAQ page ---------- */
.page-faq{background:var(--paper)}
/* the bar has no film behind it here, so it never runs the transparent state */
/* the FAQ page is paper under the bar the whole way, so it never needs the
   probe: pin the tint light and let the contents follow */
.page-faq .nav{--nav-fg:var(--navy);--nav-tint:var(--paper);--nav-rule:var(--line)}
.page-faq main{padding-top:clamp(96px,12vh,140px)}
.faq-back{padding-block:0 var(--sec)}
.cta-link-quiet{font-family:var(--mono);font-size:.6875rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--grey);text-decoration:none}
.cta-link-quiet:hover{color:var(--navy);text-decoration:underline;text-underline-offset:4px}
/* the answers keep a readable measure now the page uses the full wrap */
.page-faq .faq{max-width:74ch}
/* ══════════════════════════════════════════════════════════════
   Apple-design pass
   ══════════════════════════════════════════════════════════════ */
/* §1 Response — feedback belongs on the press, not the release.
   Nothing on this page acknowledged pointer-down at all.

   These selectors are written at the same specificity as the :hover rules
   they have to beat — .nav .btn-primary:hover is (0,3,0), so a bare
   .btn:active at (0,2,0) loses no matter where it sits in the file. */
.btn:active,
.nav .btn-primary:active,
.nav.grounded .btn-primary:active,
.cover-content .btn-primary:active,
.btn-lift:active,
.btn-onblue:active,
.nav-links a:active,
.cta-link:active,
.cta-link-quiet:active,
summary:active{transform:scale(.97);transition:transform .1s ease-out}
.btn,.nav-links a,.cta-link,.cta-link-quiet,summary{-webkit-tap-highlight-color:transparent;touch-action:manipulation}
.start-cta:active{transform:scale(.97)}
/* §10 Tap targets — several sat between 11px and 41px tall */
.lockup{display:inline-flex;align-items:center;color:var(--navy);text-decoration:none;min-height:44px}
.foot-links a,.cta-link,.cta-link-quiet{display:inline-flex;align-items:center;min-height:44px}
.foot-links{list-style:none;display:flex;gap:.4rem 1.6rem;margin:0;padding:0;flex-wrap:wrap}
/* §12 Materials — the bar carried a 14px blur behind a fully opaque fill,
   which costs GPU and buys nothing. It is now a real translucent layer with
   content passing beneath it. Still navy, never light. */
/* ══════════ ROUND 8 · the bar is glass, not a tinted band ══════════
   What read as a bluish gradient at the top of the page was this: --nav-tint
   was --navy at 92%, masked by a vertical gradient, so the first 80px of
   every screen carried a navy wash fading downward.

   It is now actual glass. Over the hero the tint is fully transparent and
   only the blur is doing anything, so the footage runs under the bar almost
   untouched; once the page is grounded the tint comes up to a white 72% and
   a hairline appears underneath.

   The old comment here warned that backdrop-filter was the most expensive
   thing on the page. That was a MASKED backdrop-filter: the mask stopped the
   compositor caching the blurred region, so it re-blurred every frame. The
   mask is gone, so the blur can come back. Frame cost measured before and
   after, both engines. */
.nav{--nav-fg:var(--paper);--nav-glass:rgb(255 255 255 / 0);--nav-rule:var(--line-dark)}
.nav.grounded{--nav-fg:var(--navy);--nav-glass:rgb(255 255 255 / .62);--nav-rule:var(--line)}
/* ROUND 9: no rule under the bar, and the blur fades out downward.
   The blurred layer is masked by a vertical gradient, so it is at full
   strength along the very top of the screen and has reached nothing by its
   lower edge: there is no line where it stops because there is no edge. The
   layer extends 26px past the bar to give the gradient somewhere to finish.

   The old comment here warned that a masked backdrop-filter was the most
   expensive thing on the page, and it is true that the mask stops the
   compositor caching the blurred region. Measured again on this build before
   shipping it; the numbers are in the summary. */
.nav::before{
  content:"";position:absolute;inset:0;bottom:-26px;z-index:-1;pointer-events:none;
  background-color:var(--nav-glass);
  -webkit-backdrop-filter:blur(16px) saturate(150%);
  backdrop-filter:blur(16px) saturate(150%);
  -webkit-mask-image:linear-gradient(to bottom,#000 0%,rgba(0,0,0,.88) 44%,rgba(0,0,0,.42) 72%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0%,rgba(0,0,0,.88) 44%,rgba(0,0,0,.42) 72%,transparent 100%);
  transition:background-color var(--t-section) var(--ease)}
/* no blur available: a solid bar rather than nothing, or white nav text would
   sit on bare footage */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav{--nav-glass:rgb(11 12 13 / .42)}
  .nav.grounded{--nav-glass:rgb(255 255 255 / .96)}
  .nav::before{-webkit-mask-image:none;mask-image:none;bottom:0}
}
.nav{isolation:isolate}
/* an accent button on an accent ground disappeared - over the CTA the bar's
   button inverts to paper instead */
.nav.on-blue .btn-primary{background:var(--paper);color:var(--navy);border-color:var(--paper)}
.nav.on-blue .btn-primary:hover{background:color-mix(in srgb,var(--paper) 88%,var(--navy));
  border-color:color-mix(in srgb,var(--paper) 88%,var(--navy));color:var(--navy)}

.nav .logo{color:var(--nav-fg);transition:color var(--t-section) var(--ease)}
.nav-links a{color:color-mix(in srgb,var(--nav-fg) 78%,transparent)}
.nav-links a:hover{color:var(--nav-fg)}
.nav-links a::after{background:var(--nav-fg)}
.nav-toggle{color:var(--nav-fg)}
/* the sticky Operations header hid content behind a hard opaque edge */
/* §14 Reduced transparency — frostier means solid, not blurred */
@media (prefers-reduced-transparency: reduce){
  .nav::before{--nav-op:100%;
    -webkit-mask-image:none;mask-image:none;bottom:0}
  .ops-head{background:var(--paper);backdrop-filter:none}
  .cover-scrim{background:var(--navy);opacity:.86}
}
/* §14 Increased contrast — near-solid grounds and defined edges */
@media (prefers-contrast: more){
  :root{
    --grey:#4B5361;          /* 7.4:1 on paper rather than 4.67:1 */
    --text-muted-alt:#3A414D;
    --line:#B9BDC6;
    --line-dark:#5A6A88;
  }
  .nav::before{--nav-op:100%;
    -webkit-mask-image:none;mask-image:none;bottom:0}
  .ops-head{background:var(--paper);backdrop-filter:none}
  .btn{border-width:2px}
  .hiw-item h3,.step-row h3,.step-row p,.hiw-ico{opacity:1 !important}
  .sec-head,.hiw-sticky{border-color:var(--line)}
}
/* §14 Reduced motion already covered above; the press feedback goes too */
@media (prefers-reduced-motion: reduce){
  .btn:active,.nav-links a:active,.cta-link:active,
  .cta-link-quiet:active,summary:active,.start-cta:active{transform:none}
}
/* the question is a heading, but it still reads as a summary row */
.faq-q{display:inline;margin:0;font-family:var(--sans);font-size:1rem;font-weight:600;color:var(--navy)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* ---------- tonight: the receipt prints as you scroll ----------
   Scrolling down and paper feeding out of a printer are the same gesture,
   so the reader's scroll IS the feed. Every printed value is a pure
   function of scroll position, which is why it un-prints in reverse.

   NOTHING in this section is --accent. Four of the sixteen lines are
   mismatches and they are set identically to the other twelve: in a manual
   close the errors do not announce themselves, and that absence is the
   argument the section makes. Blue arrives in the next section, marking
   exactly these. Do not highlight them. */
/* do not override .wrap's width or the header loses the page gutter */
/* the paper keeps the page gutter on a phone rather than running edge to edge */
/* the header holds position for the whole pin */
/* torn top edge, drawn not imaged */
/* the printer lip: the paper emerges from under it */
/* the feed window: the strip travels up so the printing line stays in view */
/* Widths live on the colgroup. Under table-layout:fixed a width set on a
   td is ignored unless it is in the first row, and mine silently split the
   row into four equal columns instead — which clipped the notes. */
/* the note is the same weight, the same colour, the same everything */
/* a short window has to give the paper less room, or the header rides up
   under the bar and the final line falls off the foot */
/* Below this the pinned frame cannot hold the header, the paper and the
   final line at once, so the section falls back to the at-rest
   presentation — the same one reduced motion gets. */
/* short desktop windows: the pinned frames give up size before they clip */
/* tall windows: let the stepped sections actually use the height instead of
   sitting in the middle of an empty frame */
@media (min-height:860px){
  .step-row h3{font-size:1.35rem}
  .step-row p{font-size:1.1rem}
}
@media (min-height:940px){
  .step-row{--n-size:52px;gap:1.7rem}
  .step-n{font-size:1rem}
  .step-row h3{font-size:1.4rem}
  .step-row p{font-size:1.125rem}
  .start-head{font-size:clamp(2.4rem,4vw,3.4rem)}
}
@media (max-height:920px){
  .hiw-sticky .sec-head h2{font-size:clamp(1.9rem,3.2vw,2.6rem)}
  .hiw-row .ico{width:52px}
  .dgm{max-height:clamp(250px,35vh,380px)}
}
@media (max-height:840px){
  .dgm{max-height:clamp(220px,31vh,320px)}
  .hiw-sticky .sec-head h2{font-size:clamp(1.75rem,3vw,2.3rem)}
}
/* below 400px the docket type has to come down or the longest notes clip */
/* no pin, no printing: the whole receipt at rest, in normal flow */
/* ---------- generated icons ---------- */
.ico{display:block;height:auto;max-width:100%}
/* The generated icons. --seen is latched by scroll: once a step has been
   reached its icon stays, so earlier icons do not fade back out. 700ms,
   eased out, slow enough to notice. */
.hiw-ico{flex:none;display:block;line-height:0}
.hiw-ico img{display:block;width:var(--ico-w,40px);height:auto;
  opacity:var(--seen,0);transform:translateY(calc((1 - var(--seen,0)) * 10px));
  transition:opacity .7s cubic-bezier(.16,.84,.44,1),transform .7s cubic-bezier(.16,.84,.44,1)}
/* generated images do not share an optical weight, so they are equalised by
   eye rather than by setting one pixel height */
.hiw-item[data-i="0"] .hiw-ico img{--ico-w:44px}
.hiw-item[data-i="1"] .hiw-ico img{--ico-w:38px}
.hiw-item[data-i="2"] .hiw-ico img{--ico-w:40px}
.hiw-item[data-i="3"] .hiw-ico img{--ico-w:36px}
@media (prefers-reduced-motion:reduce){
  .hiw-ico img{opacity:1;transform:none;transition:none}
}
.ops-dgm .ico{width:auto;height:100%;max-height:100%;margin-inline:auto}
.step-body .ico{width:36px;margin-bottom:.5rem}
@media (max-width:760px){
  .hiw-row .ico{width:48px}
}
/* ══════════════════════════════════════════════════════════════
   Mobile: its own behaviour, not a squeezed desktop.
   Below 1024px or on any coarse pointer there is no pinning, no
   scrubbing and no snapping — the engines agree via the same query.
   ══════════════════════════════════════════════════════════════ */
@media (max-width:1023px), (pointer:coarse){

  /* pins become normal stacks */
  .hiw,.starter{height:auto}
  .hiw-frame,.starter-frame{position:static;height:auto;overflow:visible;padding-block:var(--sec-pad)}
  .hiw-item,.step-row{--lit:1;--snap:1}
  .hiw-ico *{stroke-dashoffset:0}
  .hiw-progress{display:none}
  .ops-head{position:static}

  /* rhythm and gutters */
  section{padding-block:clamp(56px,8vh,72px)}
  .wrap,.narrow{width:min(100% - 36px, 1280px)}
  :root{--gut:18px}

  /* every grid to one column; the instrument panel to 2x2 */
  .hiw-body,.ops-grid,.sec-facts{grid-template-columns:1fr;min-height:0}
  .hiw-list{border-right:0;padding-right:0}
  .hiw-static-ico{display:block}
  .hiw-stage{display:contents}
  .ops-col{border-left:0;padding-left:0;padding-right:0}

  /* type: nothing under 13px, body at least 16 */
  body{font-size:16px}
  .cover-content h1{font-size:clamp(2.25rem,9vw,2.75rem)}   /* 36-44px */
  .sec-head h2{font-size:clamp(1.75rem,7.5vw,2.125rem)}     /* 28-34px */
  /* Prose the reader works through to understand the product holds 16px on a
     phone. Eyebrows, micro labels and the legal line are chrome, not body
     copy, and sit on the 13px floor. */
  .cover-lede,.hiw-item p,.sec-claim p,.ops-body,.step-row p,
  .sec-body p{font-size:1rem;line-height:1.55}
  .eyebrow,.micro,.ops-systems li,.sec-facts li,
  .foot-links a,.foot-copy,.cta-link-quiet{font-size:.8125rem}  /* 13px floor */
  .foot-note{font-size:.8125rem}
  .dgm text,.dgm-sm text,svg text{font-size:13px}

  /* touch: nothing under 44px */
  .nav-links a,.foot-links a,.cta-link,.cta-link-quiet,summary,.btn{min-height:44px}
  .nav-links a{min-width:44px;justify-content:center}

  /* safe areas */
  .nav{padding-left:max(var(--gut),env(safe-area-inset-left));
       padding-right:max(var(--gut),env(safe-area-inset-right));
       padding-top:max(.75rem,env(safe-area-inset-top))}
}
/* the receipt on a phone: three columns, the reason as a chip on its own line */
@media (max-width:760px){
  /* ten rows, keeping all four flagged ones */
}
/* the sticky bottom CTA: after the hero, gone once the final CTA is on screen */
.mob-cta{position:fixed;left:0;right:0;bottom:0;z-index:45;display:none;align-items:center;justify-content:center;gap:.6rem;padding:.7rem var(--gut) calc(.7rem + env(safe-area-inset-bottom));background:var(--navy);border-top:1px solid var(--line-dark);transform:translateY(100%);transition:transform var(--t-section) var(--ease)}
.mob-cta .btn{width:100%;justify-content:center;min-height:48px}
.mob-cta.on{transform:none}
@media (max-width:860px), (pointer:coarse) and (max-width:860px){ .mob-cta{display:flex} }
@media (prefers-reduced-motion:reduce){ .mob-cta{transition:none} }
/* Tonight at rest: no pin, every step shown stacked and complete. */
@media (max-width:1023px), (pointer:coarse), (prefers-reduced-motion:reduce){
  .c-w{width:0}
}
/* ---------- the progress rail ----------
   One per stepped section, on the right edge, desktop only. It fills
   continuously across the whole pin and holds full until the section has
   gone; it never resets early. A dot per step, the live one in accent. */
/* Fixed to the viewport, not to the section box: positioning it against the
   1280px wrap put it outside the window on a 1440 screen. Only one stepped
   section is ever on screen, so the three rails never collide. */
/* ---------- Task 8: one primary, one secondary, everywhere ----------
   btn-primary is the action. btn-lift and btn-onblue are the SAME button
   recoloured for the ground it sits on, not separate styles. The secondary
   is always a quiet underlined link. */
@media (min-width:1024px) and (pointer:fine){ .nav .btn{font-size:.8125rem;min-height:40px} }
/* focus is visible on every ground, including the blue CTA and the dark ones */
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--r)}
.dark :focus-visible{outline-color:var(--accent)}
.cover :focus-visible{outline-color:var(--accent-lift)}
.cta-blue :focus-visible{outline-color:var(--accent)}
.nav:not(.grounded) :focus-visible{outline-color:var(--paper)}
/* nothing renders unstyled or invisible before the fonts land */
.page-faq,body{background:var(--paper)}

/* ══════════════════════════════════════════════════════════════
   THE DIFFERENCE — one desk, two states, one progress value
   Scroll alone writes --fcp; the wipe crosses in the first 45% of the pin.
   ══════════════════════════════════════════════════════════════ */
.fc{position:relative;height:var(--fc-scroll,205vh)}
.fc-frame{position:sticky;top:var(--nav-h,76px);height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;padding-block:var(--sec-pad);
  display:flex;flex-direction:column;justify-content:flex-start;overflow:hidden}
/* the frame's one child is the measure; it carries the column so the panel
   can take what is left over instead of being budgeted in rem */
.fc-frame > .wrap{display:flex;flex-direction:column;min-height:0;flex:1 1 auto}
.fc .sec-head h2{font-size:clamp(2rem,3.6vw,3.2rem)}

/* ---- the panel: two photographs of the same desk ---- */
/* The images are object-fit:cover, so capping the panel against the viewport
   crops the desk rather than letterboxing it - which is what keeps the whole
   section inside its frame on a short screen. */
.fc-panel{position:relative;aspect-ratio:2.33/1;overflow:hidden;background:var(--paper-alt);
  flex:0 1 auto;min-height:min(300px,26vh);--fcp:0}
.fc-layer{position:absolute;inset:0}
/* The panel is far wider than the photograph's 2.34:1, so cover crops
   vertically - and centred it cropped away the top quarter, which is exactly
   where the laptop screen is. Anchored near the top instead, so the screen
   and the Tyll view on it stay in frame at every panel height. */
.fc-shot{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 8%}
/* the after is the clipped one; both are the same image box, so every pixel
   of the desk sits at the same place in each */
.fc-after{clip-path:inset(0 0 0 calc((1 - var(--fcp)) * 100%))}

.fc-cap{position:absolute;top:1rem;z-index:3;margin:0;font-family:var(--mono);
  font-size:.8125rem;font-weight:500;letter-spacing:.12em;text-transform:uppercase;
  padding:.45rem .8rem;background:color-mix(in srgb,var(--navy) 82%,transparent);
  color:var(--paper)}
/* Never both at once. Each caption belongs to one half, so it is shown only
   while that half is the one being read: BY HAND until the wipe passes the
   middle, WITH TYLL after it. They cross at --fcp .5 with no overlap. */
.fc-cap-l{left:1rem;opacity:clamp(0,calc((.5 - var(--fcp)) * 12),1)}
.fc-cap-r{right:1rem;opacity:clamp(0,calc((var(--fcp) - .5) * 12),1)}

.fc-rule{position:absolute;top:0;bottom:0;width:1px;background:var(--accent);z-index:4;
  left:calc((1 - var(--fcp)) * 100%);pointer-events:none}

/* ---- the statistics: number over label, no arrows ---- */
.fc-stats-lede{margin:var(--sp-4) 0 var(--sp-2);font-size:.9375rem;color:var(--grey)}
.fc-stats{display:grid;grid-template-columns:repeat(4,1fr)}
.fc-stats-note{margin:var(--sp-2) 0 0;font-size:.8125rem;line-height:1.5;color:var(--grey);max-width:52ch}
@media (max-height:860px){
  .fc-stats-lede{margin-block:var(--sp-2) .4rem;font-size:.875rem}
  .fc-stats-note{font-size:.75rem;margin-top:.4rem;line-height:1.35;max-width:68ch}
  /* the German footnote is two lines where the English is one */
  .sec-claim h3{font-size:1rem}
  .sec-claim p,.sec-more{font-size:.875rem;line-height:1.45}
  .sec-claims{gap:var(--sp-2)}
  .sec-facts{margin-top:var(--sp-3)}
  .fc-side{font-size:.625rem}
  .fc-num{font-size:clamp(1.5rem,2.7vw,2.2rem)}
}
.fc-stat{padding:0 var(--sp-3);border-left:1px solid var(--rule,var(--line))}
.fc-stat:first-child{border-left:0;padding-left:0}
.fc-num{margin:0 0 .5rem;font-family:var(--sans);font-weight:600;line-height:1;
  font-size:clamp(1.8rem,3.3vw,3rem);letter-spacing:-.025em;--nt:1;
  /* --nt is the figure's own share of the wipe, written per frame by site.js:
     the colour arrives with the last digit rather than switching after it */
  color:color-mix(in srgb,var(--accent) calc(var(--nt) * 100%),var(--grey));
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.fc-lab{margin:0;display:flex;align-items:center;gap:.5rem;font-family:var(--mono);
  font-size:.625rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--grey)}
.fc-lab i{flex:none;width:5px;height:5px;background:var(--accent)}
.fc-lab span{text-transform:none;letter-spacing:.04em;color:var(--grey)}
/* the figure is meaningless without knowing which half it belongs to, and a
   colour cannot say it. The word does, and it changes with the wipe. */
.fc-side{display:block;margin-left:calc(5px + .5rem);text-transform:none;
  letter-spacing:.04em;color:var(--grey);font-size:.6875rem}
.fc-lab{flex-wrap:wrap}

@media (max-height:900px){
  .fc-stat{padding-inline:var(--sp-2)}
  .fc .sec-head h2{font-size:clamp(1.8rem,3.1vw,2.6rem)}
  .fc-stats{margin-top:var(--sp-3)}
  .fc-num{font-size:clamp(1.6rem,2.9vw,2.5rem)}
}

/* ---- unpinned: the two states stack, both complete ----
   Both captions used to fall into the flow one after the other, so BY HAND and
   WITH TYLL ended up side by side ABOVE the first image, labelling
   neither. Each one sits on its own frame now.
   And a 2.34:1 crop on a 354px column puts the laptop at about 90px across,
   which is why the panel read as not fitting: the subject was a tenth of the
   frame. Each half is cropped to 4:3 on its own subject instead - the desk
   centred, the laptop pulled in from the right. */
/* REDUCED MOTION stacks the two states. A PHONE does not: the wipe is the
   whole argument of the section, so it still runs, just over a shorter pin. */
@media (prefers-reduced-motion:reduce){
  .fc{height:auto}
  .fc-frame{position:static;height:auto;display:block;overflow:visible;
    padding-block:var(--sec-pad)}
  /* the captions are SIBLINGS of the layers, not children, so absolute
     positioning put both of them on the first frame. Each is given its own
     grid cell instead, stacked on the layer it labels. */
  .fc-panel{aspect-ratio:auto;overflow:visible;--fcp:1;min-height:0;
    display:grid;grid-template-rows:auto auto;gap:var(--sp-3)}
  .fc-layer{position:relative;inset:auto}
  .fc-before{grid-area:1/1}
  .fc-cap-l{grid-area:1/1}
  .fc-after{grid-area:2/1;clip-path:none;margin-top:0}
  .fc-cap-r{grid-area:2/1}
  .fc-shot{aspect-ratio:4/3;height:auto}
  .fc-before .fc-shot{object-position:50% 46%}
  .fc-after .fc-shot{object-position:74% 26%}
  .fc-cap{position:relative;top:auto;left:auto;right:auto;z-index:3;
    align-self:start;justify-self:start;margin:.6rem;opacity:1 !important}
  .fc-rule{display:none}
  .fc-stats{grid-template-columns:1fr 1fr;row-gap:var(--sp-3)}
  .fc-stat:nth-child(odd){border-left:0;padding-left:0}
}


/* ---------- phones: the 13px floor and 44px targets ---------- */
@media (max-width:860px){
  /* nothing below 13px anywhere */
  .fc-cap,.fc-lab,.fc-lab span,.fc-side,.foot-h,.ops-win,.ops-win li{font-size:.8125rem}
  .ops-systems li,.ops-systems-end{font-size:.8125rem}
  /* every footer link is a real target */
  .foot-col a,.foot-legal a{min-height:44px;min-width:44px;display:inline-flex;align-items:center}
  .foot-legal{gap:var(--sp-2)}
  .foot-col ul{gap:0}
  /* the drag handle has to be reachable with a thumb */
}

/* How it works has to fit three desktop heights; this has failed four times,
   so the furniture gives before the type does. */
@media (max-height:960px){
  .hiw-sticky .sec-head h2{font-size:clamp(1.7rem,2.9vw,2.3rem)}
}
@media (max-height:850px){
  .hiw-sticky .sec-head h2{font-size:clamp(1.5rem,2.6vw,2rem)}
}

/* Getting started has to fit three desktop heights too */
@media (max-height:960px){
  .start-head{margin-bottom:var(--sp-4);font-size:clamp(1.7rem,3vw,2.4rem)}
  .step-row h3{font-size:1.15rem}
  .step-row p{font-size:1rem;line-height:1.5}
  .start-cta{margin-top:var(--sp-2)}
}
@media (max-height:850px){
  .start-head{margin-bottom:var(--sp-3);font-size:clamp(1.5rem,2.7vw,2rem)}
  .step-row{--n-size:40px;padding-bottom:clamp(1.1rem,2.6vh,1.8rem);gap:1.1rem}
  .step-n{font-size:.875rem}
  .step-row h3{font-size:1.05rem;margin:.3rem 0 .35rem}
  .step-row p{font-size:.9375rem;line-height:1.45}
}

/* ---------- the footer ---------- */
.foot-cols{display:grid;grid-template-columns:2.2fr 1fr 1fr 1fr;gap:var(--sp-5);
  padding-block:var(--sp-5)}
.foot-brand .logo{height:34px;width:auto;display:block;color:var(--navy)}
.foot-brand p{margin:var(--sp-3) 0 0;max-width:40ch;font-size:.9375rem;line-height:1.6;color:var(--grey)}
.foot-h{margin:0 0 .9rem;font-family:var(--mono);font-size:.625rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--grey-2)}
.foot-col ul{list-style:none;margin:0;padding:0;display:grid;gap:.55rem}
.foot-col a{font-size:.9375rem;color:var(--grey);text-decoration:none;
  transition:color var(--t-section) var(--ease)}
.foot-col a:hover{color:var(--accent)}
.foot-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  flex-wrap:wrap;padding-block:var(--sp-3);border-top:1px solid var(--line-dark)}
.foot-bar p,.foot-legal a{font-size:.8125rem;color:var(--grey-2)}
.foot-bar p{margin:0}
.foot-legal{list-style:none;display:flex;gap:var(--sp-3);margin:0;padding:0}
.foot-legal a{text-decoration:none;transition:color var(--t-section) var(--ease)}
.foot-legal a:hover{color:var(--accent)}
@media (max-width:860px){
  .foot-cols{grid-template-columns:1fr;gap:var(--sp-4)}
  .foot-bar{flex-direction:column;align-items:flex-start}
  .foot-col a,.foot-legal a{min-height:44px;min-width:44px;display:inline-flex;align-items:center}
  .foot-col ul{gap:0}
  .foot-legal{gap:var(--sp-2)}
}

/* ══════════════════════════════════════════════════════════════
   SECTION FRAMES
   Each section carries its own hairline rectangle, inset 40px and
   sized to that section's own height. Consecutive sections share a
   boundary: every section draws left, right and bottom, and only the
   first draws a top, so the rules read continuous with no doubling.

   Drawn as BACKGROUND-IMAGE layers, not as a box that insets the
   section. background-image sits over background-color and changes
   nothing about the element's geometry, so every background still
   fills the viewport edge to edge - which is what went wrong when
   this was built as a frame in Round 4.
   ══════════════════════════════════════════════════════════════ */
:root{--grid-inset:40px}
main > section{
  position:relative;
  --rule:var(--line);
  --gi:var(--grid-inset);
  background-image:
    linear-gradient(var(--rule),var(--rule)),    /* left  */
    linear-gradient(var(--rule),var(--rule)),    /* right */
    linear-gradient(var(--rule),var(--rule));    /* bottom */
  background-size:1px 100%,1px 100%,calc(100% - var(--gi) * 2) 1px;
  background-position:
    left var(--gi) top,
    right var(--gi) top,
    left var(--gi) bottom;
  background-repeat:no-repeat;
}
/* only the first section draws a top rule; the rest inherit the one above */
main > section:first-of-type{
  background-image:
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule));
  background-size:1px 100%,1px 100%,calc(100% - var(--gi) * 2) 1px,calc(100% - var(--gi) * 2) 1px;
  background-position:left var(--gi) top,right var(--gi) top,
    left var(--gi) bottom,left var(--gi) top;
}
main > section.cta-blue{--rule:var(--line)}
/* The hero is excepted: no rectangle, and therefore no side rules painted on
   its sticky stage either. The stack of rectangles starts at the section
   BELOW it, which is why that one draws the top rule the hero used to. */
main > section.cover.cover{background-image:none}
/* THE HERO HANDOFF. A 100vh sticky stage in a 200vh container pins for one
   viewport and then spends a SECOND viewport sliding out of frame. That exit
   was the in-between state: 540px of scroll in which neither the hero nor the
   problem section owned the screen. It is not slack in the film or padding on
   the next section - it is the sticky mechanic itself, and the only way to
   remove it without removing the pin (and with it the film scrub) is to stop
   the next section WAITING for the hero to leave. It is pulled up over it
   instead, so the hero is covered rather than pushed, and that viewport of
   scroll is reclaimed rather than spent. The section needs its own opaque
   ground and a stacking context to cover anything. */
/* The hero's exit is reclaimed: its stage spends a whole viewport sliding out
   after it releases, and nothing happens in it. The problem section is pulled
   up by that viewport and rises over the still-pinned hero instead of waiting
   below it. Only the hero - cascading this through every pin nests the
   overlaps and the last section ends up inside the one before it. */
main > section[data-pin] + section{position:relative;background-color:var(--paper)}
/* Wherever the hero is actually pinned - which is now the phone too - the
   section below it is pulled up over the exit. Without this the stage slides
   away and the cover's OWN navy ground is what fills the space behind it: a
   growing black band between the hero and the problem section, up to a full
   844px of it at 390x844. Not a stray background colour, the hero's own box
   showing through where its sticky stage used to be. */
/* Round 7: the hero no longer pins, so the next section no longer
   has to be pulled up underneath it. */
/* except where the film gave up and the hero went back to a plain block */

@media (prefers-reduced-motion:reduce){

}
main > section:first-of-type{background-image:
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule));
  background-size:1px 100%,1px 100%,calc(100% - var(--gi) * 2) 1px;
  background-position:left var(--gi) top,right var(--gi) top,left var(--gi) bottom}
main > section.cover + section{
  background-image:
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule)),
    linear-gradient(var(--rule),var(--rule));
  background-size:1px 100%,1px 100%,calc(100% - var(--gi) * 2) 1px,calc(100% - var(--gi) * 2) 1px;
  background-position:left var(--gi) top,right var(--gi) top,
    left var(--gi) bottom,left var(--gi) top}

/* content never touches the frame */
main > section > .wrap,
main > section > .fc > .fc-frame > .wrap,
main > section > .sec-pin > .sec-frame > .wrap,
main > section > .wrap > .ops > .ops-frame > .wrap{
  padding-inline:var(--sp-4);
}


.cover-stage > *{position:relative;z-index:1}

/* a square that tracks scroll is still motion */

@media (max-width:1100px){
  main > section,main > section:first-of-type,
  main > section.cover + section{background-image:none}
  main > section > .wrap,
  main > section > .fc > .fc-frame > .wrap,
  main > section > .sec-pin > .sec-frame > .wrap,
  main > section > .wrap > .ops > .ops-frame > .wrap{padding-inline:0}
}

/* ---------- phones: the floors, applied last so nothing outranks them ----- */
@media (max-width:860px){
  /* body copy holds 16px */
  .hiw-item p,.step-row p,.sec-claim p,.sec-more,.ops-body,
  .cover-lede,.sec-body p,.foot-brand p{font-size:1rem;line-height:1.55}
  /* nothing below 13px anywhere */
  .foot-h,.eyebrow,.micro,.fc-cap,.fc-lab,.ops-systems li,
  .ops-systems-end,.foot-copy,.foot-legal a{font-size:.8125rem}
}

/* ══════════════════════════════════════════════════════════════
   OPERATIONS — the four diagrams, driven by scroll
   Each tile carries --t, 0 to 1, written by site.js from that tile's own
   share of the pin. Every row inside derives its OWN progress from --t with
   a clamp(), so the eight checks resolve one after another rather than
   together, and nothing needs a timer or a class per row. A pure function
   of --t, so scrolling back runs it backwards.
   Everything is PRESENT from the start in --line / --grey. What changes is
   which parts have resolved, never whether they exist.
   ══════════════════════════════════════════════════════════════ */
.dgm text{font-family:var(--mono);fill:var(--grey)}
/* --accent #39558C is right as a UI colour and wrong inside a pale diagram
   panel at 7-10px: it reads as dark grey. --dgm-blue is brighter and used
   ONLY in here; everything outside the panels stays --accent. */
.dgm{--t:0;--dgm-blue:#2F6BE0}

/* ---- tile 1: the close running down its list ---- */
/* r = this row's own 0..1, reached in sequence top to bottom */
.dgm-close .ck{--r:clamp(0,calc(var(--t) * 9 - var(--i)),1)}
.dgm-close .ck-bar{fill:var(--line)}
.dgm-close .ck-fill{fill:var(--grey);transform:scaleX(var(--r));transform-origin:10px 0}
.dgm-close .ck-tick{fill:none;stroke:var(--grey);stroke-width:1.6;stroke-linecap:round;
  stroke-linejoin:round;opacity:var(--r)}
/* the two that do not clear stay flagged and stay accent */
.dgm-close .ck-flag{fill:var(--dgm-blue);opacity:var(--r)}
.dgm-close .ck:has(.ck-flag) .ck-fill{fill:var(--dgm-blue)}
.dgm-close .run{fill:none;stroke:var(--grey);stroke-width:1}
.dgm-close .run-t,.dgm-close .ct{font-size:7px;letter-spacing:1px}
.dgm-close .ct{fill:var(--navy);font-size:8px;letter-spacing:.5px}

/* ---- tile 2: most recede, a few come forward with their value ---- */
.dgm-exc .ex{--r:clamp(0,calc(var(--t) * 2.4 - var(--i) * .12),1)}
.dgm-exc .ex-bar{fill:var(--line)}
/* the quiet ones fade back rather than disappear */
.dgm-exc .ex:not(.hot) .ex-bar{opacity:calc(1 - var(--r) * .55)}
.dgm-exc .hot .ex-bar{fill:color-mix(in srgb,var(--dgm-blue) calc(var(--r) * 100%),var(--line));
  transform:translateX(calc(var(--r) * -4px))}
.dgm-exc .ex-v{font-size:8px;fill:var(--navy);opacity:var(--r)}

/* ---- tile 3: seven nights growing left to right ---- */
.dgm-trend .bar{--r:clamp(0,calc(var(--t) * 2.6 - var(--i) * .3),1);
  fill:var(--line);transform:scaleY(var(--r));transform-origin:0 96px}
.dgm-trend .bar-hot{fill:color-mix(in srgb,var(--dgm-blue) calc(var(--r) * 100%),var(--line))}
.dgm-trend .base{stroke:var(--grey);stroke-width:1}
.dgm-trend .dl{font-size:6px}
.dgm-trend .pc{font-size:9px;fill:var(--navy);letter-spacing:.3px}

/* ---- tile 4: folios posting, then the drawer resolving ---- */
.dgm-room .fo{--r:clamp(0,calc(var(--t) * 7 - var(--i) * 1.1),1)}
.dgm-room .fo-bar{fill:color-mix(in srgb,var(--grey) calc(var(--r) * 100%),var(--line))}
.dgm-room .fo-tick{fill:none;stroke:var(--grey);stroke-width:1.6;stroke-linecap:round;
  stroke-linejoin:round;opacity:var(--r)}
.dgm-room .fo-bad .fo-bar{fill:color-mix(in srgb,var(--dgm-blue) calc(var(--r) * 100%),var(--line))}
.dgm-room .fo-x{fill:var(--dgm-blue);opacity:var(--r)}
.dgm-room .split{stroke:var(--line);stroke-width:1}
.dgm-room .dw-l{font-size:5.5px;letter-spacing:1px}
.dgm-room .dw-a,.dgm-room .dw-b{font-size:10px;fill:var(--navy);
  opacity:clamp(0,calc(var(--t) * 3 - 1.4),1)}
.dgm-room .dw-v{font-size:10px;fill:var(--dgm-blue);
  opacity:clamp(0,calc(var(--t) * 3 - 2),1)}

/* ---- the window the trend covers. Not functional; it is there because the
       product tracks the same pattern well past seven nights and a chart with
       no window named is a chart that overstates what it is. ---- */
.ops-win{display:flex;gap:.9rem;margin:0 0 var(--sp-2);font-family:var(--mono);
  font-size:.625rem;font-weight:500;letter-spacing:.12em;text-transform:uppercase;
  color:var(--grey);list-style:none;padding:0}
/* --grey on the panel's own ground is 4.4:1, just under AA; mixing a
   little navy in clears it without changing the register */
.ops-win li{color:color-mix(in srgb,var(--grey) 82%,var(--navy))}
.ops-win li.on{color:var(--navy);box-shadow:inset 0 -1px 0 var(--accent)}

/* On a short window the 1fr panel track is what gets squeezed, and below about
   820px it collapsed to 19px. The column gives up type size before the panel
   gives up being legible. */
@media (max-height:860px){
  /* the section head was taking 316px of a 590px frame and the panels were
     left with 35. It gives up size before the diagrams do. */
  #operations .sec-head{padding-block:var(--sp-2)}
  #operations .sec-head h2{font-size:clamp(1.5rem,2.5vw,2rem)}
  #operations .sec-body p{font-size:.875rem;line-height:1.5}
  #operations .ops-systems{margin-top:.5rem}
  .ops-grid{margin-top:var(--sp-2)}
  .ops-tt{margin-bottom:var(--sp-2)}
  .ops-col h3{font-size:.9375rem}
  .ops-body{font-size:.8125rem;line-height:1.45;margin-top:.7rem}
  .ops-ico{height:26px;margin-bottom:.35rem}
  .ops-ico img{width:26px;height:26px}
}

/* flat: every tile in its finished state, nothing moving */
@media (max-width:860px), (prefers-reduced-motion:reduce){
  .dgm{--t:1 !important}
  .ops-col{transform:none !important}
}

/* ══════════════════════════════════════════════════════════════
   HOW IT WORKS — the four-icon panel that replaced the film
   Four layers in one stacking context, opacity and scale written per frame
   by site.js from scroll position. --paper ground, not navy: these are flat
   navy icons on off-white, the same family as the Operations tiles.
   ══════════════════════════════════════════════════════════════ */
.hiw-stage{position:relative;overflow:hidden;background-color:var(--paper);
  margin-left:var(--sp-4);display:grid;place-items:center;
  border:1px solid var(--line);min-height:0}
.hiw-icon{grid-area:1/1;place-self:stretch;display:grid;place-items:center;
  padding:clamp(1.5rem,4vw,4rem);opacity:0;will-change:opacity,transform}
.hiw-icon:first-child{opacity:1}
.hiw-icon img{width:100%;height:100%;max-width:min(78%,420px);object-fit:contain;display:block}
/* the files are not here yet, and the panel says so rather than sitting blank */
.hiw-icon.awaiting img{display:none}
.hiw-icon.awaiting::after{content:"AWAITING ASSET " attr(data-step);
  font-family:var(--mono);font-size:.6875rem;letter-spacing:.12em;color:var(--accent);
  border:1px dashed var(--accent);padding:.5rem .75rem}
.hiw-icon.awaiting{align-content:center}
@media (max-width:1023px), (pointer:coarse), (prefers-reduced-motion:reduce){
  /* flat: the last state, still */
  .hiw-stage{margin-left:0;min-height:40vh}
  .hiw-icon{opacity:0 !important;transform:none !important}
  .hiw-icon:last-child{opacity:1 !important}
}

/* ══════════════════════════════════════════════════════════════
   HOW IT WORKS — one scale that grows with the viewport
   The section sat at a fixed size inside an ever-larger frame: at 2560 it was
   a small block in a lot of white. --hiw-s is a single shared scale, and type,
   icons, panel padding and row spacing are all expressed against it, so the
   whole section grows together instead of each value being tuned apart.
   ══════════════════════════════════════════════════════════════ */
/* --hiw-s is a LENGTH, not a ratio: a unitless clamp cannot take a vw term,
   which is why the first attempt resolved to nothing. Everything in the
   section is a multiple of it, so one value grows the whole thing. */
.hiw{--hiw-s:clamp(14px, min(10px + 0.62vw, 4px + 1.28vh), 26px)}
.hiw-item h3{font-size:calc(1.3 * var(--hiw-s))}
.hiw-item p{font-size:calc(1.06 * var(--hiw-s));line-height:1.55}
.hiw-item{padding-block:calc(.72 * var(--hiw-s))}
/* --ico-k is a NUMBER, not a length: calc(length / 16 * length) is px squared,
   which is invalid, so width was dropped entirely and the icons rendered at
   their intrinsic 512px - visible as three huge dockets hanging over the
   section above. */
.hiw-ico{width:auto;height:auto}
.hiw-ico img{width:calc(var(--ico-k,2.1) * var(--hiw-s));height:auto}
.hiw-item[data-i="0"] .hiw-ico img{--ico-k:2.3}
.hiw-item[data-i="1"] .hiw-ico img{--ico-k:2.0}
.hiw-item[data-i="2"] .hiw-ico img{--ico-k:2.1}
.hiw-item[data-i="3"] .hiw-ico img{--ico-k:1.9}
/* the icon sat flush against its title and read as an accident */
.hiw-row{gap:var(--sp-3)}
.hiw-icon{padding:calc(2.4 * var(--hiw-s))}
.hiw-icon img{max-width:min(82%,calc(26 * var(--hiw-s)))}
.hiw-sticky .sec-head h2{font-size:calc(2.6 * var(--hiw-s))}
.hiw-sticky .sec-head .sec-body p{font-size:calc(1.06 * var(--hiw-s))}
.hiw-stage{min-height:calc(21 * var(--hiw-s))}

/* ══════════════════════════════════════════════════════════════
   THE PROBLEM PANEL — one treatment over both halves
   The after now carries the real Trends view composited onto the laptop
   screen, perspective-matched to the glass. A sharp screenshot pasted into a
   photograph reads as a sticker, so the SAME dot screen and the same
   desaturation run over both halves and over the screen with them - which is
   what makes the wipe read as one object rather than two pictures and a
   rectangle. The before is pushed further down and cooler, so it reads as the
   lamp-lit one.
   ══════════════════════════════════════════════════════════════ */
.fc-panel .fc-layer::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:radial-gradient(var(--navy) 38%, transparent 39%);
  background-size:3px 3px;
  opacity:.3;mix-blend-mode:multiply}
.fc-panel .fc-shot{filter:saturate(.55) contrast(1.06)}
.fc-panel .fc-before .fc-shot{filter:saturate(.35) contrast(1.1) brightness(.72)}
@media (prefers-reduced-motion:reduce){ .fc-panel .fc-layer::after{opacity:.22} }

/* ---------- security: a stop of its own ----------
   It was the one section between two pins that simply scrolled past. It holds
   now, so the three claims are read rather than passed. */
.sec-pin{position:relative;height:var(--sec-scroll,175vh)}
.sec-frame{position:sticky;top:var(--nav-h,76px);height:calc(100svh - var(--nav-h,76px));
  box-sizing:border-box;padding-block:var(--sec-pad);display:flex;
  flex-direction:column;justify-content:flex-start;overflow:hidden}
@media (max-width:1023px), (pointer:coarse), (prefers-reduced-motion:reduce){
  .sec-pin{height:auto}
  .sec-frame{position:static;height:auto;display:block;overflow:visible;
    padding-block:var(--sec-pad)}
}

/* ══════════════════════════════════════════════════════════════
   LANGUAGE TOGGLE
   EN | DE, in the bar's own colours so it reads as chrome rather than as an
   action. It is a real button, not a link: nothing navigates.
   ══════════════════════════════════════════════════════════════ */
.lang{display:inline-flex;align-items:center;gap:.5rem;margin:0;
  font-family:var(--mono);font-size:.6875rem;font-weight:500;letter-spacing:.14em}
.lang a{display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;min-width:28px;text-decoration:none;
  color:var(--nav-fg);opacity:.45;transition:opacity var(--t-section) var(--ease)}
.lang a.lang-on{opacity:1}
.lang a:hover{opacity:.8}
.lang a.lang-on:hover{opacity:1}
.lang i{display:block;width:1px;height:11px;background:var(--nav-fg);opacity:.3}
@media (max-width:1023px){ .lang{margin-right:.4rem} }

/* ---------- the 13px floor, last word ----------
   These have to sit at the end of the file: the phone floor block above is
   outranked on source order by the language toggle (declared later) and by the
   max-height:860px reductions, which a 390x844 phone also matches. */
@media (max-width:860px){
  .lang,.lang a,.fc-stats-note,.fc-stats-lede,.foot-h{color:var(--grey-2);font-size:.8125rem}
}

/* ---------- compositing hints ----------
   Only the elements the scroll loop actually writes to every frame. Over-
   applying will-change is worse than omitting it: each one costs a layer. */
.fc-after,.fc-rule{will-change:clip-path}
.fc-panel{will-change:contents}
.ops-col{will-change:transform}
.hiw-icon{will-change:opacity,transform}
.step-row::after{will-change:transform}
@media (prefers-reduced-motion:reduce){
  .fc-after,.fc-rule,.fc-panel,.ops-col,.hiw-icon,.step-row::after{will-change:auto}
}

/* ══════════════════════════════════════════════════════════════
   THE HERO EXIT — play out, flash, land
   A cut, not a fade. One fixed element, one property: opacity, written from
   scroll position. Nothing here uses a timer and nothing moves the scroll.
   The hero paints ABOVE the problem section for the whole pin (see the
   z-index pair below), so the problem is never half-visible behind it; at the
   moment the flash is solid the stage is dropped, and when the flash lifts the
   problem is already filling the viewport.
   ══════════════════════════════════════════════════════════════ */
.flash{position:fixed;inset:0;z-index:60;pointer-events:none;
  background:var(--paper);opacity:var(--flash,0);will-change:opacity}
/* the hero sits over the section pulled up beneath it until it is cut away */
main > section.cover{position:relative;z-index:2}
main > section.cover + section{z-index:1}
.cover-stage{opacity:calc(1 - var(--hero-out,0));
  visibility:var(--hero-vis,visible)}
@media (prefers-reduced-motion:reduce){
  /* no flash, straight cut */
  .flash{display:none}
  .cover-stage{opacity:1;visibility:visible}
}

/* ══════════════════════════════════════════════════════════════
   HOW IT WORKS — the progress rail
   A track down the left of the step list with a fill that grows as the reader
   moves through the four steps, and a tick per step that fills as its step is
   reached. The fill is scaleY on a transform, never height, so it composites
   rather than relaying out. The rail sits in the gutter on a negative margin,
   so the step text keeps the left edge it shares with the eyebrow above it.
   ══════════════════════════════════════════════════════════════ */
.hiw-list{padding-left:calc(var(--sp-3) + 3px);margin-left:calc((var(--sp-3) + 3px) * -1)}
.hiw-rail{position:absolute;left:0;top:0;bottom:0;width:3px;pointer-events:none}
.hiw-rail::before{content:"";position:absolute;inset:0;left:1px;width:1px;background:var(--line)}
.hiw-rail-fill{position:absolute;inset:0;left:1px;width:1px;background:var(--accent);
  transform:scaleY(var(--hiw-fill,0));transform-origin:top;will-change:transform}
/* one tick per step, at its own position down the track */
.hiw-rail b{position:absolute;left:0;width:3px;height:3px;border-radius:50%;
  top:calc(var(--k) / 3 * 100%);margin-top:-1px;
  background:var(--line);transition:background var(--t-section) var(--ease)}
.hiw-rail b:nth-child(2){background:color-mix(in srgb,var(--accent) calc(var(--s0,0) * 100%),var(--line))}
.hiw-rail b:nth-child(3){background:color-mix(in srgb,var(--accent) calc(var(--s1,0) * 100%),var(--line))}
.hiw-rail b:nth-child(4){background:color-mix(in srgb,var(--accent) calc(var(--s2,0) * 100%),var(--line))}
.hiw-rail b:nth-child(5){background:color-mix(in srgb,var(--accent) calc(var(--s3,0) * 100%),var(--line))}
@media (max-width:1023px), (pointer:coarse), (prefers-reduced-motion:reduce){
  .hiw-list{padding-left:0;margin-left:0}
  .hiw-rail{display:none}
}

/* ══════════════════════════════════════════════════════════════
   SECURITY — the three claims draw as you scroll through them
   They used to fire from an IntersectionObserver, which sounded right and was
   not: the claims sit inside a sticky frame in a pinned section, so the
   observer tripped 400px BEFORE the section arrived and every mark was fully
   drawn by the time anyone could see it. They are driven by the section's own
   scroll progress now, like everything else on the page - each claim has its
   own --d, 0 to 1, and the mark's dash offset, the rise and the fade are all
   read off it. A pure function of scroll, so it reverses.
   ══════════════════════════════════════════════════════════════ */
.sec-claim{--d:1;
  opacity:calc(.12 + var(--d) * .88);
  transform:translateY(calc((1 - var(--d)) * 14px))}
.sec-mark path,.sec-mark rect{fill:none;stroke:var(--accent);stroke-width:1.5;
  stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke;
  stroke-dashoffset:calc(var(--len,0) * (1 - var(--d,1)))}
@media (prefers-reduced-motion:reduce){
  /* drawn and still */
  .sec-claim{--d:1 !important;opacity:1;transform:none}
}

/* one paragraph for the phone, one for everything wider */
.only-phone{display:none}
@media (max-width:860px){ .only-wide{display:none} .only-phone{display:block} }


/* ---- the problem section on a phone ----
   The wipe is the whole argument of this section, so it still runs. What goes
   is the PIN: a sticky frame has to hold head, panel, five figures and two
   notes inside one viewport, and at 390x844 that content measures 952px
   against 664px of frame - it cannot be made to fit without dropping type
   under the 13px floor. Unpinned, the section is a normal tall block, the
   wipe is driven by its scroll progress exactly as before, and nothing clips.
   Reduced motion still stacks the two states; this is the motion path. */
@media (max-width:860px) and (prefers-reduced-motion:no-preference){
  .fc{height:auto}
  .fc-frame{position:static;height:auto;display:block;overflow:visible;
    padding-block:var(--sec-pad)}
  .fc-frame > .wrap{display:block}
  /* the two frames stay stacked ON each other so the wipe has something to
     wipe; only the layout around them changes */
  .fc-panel{position:relative;aspect-ratio:3/2;overflow:hidden;min-height:0;
    max-height:none;flex:none;margin-block:var(--sp-3)}
  .fc-layer{position:absolute;inset:0}
  .fc-shot{height:100%;aspect-ratio:auto;object-position:62% 32%}
  .fc-before .fc-shot{object-position:54% 46%}
  .fc-after{clip-path:inset(0 0 0 calc((1 - var(--fcp)) * 100%));margin-top:0}
  .fc-rule{display:block}
  .fc-cap{position:absolute;top:.6rem;margin:0}
  .fc-cap-l{left:.6rem;right:auto}
  .fc-cap-r{right:.6rem;left:auto}
  .fc-stats{grid-template-columns:1fr 1fr;row-gap:var(--sp-3);column-gap:var(--sp-2)}
  .fc-stat:nth-child(odd){border-left:0;padding-left:0}
  .fc-stat{padding-inline:0 var(--sp-2)}
}

/* ══════════════════════════════════════════════════════════════
   HOW IT WORKS — an accordion on a phone
   Four steps open at once is too much on a small screen. Each row is a real
   <button> with aria-expanded and aria-controls, so it is keyboard and screen
   reader operable; tapping one opens it and closes the others. The first is
   open so the section is never empty. The body animates on transform and
   opacity with its height set once on expand, never tweened.
   Desktop is untouched: the button is a plain block there.
   ══════════════════════════════════════════════════════════════ */
.hiw-head{display:block;width:100%;text-align:left;background:none;border:0;
  padding:0;font:inherit;color:inherit}
@media (min-width:861px){ .hiw-head{cursor:default} }
@media (max-width:860px){
  .hiw-head{cursor:pointer;min-height:56px;display:flex;align-items:center;
    position:relative;padding-right:2rem}
  /* the open/closed mark */
  .hiw-head::after{content:"";position:absolute;right:.25rem;top:50%;
    width:11px;height:11px;margin-top:-6px;
    border-right:1.5px solid var(--grey);border-bottom:1.5px solid var(--grey);
    transform:rotate(45deg) translate(-2px,-2px);
    transition:transform var(--t-section) var(--ease)}
  .hiw-item.is-open .hiw-head::after{transform:rotate(-135deg) translate(-3px,-3px)}
  .hiw-head:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
  /* The open/close is a grid row going 0fr to 1fr, not a height. Height was
     being transitioned - the JS set it once, but naming it in the transition
     list meant the browser tweened it anyway, which is a layout animation on
     every frame of the open. grid-template-rows is the cheap way to do this
     and it needs no measured pixel value at all, so the JS stops measuring. */
  .hiw-body-wrap{display:grid;grid-template-rows:0fr;opacity:0;
    transform:translateY(-4px);
    transition:grid-template-rows var(--t-section) var(--ease),
               opacity var(--t-section) var(--ease),
               transform var(--t-section) var(--ease)}
  .hiw-body-wrap > p{min-height:0;overflow:hidden;padding-bottom:0}
  .hiw-item.is-open .hiw-body-wrap{grid-template-rows:1fr;opacity:1;transform:none}
  .hiw-item.is-open .hiw-body-wrap > p{padding-bottom:var(--sp-3)}
  /* lit state is meaningless when only one is open */
  .hiw-item{--lit:1}
  .hiw-item h3{opacity:1}
}
@media (max-width:860px) and (prefers-reduced-motion:reduce){
  /* all four open, nothing animating */
  .hiw-body-wrap{grid-template-rows:1fr !important;opacity:1 !important;transform:none !important;transition:none}
  .hiw-body-wrap > p{padding-bottom:var(--sp-3)}
  .hiw-head::after{display:none}
}

/* ══════════════════════════════════════════════════════════════
   PHONE — Operations, and the close
   ══════════════════════════════════════════════════════════════ */
@media (max-width:860px){
  /* one column, full width inside the gutter, and every diagram the same
     height - the Trends tile was 46px taller than the others because it
     carries the window row above its chart */
  .ops-grid{grid-template-columns:1fr;row-gap:var(--sp-4);margin-inline:0}
  .ops-col{padding-inline:0;border-left:0;display:block}
  .ops-dgm{aspect-ratio:16/11;height:auto;max-height:none;margin:var(--sp-3) 0 var(--sp-2)}
  .ops-dgm-win{display:grid;grid-template-rows:auto 1fr}
  .ops-tt{margin-bottom:0}
  .ops-body{margin-top:.6rem}

  /* THE CLOSE. It was 832px - a whole screen - for a headline, a line and a
     button. Half that, with the button full width and the type reflowed to
     a phone's measure rather than a desktop's scaled down. */
  .cta-blue{padding-block:var(--sp-5)}
  .cta-row{flex-direction:column;align-items:stretch;gap:var(--sp-3)}
  /* flex:1 1 32rem - the 32rem basis is on the MAIN axis, and the row is a
     column here, so it was setting a 512px HEIGHT on the text block. That
     is most of what made this section a whole screen. */
  .cta-words{max-width:none;flex:none}
  .cta-blue h2{font-size:clamp(1.6rem,7.2vw,2.1rem);max-width:none;line-height:1.1}
  .cta-lede{margin-top:.7rem;font-size:1rem;line-height:1.5}
  .cta-acts{margin-top:0;flex-direction:column;align-items:stretch;gap:.9rem}
  .cta-acts .btn{width:100%;justify-content:center;min-height:52px}
  .cta-link,.cta-link-quiet{justify-content:center}
}

/* ══════════════════════════════════════════════════════════════
   THE FOOTER
   Four even columns on desktop, one column on a phone, in the same order.
   The rhythm is the page's own: --sec-pad above and below, --sp-col between
   the columns, --sp-3 between rows - no spacing invented for the footer.
   ══════════════════════════════════════════════════════════════ */
.foot{background:var(--paper);border-top:1px solid var(--line);padding-block:var(--sec-pad)}
.foot-cols{display:grid;grid-template-columns:repeat(4,1fr);
  gap:var(--sp-col);align-items:start;padding-block:0}
.foot-brand .lockup{color:var(--navy);margin-bottom:var(--sp-3)}
.foot-brand .logo{height:30px;width:auto}
.foot-brand p{margin:0;max-width:34ch;font-size:.9375rem;line-height:1.55;color:var(--grey)}
.foot-h{margin:0 0 var(--sp-3);font-family:var(--mono);font-size:.6875rem;
  font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--grey-2)}
.foot-col ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--sp-2)}
.foot-col a{display:inline-flex;align-items:center;min-height:28px;
  font-size:.9375rem;color:var(--grey);text-decoration:none;
  transition:color var(--t-section) var(--ease)}
.foot-col a:hover{color:var(--accent)}
.foot-bar{margin-top:var(--sec-pad);padding-top:var(--sp-3);
  border-top:1px solid var(--line-dark);
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3)}
.foot-copy{margin:0;font-family:var(--mono);font-size:.6875rem;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--grey-2)}
@media (max-width:860px){
  .foot-cols{grid-template-columns:1fr;gap:var(--sp-5)}
  .foot-brand p{max-width:none;font-size:1rem}
  .foot-col a{min-height:44px;font-size:1rem}
  .foot-bar{margin-top:var(--sp-5);justify-content:flex-start}
  .foot-copy,.foot-h{color:var(--grey-2);font-size:.8125rem}
}

/* every control the keyboard can reach says so, explicitly */
.lang a:focus-visible,.hiw-head:focus-visible,.nav-toggle:focus-visible{
  outline:2px solid var(--accent);outline-offset:3px;border-radius:2px}
.nav:not(.grounded) .lang a:focus-visible,
.nav:not(.grounded) .nav-toggle:focus-visible{outline-color:var(--paper)}

/* ══════════════════════════════════════════════════════════════
   THE COOKIE NOTICE
   A small bar, not a consent wall, because there is nothing to consent to:
   the site sets no cookies, loads no third-party anything, and stores one
   key - the language you picked - which is strictly necessary storage under
   ePrivacy and exempt from consent. So it informs and dismisses. No "manage
   preferences", no vendor list, no second button pretending to be a choice.
   ══════════════════════════════════════════════════════════════ */
/* ══════════ ROUND 8 · the notice ══════════
   The storage audit found two localStorage keys, no cookies, no third-party
   requests and nothing that is not strictly necessary for something the
   visitor asked for, so §25 TDDDG needs no consent and this is a notice
   rather than a banner: one line, no buttons to weigh up, nothing blocking.
   The text names the two keys and nothing else. */
.ck{position:fixed;left:var(--gut);bottom:var(--gut);z-index:55;
  max-width:min(360px,calc(100vw - var(--gut) * 2));
  display:flex;align-items:flex-start;gap:.6rem;
  padding:.6rem .7rem;
  background:color-mix(in srgb,var(--paper) 94%,transparent);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border:1px solid var(--line);border-radius:10px;
  box-shadow:0 4px 18px rgb(19 26 40 / .07);
  transform:translateY(calc(100% + var(--gut) + 12px));
  transition:transform var(--t-reveal) var(--ease)}
.ck.on{transform:none}
.ck[hidden]{display:none}
.ck-t{margin:0;font-size:.8125rem;line-height:1.45;color:var(--grey)}
.ck-t a{color:var(--accent);text-underline-offset:2px}
.ck-b{flex:none;width:44px;height:44px;margin:-10px -10px -10px 0;
  display:inline-flex;align-items:center;justify-content:center;
  background:none;border:0;cursor:pointer;border-radius:4px;
  font-size:1rem;line-height:1;color:var(--grey-2);
  transition:color var(--t-state) var(--ease),background var(--t-state) var(--ease)}
.ck-b:hover{color:var(--navy);background:var(--paper-alt)}
.ck-b:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* it must never cover the phone's sticky Book a call bar */
@media (max-width:860px){
  .ck{left:var(--gut);right:var(--gut);max-width:none;
      bottom:calc(var(--mob-cta-h,68px) + var(--gut))}
}
/* The round 7 phone block used to live here and came AFTER the round 8 one,
   so its bottom:var(--gut) won and the notice sat on top of the sticky Book a
   call bar. Round 8's rule is the only one now. */
@media (prefers-reduced-motion:reduce){ .ck{transition:none} }

/* ══════════════════════════════════════════════════════════════
   STILL BY HAND — the evidence between the problem and the answer
   Four figures reading as ONE argument, left to right: it is still manual,
   this is how much of the week it eats, this is what leaks, and this is what
   leaks on top. They share a rule and a rhythm so the eye runs along them
   rather than stopping at each. Not pinned - this section is read, not
   scrubbed.
   ══════════════════════════════════════════════════════════════ */
.ev-row{list-style:none;margin:var(--sp-5) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(4,1fr)}
.ev{padding:0 var(--sp-3);border-left:1px solid var(--line)}
.ev:first-child{border-left:0;padding-left:0}
.ev-n{margin:0 0 .5rem;font-family:var(--sans);font-weight:600;line-height:1;
  font-size:clamp(1.8rem,3.3vw,3rem);letter-spacing:-.025em;color:var(--accent);
  font-variant-numeric:tabular-nums}
.ev-n span{font-size:.44em;font-weight:500;letter-spacing:0;color:var(--grey)}
.ev-l{margin:0;font-size:.9375rem;line-height:1.5;color:var(--grey);max-width:24ch}
.ev-note{margin:var(--sp-3) 0 0;font-size:.8125rem;line-height:1.5;color:var(--grey)}
.ev-val{margin-top:var(--sp-5);padding-top:var(--sp-3);border-top:1px solid var(--line);
  display:grid;grid-template-columns:1.05fr .95fr;gap:var(--sp-col);align-items:start}
.ev-val p:last-child{margin:0;font-size:1.0625rem;line-height:1.55;color:var(--navy)}
@media (max-width:980px){
  .ev-row{grid-template-columns:1fr 1fr;row-gap:var(--sp-4)}
  .ev:nth-child(odd){border-left:0;padding-left:0}
  .ev-val{grid-template-columns:1fr;gap:var(--sp-2)}
}
@media (max-width:860px){
  .ev-row{margin-top:var(--sp-4)}
  .ev-l{font-size:.875rem;max-width:none}
  /* the unit suffix rides the figure's size; at the phone clamp that put it
     at 12.7px, under the floor */
  .ev-n span{font-size:.8125rem}
  .ev-val p:last-child{font-size:1rem}
}


/* ══════════ ROUND 7 · a figure inside a sentence ══════════
   The statistics section is gone; its numbers now live in running text. The
   mono face and tabular figures are what mark them as measurements rather
   than as emphasis, so no colour and no weight change is needed. */
.n{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:.94em;font-weight:500;letter-spacing:-.01em;color:inherit}
.faq-src{max-width:70ch;margin:var(--sp-5) auto 0;padding-top:var(--sp-3);
  border-top:1px solid var(--line);font-size:.8125rem;line-height:1.6;color:var(--grey-2)}
.faq-src a{color:var(--accent)}

/* ══════════════════════════════════════════════════════════════════════
   ROUND 8 · HOW IT WORKS
   An accordion on the left, one step open at a time, and the diagram on the
   right playing the close as the pin scrolls. The whole section is sized to
   fit one viewport below the nav; the binding case is 1280x720, which leaves
   644px, so every band below is budgeted against that and simply has more
   air at 900 or 864.

   Everything the animation touches is written by site.js as --k (the stage,
   0 to 3) and --q (progress inside it), plus a handful of per-element flags.
   All of it is a pure function of scroll, so it reverses exactly.
   ══════════════════════════════════════════════════════════════════════ */

#how{overflow-x:clip}                      /* sticky dies inside overflow:hidden */
.hiw{--k:0;--q:0;
  /* four stages at about 1.35 viewports each */
  --hiw-scroll:540vh;position:relative;height:var(--hiw-scroll)}
.hiw-frame{position:sticky;top:var(--nav-h,76px);
  height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;
  display:flex;align-items:center}
/* NB: this element also carries .wrap, so it must not declare a width. */
.hiw-sticky{display:flex;flex-direction:column;justify-content:center;
  gap:clamp(18px,2.8vh,38px);height:100%;padding-block:clamp(16px,3vh,38px);border:0}

/* ---- heading and the one intro line, side by side to save height ---- */
.hw-top{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.84fr);
  gap:clamp(20px,3vw,56px);align-items:end}
.hw-top h2{margin:0;font-family:var(--display);font-weight:600;
  font-size:clamp(1.5rem,2.5vw,2.3rem);line-height:1.08;letter-spacing:var(--tr-h2);
  color:var(--navy);text-wrap:balance}
.hw-top p{margin:0;font-size:clamp(.875rem,1.05vw,1rem);line-height:1.5;color:var(--grey)}

/* ---- the two columns ---- */
.hw-main{display:grid;grid-template-columns:34% minmax(0,1fr);
  gap:clamp(28px,3.6vw,68px);align-items:start;min-height:0}

/* ---- the accordion ---- */
.hw-acc{list-style:none;margin:0;padding:0;display:grid;align-content:start;
  gap:clamp(2px,.7vh,8px)}
.hw-item{border-bottom:1px solid var(--line)}
.hw-item:last-child{border-bottom:0}
.hw-head{display:flex;align-items:center;gap:.7rem;width:100%;
  padding:.5rem 0;background:none;border:0;cursor:pointer;text-align:left;
  color:inherit;font:inherit;
  --on:0;opacity:calc(.46 + var(--on) * .54);
  transition:opacity var(--t-section) var(--ease)}
.hw-ico{flex:none;width:clamp(38px,3.1vw,52px);height:auto;display:block;
  transform:scale(calc(.88 + var(--on) * .12));
  transition:transform var(--t-section) var(--ease)}
.hw-head b{font-family:var(--sans);font-size:clamp(1rem,1.25vw,1.2rem);font-weight:600;
  letter-spacing:-.01em;color:var(--navy)}
/* 1fr/0fr is the only height animation that needs no measured pixel value */
.hw-body{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows var(--t-reveal) var(--ease),
             opacity var(--t-section) var(--ease);opacity:0}
.hw-body > p{overflow:hidden;margin:0;
  padding-left:calc(clamp(38px,3.1vw,52px) + .7rem);
  padding-bottom:.7rem;
  font-size:clamp(.8125rem,.95vw,.9375rem);line-height:1.55;color:var(--grey)}
.hw-item.on .hw-head{--on:1}
.hw-item.on .hw-body{grid-template-rows:1fr;opacity:1}

/* ---- the diagram ---- */
.dg{position:relative;display:grid;
  grid-template-columns:minmax(0,.92fr) minmax(0,1.18fr) minmax(0,.78fr);
  gap:clamp(22px,2.8vw,46px);align-items:center;min-height:0}
.dg-wires{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;overflow:visible}
.dg-wires path{fill:none;stroke:var(--accent);stroke-width:1.5;stroke-linecap:round;
  stroke-dasharray:var(--len,1);stroke-dashoffset:calc(var(--len,1) * (1 - var(--draw,0)));
  opacity:calc(.22 + var(--draw,0) * .58)}

/* the six sources */
.dg-srcs{position:relative;z-index:1;list-style:none;margin:0;padding:0;display:grid;gap:clamp(5px,.9vh,9px)}
.dg-src{--own:0;
  display:grid;grid-template-columns:26px minmax(0,1fr) 22px;
  grid-template-areas:"i n p" "i c p" "i o p";
  gap:0 .5rem;align-items:center;
  padding:.4rem .5rem;background:var(--paper);
  border:1px solid color-mix(in srgb,var(--accent) calc(var(--own) * 55%),var(--line));
  border-radius:8px;opacity:calc(.46 + var(--own) * .54);
  transition:opacity .22s var(--ease),border-color .22s var(--ease)}
.dg-si{grid-area:i;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:7px;background:var(--accent-tint);color:var(--accent)}
.dg-si svg{width:15px;height:15px;display:block}
.dg-sn{grid-area:n;font-size:.75rem;font-weight:600;color:var(--navy);line-height:1.2}
.dg-sc{grid-area:c;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:.5938rem;letter-spacing:.01em;color:var(--grey-2);line-height:1.3;
  opacity:var(--own);transition:opacity .25s var(--ease)}
/* the plug: an off/on switch that flips as the source connects */
.dg-plug{grid-area:p;justify-self:end;width:22px;height:12px;border-radius:7px;
  background:color-mix(in srgb,var(--accent) calc(var(--own) * 100%),var(--line));
  position:relative;transition:background .22s var(--ease)}
.dg-plug i{position:absolute;top:1.5px;left:1.5px;width:9px;height:9px;border-radius:50%;
  background:var(--paper);transform:translateX(calc(var(--own) * 10px));
  transition:transform .22s var(--ease)}
.dg-ok{grid-area:o;font-family:var(--mono);font-size:.5625rem;letter-spacing:.02em;
  color:var(--accent);line-height:1.3;white-space:nowrap;
  opacity:var(--own);
  /* it has no height until the source is connected, so six disconnected
     tiles are no taller than they need to be */
  max-height:calc(var(--own) * 14px);overflow:hidden;
  transition:opacity .25s var(--ease)}

/* the Tyll card */
.dg-card{position:relative;z-index:1;background:var(--paper);
  border:1px solid var(--line);border-radius:12px;padding:clamp(11px,1.15vw,16px);
  box-shadow:0 1px 2px rgb(19 26 40 / .04);display:grid;gap:.4rem;align-content:start}
.dg-card-h{display:flex;align-items:center;justify-content:space-between;gap:.6rem;
  padding-bottom:.45rem;border-bottom:1px solid var(--line)}
.dg-logo{height:17px;width:auto;color:var(--navy);display:block}
.dg-chip-s{font-family:var(--mono);font-size:.5625rem;font-weight:500;letter-spacing:.09em;
  text-transform:uppercase;padding:.22rem .42rem;border-radius:4px;
  background:var(--accent-tint);color:var(--accent);white-space:nowrap}

/* the matching pairs: two chips that slide together and lock */
.dg-pairs{list-style:none;margin:0;padding:0;display:grid;gap:3px;
  max-height:calc(var(--pairsh,0) * 112px);overflow:hidden;
  opacity:var(--pairs,0);transition:opacity .2s var(--ease)}
.dg-pair{--p:0;--lock:0;
  display:flex;align-items:center;justify-content:center;gap:0;
  opacity:var(--p);height:20px}
.dg-chip{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:.5938rem;
  padding:.16rem .36rem;border-radius:4px;border:1px solid var(--line);
  background:var(--paper);color:var(--navy);white-space:nowrap;
  transition:transform .18s var(--ease)}
.dg-chip-a{transform:translateX(calc((1 - var(--lock)) * -26px))}
.dg-chip-b{transform:translateX(calc((1 - var(--lock)) * 26px))}
/* The tick that appears when a pair locks together. It used to animate its
   own width, which lays out the whole flex row on every frame of every one of
   the five pairs. The width is fixed now and only transform and opacity move,
   so the row's geometry is settled before the animation starts. */
.dg-lock{display:inline-flex;width:15px;color:var(--accent);
  opacity:var(--lock);transform:scale(var(--lock));transform-origin:center;
  transition:transform .18s var(--ease),opacity .18s var(--ease)}
.dg-lock svg{width:13px;height:13px;display:block;margin-inline:1px}

.dg-prog{height:3px;border-radius:3px;background:var(--line);overflow:hidden}
.dg-prog i{display:block;height:100%;width:calc(var(--fill,0) * 100%);background:var(--accent);
  border-radius:3px}
.dg-count{margin:0;font-size:.6875rem;color:var(--grey);display:flex;gap:.35rem;align-items:baseline}
.dg-count b{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:.9375rem;font-weight:600;color:var(--accent)}
.dg-open{margin:0;font-size:.6563rem;color:var(--grey);display:flex;gap:.3rem;align-items:baseline;
  opacity:var(--openshow,0);transition:opacity .22s var(--ease)}
.dg-open b{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:600;
  color:var(--flag)}

/* the four differences: red while open, green once resolved */
.dg-rows{list-style:none;margin:0;padding:0;display:grid;gap:2px}
.dg-row{--row:0;--done:0;
  display:grid;grid-template-columns:14px minmax(0,1fr) auto;gap:0 .4rem;
  align-items:center;padding:calc(var(--row) * .26rem) .34rem;border-radius:6px;
  /* red while it is open, green once it has been reviewed */
  background:color-mix(in srgb,
    color-mix(in srgb,var(--ok-tint) calc(var(--done) * 100%),var(--flag-tint))
    calc(var(--row) * 100%),transparent);
  opacity:calc(var(--row) * (1 - var(--gone,0)));
  max-height:calc(var(--row) * (1 - var(--gone,0)) * 40px);
  transform:translateX(calc(var(--gone,0) * 16px));
  transition:background .25s var(--ease)}
.dg-dot{grid-row:span 2;width:14px;height:14px;border-radius:50%;
  border:1.5px solid color-mix(in srgb,var(--ok) calc(var(--done) * 100%),var(--flag));
  color:var(--ok);display:inline-flex;align-items:center;justify-content:center}
.dg-dot svg{width:9px;height:9px;opacity:var(--done);display:block}
.dg-where{font-size:.6563rem;font-weight:600;color:var(--navy);line-height:1.2}
.dg-why{grid-column:2;font-size:.5938rem;color:var(--grey);line-height:1.25}
.dg-eur{grid-row:span 2;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:.6563rem;font-weight:600;white-space:nowrap;
  color:color-mix(in srgb,var(--ok) calc(var(--done) * 100%),var(--flag))}

/* the finish */
.dg-done{display:flex;align-items:center;gap:.5rem;justify-content:center;
  padding:.4rem 0 .1rem;opacity:var(--doneshow,0);
  transform:scale(calc(.82 + var(--doneshow,0) * .18));
  transition:opacity .3s var(--ease),transform .3s var(--ease)}
.dg-tick{display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:50%;background:var(--ok-tint);color:var(--ok)}
.dg-tick svg{width:18px;height:18px;display:block}
.dg-done b{font-size:.8125rem;font-weight:600;color:var(--ok)}

/* the export card */
.dg-exp{position:relative;z-index:1;background:var(--paper);
  border:1px solid color-mix(in srgb,var(--accent) calc(var(--live,0) * 55%),var(--line));
  border-radius:10px;padding:clamp(10px,1.1vw,15px);
  opacity:calc(.44 + var(--live,0) * .56);
  transition:opacity .28s var(--ease),border-color .28s var(--ease)}
.dg-exp-h{margin:0;font-size:.75rem;font-weight:600;color:var(--navy);line-height:1.3}
.dg-exp-b{margin:.6rem 0 0;display:flex;gap:.4rem;flex-wrap:wrap}
.dg-btn{font-family:var(--mono);font-size:.5938rem;font-weight:500;letter-spacing:.07em;
  padding:.3rem .5rem;border-radius:5px;
  border:1px solid color-mix(in srgb,var(--accent) calc(var(--live,0) * 70%),var(--line));
  color:color-mix(in srgb,var(--accent) calc(var(--live,0) * 100%),var(--grey-2));
  background:color-mix(in srgb,var(--accent-tint) calc(var(--live,0) * 100%),var(--paper))}

/* ---- the time, counting down under the diagram ---- */
.hw-time{display:flex;align-items:center;gap:clamp(14px,1.8vw,28px);
  padding-top:clamp(12px,2vh,20px);border-top:1px solid var(--line)}
.hw-time-l,.hw-time-v{font-family:var(--mono);font-size:.6875rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--grey-2);white-space:nowrap}
.hw-time-v{color:var(--navy)}
.hw-time-v b{font-size:1rem;font-weight:600;font-variant-numeric:tabular-nums;color:var(--accent)}
.hw-time-bar{flex:1;height:6px;border-radius:6px;background:var(--line);position:relative;overflow:hidden}
/* The bar is the hour you are getting back. It starts full and red, and as
   the stages run it both shortens and crosses to green, so the colour says
   the same thing the number does. */
.hw-time-bar i{position:absolute;inset:0;transform-origin:left;
  transform:scaleX(calc(1 - var(--saved,0) * .86));border-radius:6px;
  background:color-mix(in srgb,var(--ok) calc(var(--saved,0) * 100%),var(--flag))}
.hw-time-v b{color:color-mix(in srgb,var(--ok) calc(var(--saved,0) * 100%),var(--flag))}
.hw-mob{display:none}

/* ---- below 1024: no pin, a sticky label, the diagram stacks ---- */
@media (max-width:1023px), (pointer:coarse){
  .hiw{--hiw-scroll:auto;height:auto}
  .hiw-frame{position:static;height:auto;display:block}
  .hiw-sticky{height:auto;justify-content:flex-start;padding-block:var(--sec-pad)}
  .hw-top{grid-template-columns:1fr;gap:var(--sp-2);align-items:start}
  .hw-main{grid-template-columns:1fr;gap:var(--sp-4)}
  .hw-mob{display:flex;gap:.5rem;align-items:baseline;position:sticky;
    top:calc(var(--nav-h,64px) + 6px);z-index:3;align-self:flex-start;
    margin:0;padding:.5rem .7rem;
    background:color-mix(in srgb,var(--paper) 92%,transparent);
    -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
    border:1px solid var(--line);border-radius:8px;
    font-size:.8125rem;color:var(--grey)}
  .hw-mob b{font-family:var(--mono);font-weight:600;color:var(--accent)}
  .hw-mob span{font-weight:600;color:var(--navy)}
  /* every step is readable on a phone; the accordion is a desktop device */
  .hw-body{grid-template-rows:1fr;opacity:1}
  .hw-head{--on:1;opacity:1}
  .dg{grid-template-columns:1fr;gap:var(--sp-3)}
  .dg-wires{display:none}
  .dg-srcs{grid-template-columns:1fr 1fr;gap:6px}
  .dg-sn{font-size:.8125rem}
  .dg-sc{font-size:.6563rem}
  .dg-where{font-size:.75rem}
  .dg-why{font-size:.6875rem}
  .dg-count b{font-size:1.0625rem}
  .dg-logo{height:19px}
}
@media (max-width:359px){ .dg-srcs{grid-template-columns:1fr} }

/* ---- reduced motion: the finished state, every step readable ---- */
@media (prefers-reduced-motion: reduce){
  .hw-head{--on:1;opacity:1}
  .hw-body{grid-template-rows:1fr;opacity:1}
  .dg-src{--own:1;opacity:1}
  .dg-pairs{display:none}
  .dg-row{--row:1;--done:1;--gone:0}
  .dg-exp{--live:1;opacity:1}
  .dg-done{--doneshow:1}
  .dg-open{opacity:1}
}

/* ══════════ ROUND 7 · the four operations widgets ══════════
   Every one of these was a box of grey placeholder bars. They now carry the
   actual thing the card claims, with the same numbers as the diagram and the
   Problem section. Each reveals against its tile's own --t, which the ops
   engine already drives across the pin. */
.ops-dgm{background:var(--paper-alt);border:1px solid var(--line);border-radius:10px;
  padding:.8rem .85rem;display:block;overflow:hidden}
.ow{--t:inherit}
.ow-lab{margin:0 0 .55rem;font-family:var(--mono);font-size:.5625rem;font-weight:500;
  letter-spacing:.11em;text-transform:uppercase;color:var(--grey-2)}
.ow-cause{margin:.6rem 0 0;padding-top:.5rem;border-top:1px solid var(--line);
  font-size:.6875rem;line-height:1.35;color:var(--grey)}

/* 1 · the eight checks, ticking one at a time */
.ow-h{margin:0 0 .6rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.ow-tag{font-family:var(--mono);font-size:.5625rem;font-weight:600;letter-spacing:.12em;
  padding:.18rem .4rem;border:1px solid var(--line);border-radius:3px;color:var(--grey-2)}
.ow-h .ct{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:.75rem;
  font-weight:600;color:var(--accent)}
.ow-checks{list-style:none;margin:0;padding:0;display:grid;gap:.22rem}
.ow-checks li{display:grid;grid-template-columns:12px 1fr;gap:.45rem;align-items:center;
  font-size:.6875rem;line-height:1.3;color:var(--grey);
  --on:clamp(0,calc((var(--t,0) - var(--i) * .11) * 9),1);
  color:color-mix(in srgb,var(--navy) calc(var(--on) * 100%),var(--grey-2))}
.ow-checks i{display:block;width:12px;height:12px;border-radius:50%;
  border:1.5px solid var(--line);position:relative;
  border-color:color-mix(in srgb,var(--accent) calc(var(--on) * 100%),var(--line))}
.ow-checks i::after{content:"";position:absolute;left:3px;top:3.5px;width:4.5px;height:2.5px;
  border-left:1.5px solid var(--accent);border-bottom:1.5px solid var(--accent);
  transform:rotate(-45deg) scale(var(--on));transform-origin:center}

/* 2 · the differences, ranked by what they are worth */
.ow-rank{list-style:none;margin:0;padding:0;display:grid;gap:.42rem}
.ow-rank li{display:grid;grid-template-columns:1fr auto;gap:.1rem .5rem;align-items:center;
  --on:clamp(0,calc((var(--t,0) - var(--i) * .14) * 7),1);opacity:calc(.25 + var(--on) * .75)}
.ow-rank span{font-size:.6875rem;color:var(--navy);line-height:1.2}
.ow-rank b{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:.6875rem;
  font-weight:600;color:var(--navy);grid-row:span 2;align-self:center}
.ow-rank i{grid-column:1;display:block;height:5px;border-radius:3px;background:var(--accent);
  width:calc(var(--w) * var(--on));min-width:3px}

/* 3 · seven nights, the last one the reason the card exists */
.ow-days{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(7,1fr);
  gap:4px;align-items:end;height:62px}
.ow-days li{display:grid;align-content:end;justify-items:center;gap:3px;
  --on:clamp(0,calc((var(--t,0) - var(--i) * .08) * 8),1)}
.ow-days i{display:block;width:100%;border-radius:2px 2px 0 0;background:var(--line);
  height:calc(var(--h) * var(--on));min-height:2px}
.ow-days li.on i{background:var(--accent)}
.ow-days em{font-family:var(--mono);font-style:normal;font-size:.5rem;color:var(--grey-2)}
.ow-days li.on em{color:var(--accent);font-weight:600}

/* 4 · counted against recorded, whole euros */
.ow-draw{margin:0;display:grid;gap:.3rem}
.ow-draw > div{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem;
  --on:clamp(0,calc((var(--t,0) - var(--i) * .16) * 6),1);opacity:calc(.25 + var(--on) * .75)}
.ow-draw dt{font-family:var(--mono);font-size:.5625rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--grey-2)}
.ow-draw dd{margin:0;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:.8125rem;font-weight:600;color:var(--navy)}
.ow-diff{padding-top:.3rem;border-top:1px solid var(--line)}
.ow-diff dd{color:var(--flag)}

/* ══════════ ROUND 7 · one icon set ══════════
   Every icon on the site is an inline SVG on a 24x24 grid, 1.5px stroke,
   round caps and joins, currentColor, no fills. They sit in a tinted badge so
   an icon reads as an icon wherever it appears. The small variant is for
   inline chips, which carry no badge. */
.icn{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;border-radius:13px;
  background:var(--accent-tint);color:var(--accent)}
.icn svg{display:block;width:24px;height:24px}
.icn-sm{width:auto;height:auto;border-radius:0;background:none;color:var(--grey-2)}
.icn-sm svg{width:18px;height:18px}
.ops-ico{height:46px;margin-bottom:.8rem;display:flex;align-items:center}

/* the quiet nights have to be visible against the widget's own ground */
.ow-days i{background:#CFD3DB}

/* the four chips */
.sec-facts{list-style:none;margin:var(--sp-5) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2)}
.sec-facts li{display:flex;align-items:center;gap:.55rem;
  font-size:.8125rem;color:var(--grey);line-height:1.4}
@media (max-width:860px){ .sec-facts{grid-template-columns:1fr 1fr;row-gap:1rem} }
@media (max-width:460px){ .sec-facts{grid-template-columns:1fr} }

/* ══════════ ROUND 7 · the standing pages ══════════ */
.page-doc{background:var(--paper)}
.page-doc .nav{--nav-fg:var(--navy);--nav-tint:var(--paper);--nav-rule:var(--line)}
.page-doc main{padding-top:clamp(104px,13vh,152px);padding-bottom:clamp(64px,8vh,112px)}
.doc{max-width:70ch}
.doc h1{margin:.4rem 0 0;font-family:var(--display);font-size:var(--fs-h2);line-height:1.06;
  letter-spacing:var(--tr-h2);font-weight:600;color:var(--navy)}
.doc .doc-lede{margin:1rem 0 0;font-size:var(--fs-lede);line-height:1.5;color:var(--grey)}
.doc h2{margin:2.4rem 0 .5rem;font-family:var(--sans);font-size:var(--fs-h3);
  font-weight:600;line-height:1.25;letter-spacing:-.01em;color:var(--navy)}
.doc p{margin:0 0 .9rem;font-size:var(--fs-body);line-height:1.62;color:var(--grey)}
.doc a{color:var(--accent);text-underline-offset:3px}
.doc .doc-addr{font-family:var(--mono);font-size:.9375rem;line-height:1.8;color:var(--navy)}
.doc .doc-note{font-family:var(--mono);font-size:.875rem;color:var(--grey-2);
  background:var(--paper-alt);border:1px solid var(--line);border-radius:8px;padding:.7rem .9rem}
.doc .doc-back{margin-top:2.6rem;padding-top:1.3rem;border-top:1px solid var(--line)}
.doc .doc-back a{display:inline-flex;align-items:center;min-height:44px}

/* ══════════ ROUND 7 · /book ══════════
   Three steps on one screen. Big targets, one column on a phone, and no
   step appears before the one above it has been answered. */
.bk{max-width:760px}
.bk-head h1{margin:0;font-family:var(--display);font-size:var(--fs-h2);line-height:1.06;
  letter-spacing:var(--tr-h2);font-weight:600;color:var(--navy)}
.bk-lede{margin:.9rem 0 0;font-size:var(--fs-lede);line-height:1.5;color:var(--grey);max-width:46ch}
.bk-form{margin-top:var(--sp-6);display:grid;gap:var(--sp-5)}
.bk-step h2{margin:0 0 var(--sp-3);display:flex;align-items:center;gap:.7rem;
  font-family:var(--sans);font-size:1.0625rem;font-weight:600;color:var(--navy)}
.bk-n{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:50%;background:var(--accent-tint);color:var(--accent);
  font-family:var(--mono);font-size:.75rem;font-weight:600}
.bk-wait{margin:0;font-size:.9375rem;color:var(--grey)}
.bk-retry{background:none;border:0;padding:0;color:var(--accent);cursor:pointer;
  font:inherit;text-decoration:underline;text-underline-offset:3px}

.bk-days{display:grid;grid-template-columns:repeat(auto-fit,minmax(78px,1fr));gap:.5rem}
.bk-day{display:grid;justify-items:center;gap:1px;min-height:74px;padding:.6rem .4rem;
  background:var(--paper);border:1px solid var(--line);border-radius:10px;cursor:pointer;
  color:var(--navy);font:inherit;transition:border-color var(--t-state) var(--ease),
  background var(--t-state) var(--ease)}
.bk-day em{font-style:normal;font-family:var(--mono);font-size:.5625rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--grey-2)}
.bk-day b{font-size:1.25rem;font-weight:600;line-height:1.1}
.bk-day i{font-style:normal;font-family:var(--mono);font-size:.5625rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--grey-2)}
.bk-day:hover{border-color:var(--accent)}
.bk-day.on{border-color:var(--accent);background:var(--accent-tint)}
.bk-day.on em,.bk-day.on i{color:var(--accent)}
.bk-day[disabled]{opacity:.4;cursor:not-allowed}

.bk-times{display:grid;grid-template-columns:repeat(auto-fit,minmax(88px,1fr));gap:.5rem}
.bk-time{min-height:48px;padding:0 .5rem;background:var(--paper);border:1px solid var(--line);
  border-radius:9px;cursor:pointer;color:var(--navy);font:inherit;
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:.9375rem;
  transition:border-color var(--t-state) var(--ease),background var(--t-state) var(--ease)}
.bk-time:hover{border-color:var(--accent)}
.bk-time.on{border-color:var(--accent);background:var(--accent-tint);color:var(--accent);font-weight:600}
.bk-tz{margin:.8rem 0 0;font-size:.8125rem;color:var(--grey-2)}

.bk-fields{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2)}
.bk-f{display:grid;gap:.35rem;font-size:.8125rem;color:var(--grey)}
.bk-f input{min-height:48px;padding:0 .8rem;border:1px solid var(--line);border-radius:9px;
  background:var(--paper);color:var(--navy);font:inherit;font-size:1rem}
.bk-f input:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
.bk-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.bk-err{margin:var(--sp-2) 0 0;font-size:.875rem;color:var(--flag)}
.bk-go{margin-top:var(--sp-3);min-height:52px}
.bk-go[disabled]{opacity:.6;cursor:progress}

.bk-done{margin-top:var(--sp-6);padding:var(--sp-5);text-align:center;
  background:var(--paper);border:1px solid var(--line);border-radius:14px}
.bk-done .icn{margin:0 auto var(--sp-3)}
.bk-done h2{margin:0;font-family:var(--sans);font-size:1.3rem;font-weight:600;color:var(--navy);
  line-height:1.35}
.bk-done p{margin:.6rem 0 0;color:var(--grey)}
.bk-done-acts{margin-top:var(--sp-4);display:flex;gap:var(--sp-3);align-items:center;
  justify-content:center;flex-wrap:wrap}
.bk-back{color:var(--accent);font-size:.9375rem}

@media (max-width:620px){
  .bk-fields{grid-template-columns:1fr}
  .bk-days{grid-template-columns:repeat(auto-fit,minmax(70px,1fr))}
  .bk-times{grid-template-columns:repeat(auto-fit,minmax(78px,1fr))}
}
/* the booking page's steps are sections, and the global section rhythm would
   otherwise put a screen of air between each one */
.page-book main section{padding-block:0}
.page-book .bk-form{gap:var(--sp-4)}
/* [hidden] is a UA rule, so any class that declares display beats it. Several
   do on this page, which left the form on screen underneath its own
   confirmation. */
[hidden]{display:none !important}
/* The one call to action deserves a full-size target. Scoped to the width
   where the nav button exists at all: below it the nav collapses and
   declaring display here would put the button back on screen. */
@media (min-width:1000px){
  .nav .btn-primary{min-height:44px;display:inline-flex;align-items:center}
}

/* ══════════ ROUND 8 · the Problem wipe holds its size ══════════
   The panel already had a fixed ratio and measured 0.00px of drift in both
   engines. What jumped was the picture, not the box: the "after" image was
   loading="lazy", so the strip the wipe opened was empty until the file
   arrived and popped in. It is eager and preloaded now. These rules stop the
   box from ever being able to size to its content, so that stays true. */
.fc-panel{aspect-ratio:2.33 / 1;height:auto}   /* matches the source files exactly: no crop, no squash */
.fc-layer{width:100%;height:100%;contain:paint}
.fc-shot{display:block;width:100%;height:100%;object-fit:cover}
@media (max-width:860px){
  /* stacked rather than wiped on a phone, and both slides take the same box */
  .fc-shot{aspect-ratio:4 / 3;width:100%;height:auto}
}
.doc code{font-family:var(--mono);font-size:.875em;background:var(--paper-alt);
  border:1px solid var(--line);border-radius:4px;padding:.08em .34em;color:var(--navy)}

/* ══════════ ROUND 8 · the Getting started icons on white ══════════
   These four are the original files, restored from before-round7. They were
   rendered in light silver for the navy ground that section used to have, so
   on white they sat at barely any contrast. Rather than replace them with
   lookalikes, the same objects are darkened: the forms and the blue accents
   survive, and they now hold against the page. */
.step-body .ico,
.steps-list .ico{filter:brightness(.58) contrast(1.35) saturate(1.1)}

/* ══════════ ROUND 8 · the integration row ══════════
   1fr for the marks and auto for the chip, so the chip is pinned to the right
   end at every width and can never wrap below the logos. The marks track can
   scroll on a narrow screen rather than pushing the chip off. Every mark is
   normalised to the same optical height and rendered in the same grey. */
/* ROUND 9: back under the intro text, where it was before round 8 moved it
   full width. In a half-width column six marks will not sit on one line, so
   the row wraps instead of scrolling: nothing is hidden off the edge, and the
   chip stays at the end of the last line. */
.ops-systems{display:flex;flex-wrap:wrap;align-items:center;
  gap:clamp(8px,1.1vh,14px) clamp(14px,1.6vw,26px);
  margin-top:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--line)}
.opl-row{list-style:none;margin:0;padding:0;min-width:0;
  display:flex;flex-wrap:wrap;align-items:center;
  gap:clamp(8px,1.1vh,14px) clamp(14px,2vw,28px)}
.opl-row::-webkit-scrollbar{display:none}
.opl{flex:none;display:inline-flex;align-items:center;height:18px;color:var(--grey-2)}
/* one height for every mark, whatever its own viewBox says */
.opl-mark{height:15px;width:auto;display:block;fill:currentColor;
  filter:grayscale(1);opacity:.8}
/* Apaleo and Oracle OPERA have no mark in the repo, so they are set rather
   than drawn. Never generate a company's logo. */
.opl-wm{font-family:var(--sans);font-size:.9375rem;font-weight:600;letter-spacing:-.01em;
  line-height:1;color:var(--grey-2);white-space:nowrap}
.opl-wm-sm{font-size:.75rem;font-weight:600;letter-spacing:.04em}
.opl-plus{flex:none;white-space:nowrap;
  font-family:var(--mono);font-size:.625rem;font-weight:500;letter-spacing:.08em;
  text-transform:uppercase;color:var(--grey-2);
  padding:.34rem .6rem;border:1px solid var(--line);border-radius:999px}
@media (max-width:620px){
  .ops-systems{grid-template-columns:minmax(0,1fr) auto;gap:10px}
  .opl-plus{font-size:.5625rem;padding:.3rem .5rem}
}

/* ══════════ ROUND 8 · room inside the operations widgets ══════════
   Every demo widget sits at least 24px from its card's edge now, and the
   content inside was shrunk rather than the padding, so nothing is clipped
   and nothing touches a border. */
.ops-dgm{padding:24px !important;display:block !important;
  background:var(--paper-alt);border:1px solid var(--line);border-radius:12px}
.ow-checks li{font-size:.6563rem}
.ow-checks{gap:.18rem}
.ow-rank{gap:.36rem}
.ow-days{height:52px}
.ow-draw{gap:.26rem}
/* the row is full width under the head, not inside the right column */
.ops-head .ops-systems{margin-top:var(--sp-3)}
/* eight checks read as two columns of four in the height a pinned card has */
.ow-checks{grid-template-columns:1fr 1fr;column-gap:.9rem}
.ow-rank{gap:.3rem}
.ow-days{height:44px}
/* the widgets, sized so 24px of air survives on all four sides inside a card
   that only gets one viewport's share of height */
.ow-checks{grid-template-columns:1fr 1fr;column-gap:.8rem;gap:.14rem .8rem}
.ow-checks li{font-size:.625rem}
.ow-checks i{width:10px;height:10px}
.ow-checks i::after{left:2.4px;top:2.8px;width:3.8px;height:2px}
.ow-h{margin-bottom:.4rem}
.ow-lab{margin-bottom:.4rem}
.ow-rank li{gap:.06rem .45rem}
.ow-rank i{height:4px}
.ow-days{height:34px}
.ow-cause{margin-top:.4rem;padding-top:.36rem;font-size:.625rem}
.ow-draw{gap:.2rem}
.ow-draw dd{font-size:.75rem}
/* The pinned frame was spending 150px of a single viewport on its own top and
   bottom padding, which is what left the demo widgets 111px for 175px of
   content. The padding comes down so the cards can have the room; the widgets
   keep their full 24px inset. */
.ops-frame{padding-block:clamp(18px,2.6vh,34px)}
.ops-head{padding-bottom:var(--sp-2)}
.ops-grid{margin-top:var(--sp-3)}
.ops-systems{margin-top:var(--sp-2);padding-top:var(--sp-2)}

@media (min-width:861px) and (max-height:760px){
  .ow-days{height:20px}
  .ow-checks li{font-size:.5rem}
  .ops-body{font-size:.6875rem}
}

@media (min-width:861px) and (max-height:840px){
  .ow-rank i{height:3px}
  .ow-rank span{line-height:1.1}
  .ow-rank li{--on:var(--on)}
  .ops-col:nth-child(2) .ow-lab{margin-bottom:.3rem}
}

/* The seven bars carry pixel heights in the markup (the tallest is 46px),
   so when the card's box shrinks the tallest one overflowed and sat on top
   of the caption. They scale to whatever height the box actually has. */
.ow-days{--bh:.62;position:relative;align-items:end}
.ow-days i{height:calc(var(--h) * var(--on) * var(--bh))}
.ow-days li{min-width:0}
@media (min-width:861px) and (max-height:840px){ .ow-days{--bh:.46} }
@media (min-width:861px) and (max-height:760px){ .ow-days{--bh:.34} }
@media (max-width:1023px), (pointer:coarse){ .ow-days{--bh:1} }

/* ══════════ ROUND 8 · security: one way in, nothing back ══════════
   One visual instead of three claim cards. The arrow into Tyll is solid and
   labelled read-only; the return arrow is drawn and then crossed out, which
   says more than leaving it off would. Under the mark, what Tyll reads sits
   next to what it never stores, struck through. Everything is real text. */
.fl{display:grid;grid-template-columns:minmax(0,.9fr) minmax(130px,.75fr) minmax(0,1.3fr);
  gap:clamp(10px,1.6vw,26px);align-items:center;margin-top:var(--sp-5)}
.fl-side,.fl-tyll{background:var(--paper);border:1px solid var(--line);border-radius:12px;
  padding:clamp(16px,1.6vw,22px)}
.fl-h,.fl-k{margin:0 0 .5rem;font-family:var(--mono);font-size:.5938rem;font-weight:500;
  letter-spacing:.11em;text-transform:uppercase;color:var(--grey-2)}
.fl-list{list-style:none;margin:0;padding:0;display:grid;gap:.3rem;
  font-size:.8125rem;color:var(--navy)}

/* the two arrows */
.fl-mid{display:grid;gap:clamp(14px,2.4vh,26px);justify-items:stretch}
.fl-arrow{position:relative;display:block;padding-block:.55rem}
.fl-arrow svg{display:block;width:100%;height:12px;overflow:visible}
.fl-arrow path{fill:none;stroke-width:1.5;stroke-linecap:round;
  stroke-dasharray:var(--l,140);stroke-dashoffset:calc(var(--l,140) * (1 - var(--go,0)))}
.fl-arrow-in path{stroke:var(--accent)}
.fl-arrow-back path{stroke:var(--grey-2);stroke-dasharray:4 4;stroke-dashoffset:0;
  opacity:calc(var(--go,0) * .85)}
/* The two arrowheads. These are the CSS triangle: a 0x0 box whose top and
   bottom borders are transparent and whose one solid side border becomes the
   point. The solid single-side border is the whole technique, not a tab on
   the side of a card; the elements are 7x8px and have no content. */
.fl-head{position:absolute;top:50%;width:0;height:0;
  border-top:4px solid transparent;border-bottom:4px solid transparent;
  opacity:var(--go,0);transition:opacity .4s var(--ease)}
.fl-arrow-in .fl-head{right:0;margin-top:-4px;border-left:7px solid var(--accent)}
.fl-head-back{left:0;margin-top:-4px;border-right:7px solid var(--grey-2)}
.fl-cross{position:absolute;left:50%;top:50%;width:16px;height:16px;margin:-8px 0 0 -8px;
  opacity:var(--go,0)}
.fl-cross::before,.fl-cross::after{content:"";position:absolute;left:0;top:7px;
  width:16px;height:1.5px;background:var(--flag);border-radius:2px}
.fl-cross::before{transform:rotate(45deg)} .fl-cross::after{transform:rotate(-45deg)}
.fl-arrow em{position:absolute;left:0;right:0;top:100%;margin-top:.1rem;
  font-style:normal;font-family:var(--mono);font-size:.5625rem;letter-spacing:.06em;
  text-transform:uppercase;text-align:center;line-height:1.3}
.fl-arrow-in em{color:var(--accent)}
.fl-arrow-back em{color:var(--grey-2)}

/* what it reads, and what it never keeps */
.fl-logo{height:19px;width:auto;color:var(--navy);display:block;margin-bottom:.7rem}
.fl-cols{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,22px)}
.fl-cols ul{list-style:none;margin:0;padding:0;display:grid;gap:.26rem;font-size:.8125rem}
.fl-keep ul{color:var(--navy)}
.fl-never ul li{color:var(--grey-2);text-decoration:line-through;
  text-decoration-color:var(--flag);text-decoration-thickness:1.5px}
.fl-never .fl-k{color:var(--flag)}

.sec-avv{margin:var(--sp-3) 0 0;font-size:.8125rem;color:var(--grey-2)}

@media (max-width:860px){
  .fl{grid-template-columns:1fr;gap:var(--sp-2)}
  .fl-mid{justify-items:center;gap:var(--sp-4);padding-block:.4rem}
  .fl-arrow{width:min(260px,70%)}
  .fl-arrow svg{transform:rotate(90deg) scaleX(.5);transform-origin:center}
  .fl-arrow em{position:static;display:block;text-align:center;margin-top:.2rem}
  .fl-head{display:none}
  .fl-cols{grid-template-columns:1fr 1fr}
}
@media (prefers-reduced-motion: reduce){
  .fl-arrow{--go:1 !important}
}
.nf-links{list-style:none;margin:var(--sp-4) 0 0;padding:0;display:grid;gap:.6rem}
.nf-links a{display:inline-flex;align-items:center;min-height:44px;color:var(--accent);
  font-size:var(--fs-body)}
/* The anchors landed a few pixels under the bar on a phone, because --nav-h is
   measured once and the bar is a little shorter there. A small cushion costs
   nothing and stops a heading from hiding behind the glass. */
section[id]{scroll-margin-top:calc(var(--nav-h,76px) + 10px)}
/* The Getting started rows dim to show which one is being read. On the old
   navy ground 0.72 was fine; on white it took a 6:1 grey down to about 4.4,
   so the floor is higher and the lit state still reads as lit. */

/* ══════════ ROUND 8 · the operations widgets, sized fluidly ══════════
   These were tuned at two height breakpoints, 840px and 760px. That left the
   band between about 850 and 960 at full size inside a frame that had already
   shrunk, so the widgets clipped at 1024x864, 1280x864 and 1280x900 while
   720, 800 and 1000 all looked fine. Every internal dimension is a clamp
   against viewport height now, so the content shrinks continuously with the
   room the pinned frame actually has, and there is no band to fall into.
   The 24px inset is the one value that never moves. */
@media (min-width:861px){
  .ops-dgm{padding:24px !important}
  .ow-lab,.ow-draw dt{font-size:clamp(.4375rem,.92vh,.5938rem)}
  .ow-h{margin-bottom:clamp(.2rem,.55vh,.6rem)}
  .ow-lab{margin-bottom:clamp(.2rem,.55vh,.55rem)}
  .ow-checks{gap:clamp(.04rem,.2vh,.22rem) .8rem}
  .ow-checks li{font-size:clamp(.4688rem,1.02vh,.6875rem)}
  .ow-checks i{width:clamp(8px,1.3vh,12px);height:clamp(8px,1.3vh,12px)}
  .ow-rank{gap:clamp(.14rem,.44vh,.42rem)}
  .ow-rank span{font-size:clamp(.5rem,1.02vh,.6875rem);line-height:1.15}
  .ow-rank b{font-size:clamp(.5rem,1.02vh,.6875rem)}
  .ow-rank i{height:clamp(3px,.55vh,5px)}
  .ow-days{height:clamp(16px,4.2vh,44px)}
  .ow-days em{font-size:clamp(.375rem,.8vh,.5rem)}
  .ow-cause{margin-top:clamp(.2rem,.55vh,.6rem);padding-top:clamp(.2rem,.5vh,.5rem);
    font-size:clamp(.4688rem,.98vh,.6875rem)}
  .ow-draw{gap:clamp(.12rem,.4vh,.3rem)}
  .ow-draw dd{font-size:clamp(.5625rem,1.25vh,.8125rem)}
  .ow-diff{padding-top:clamp(.14rem,.4vh,.3rem)}
  .ops-col .micro{font-size:clamp(.4375rem,.9vh,.5625rem)}
  .ops-col h3{font-size:clamp(.8125rem,1.85vh,1.0625rem);line-height:1.2}
  .ops-body{font-size:clamp(.625rem,1.38vh,.8125rem);line-height:1.45}
  .ops-ico{height:clamp(26px,4.6vh,46px);margin-bottom:clamp(.25rem,.9vh,.8rem)}
  .ops-ico .icn{width:clamp(26px,4.6vh,46px);height:clamp(26px,4.6vh,46px);
    border-radius:clamp(7px,1.4vh,13px)}
  .ops-ico .icn svg{width:clamp(14px,2.5vh,24px);height:clamp(14px,2.5vh,24px)}
}
/* Between 861 and 1120 the four cards are at their narrowest, so every label
   wraps onto more lines than it does at 1440 and the same clamps run out of
   room. The narrow range gets its own, tighter set. */
@media (min-width:861px) and (max-width:1120px){
  .ow-checks li{font-size:clamp(.4375rem,.88vh,.5938rem)}
  .ow-rank span,.ow-rank b{font-size:clamp(.4375rem,.9vh,.5938rem)}
  .ow-days{height:clamp(14px,3.4vh,34px)}
  .ow-draw dd{font-size:clamp(.5rem,1.05vh,.6875rem)}
  .ow-lab,.ow-draw dt,.ops-col .micro{font-size:clamp(.4063rem,.8vh,.5rem)}
  .ow-cause{font-size:clamp(.4375rem,.85vh,.5625rem)}
  .ops-col h3{font-size:clamp(.75rem,1.6vh,.9375rem)}
  .ops-body{font-size:clamp(.5938rem,1.2vh,.75rem)}
  .ops-ico{height:clamp(24px,3.8vh,36px);margin-bottom:clamp(.2rem,.7vh,.5rem)}
  .ops-ico .icn{width:clamp(24px,3.8vh,36px);height:clamp(24px,3.8vh,36px)}
  .ops-ico .icn svg{width:clamp(13px,2.1vh,19px);height:clamp(13px,2.1vh,19px)}
}

/* ══════════ ROUND 9 · the Getting started rail, visible on white ══════════
   The segments and the numeral chips survived from the navy build but were
   painted in --line-dark (white at 18%) and --accent-lift, so on white there
   was nothing to see: the line from 1 to 2 to 3 to 4 had simply vanished.
   Same colours, re-pointed at the tokens that mean something on this ground.
   ::before is the track, ::after the fill, so the progressive draw survives. */
.step-row:not(:last-child)::before{background:var(--line)}
.step-row:not(:last-child)::after{background:var(--accent)}
.step-n{
  background:color-mix(in srgb,var(--accent) calc(var(--snap,0) * 100%),var(--paper));
  border:1px solid color-mix(in srgb,var(--accent) calc(var(--snap,0) * 100%),var(--line));
  color:color-mix(in srgb,var(--paper) calc(var(--snap,0) * 100%),var(--grey-2))}
/* The row moved back under the intro text, which makes the head taller and
   gives the cards less of the one viewport the pin has. The clamps lose
   roughly 30px to pay for it, and the row itself is compact in its column. */
@media (min-width:861px){
  .ops-systems{margin-top:clamp(8px,1.4vh,18px);padding-top:clamp(8px,1.4vh,18px);
    gap:clamp(6px,.9vh,11px) clamp(12px,1.4vw,22px)}
  .opl{height:clamp(12px,2vh,17px)}
  .opl-mark{height:clamp(10px,1.7vh,14px)}
  .opl-wm{font-size:clamp(.625rem,1.35vh,.875rem)}
  .opl-wm-sm{font-size:clamp(.5rem,1.1vh,.6875rem)}
  .opl-plus{font-size:clamp(.4375rem,.88vh,.5625rem);padding:.24rem .5rem}
  .ow-checks li{font-size:clamp(.4375rem,.9vh,.625rem)}
  .ow-rank span,.ow-rank b{font-size:clamp(.4375rem,.92vh,.625rem)}
  .ow-rank{gap:clamp(.1rem,.36vh,.34rem)}
  .ow-days{height:clamp(14px,3.4vh,36px)}
  .ow-draw dd{font-size:clamp(.5rem,1.1vh,.75rem)}
  .ow-draw{gap:clamp(.08rem,.32vh,.24rem)}
  .ow-cause{font-size:clamp(.4375rem,.88vh,.625rem);margin-top:clamp(.16rem,.45vh,.5rem);
    padding-top:clamp(.16rem,.4vh,.42rem)}
  .ops-body{font-size:clamp(.5938rem,1.26vh,.75rem)}
  .ops-col h3{font-size:clamp(.75rem,1.7vh,.9375rem)}
  .ops-ico{height:clamp(24px,4vh,40px);margin-bottom:clamp(.2rem,.7vh,.6rem)}
  .ops-ico .icn{width:clamp(24px,4vh,40px);height:clamp(24px,4vh,40px)}
  .ops-ico .icn svg{width:clamp(13px,2.2vh,21px);height:clamp(13px,2.2vh,21px)}
}
/* 1024 is where the four cards are narrowest and every label wraps most, so
   it needs one more notch off the same clamps. */
@media (min-width:861px) and (max-width:1120px){
  .ow-checks li{font-size:clamp(.4063rem,.8vh,.5312rem)}
  .ow-rank span,.ow-rank b{font-size:clamp(.4063rem,.82vh,.5312rem)}
  .ow-days{height:clamp(12px,2.8vh,28px)}
  .ow-draw dd{font-size:clamp(.4688rem,.95vh,.625rem)}
  .ow-cause{font-size:clamp(.4063rem,.78vh,.5312rem)}
  .ops-body{font-size:clamp(.5625rem,1.12vh,.6875rem)}
  .ops-col h3{font-size:clamp(.6875rem,1.5vh,.8125rem)}
  .ops-ico{height:clamp(22px,3.4vh,32px)}
  .ops-ico .icn{width:clamp(22px,3.4vh,32px);height:clamp(22px,3.4vh,32px)}
  .ops-ico .icn svg{width:clamp(12px,1.9vh,17px);height:clamp(12px,1.9vh,17px)}
}

/* ══════════ ROUND 9 · security holds the page while it draws ══════════
   It used to draw once on an IntersectionObserver, which meant the arrows
   were often already finished by the time the section was properly on screen.
   It pins like How it works now: the flow is a function of scroll, so it
   draws as you come down and undraws as you go back up. */
.secp{position:relative;height:var(--secp-scroll,190vh)}
.secp-frame{position:sticky;top:var(--nav-h,76px);
  height:calc(100svh - var(--nav-h,76px));box-sizing:border-box;
  display:flex;align-items:center}
.secp-frame > .wrap{width:min(1280px,100% - var(--gut) * 2)}
#security{padding-block:0;overflow-x:clip}
/* the two arrows draw one after the other across the pin */
.fl-arrow{--go:0}
@media (max-width:1023px), (pointer:coarse){
  .secp{--secp-scroll:auto;height:auto}
  .secp-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
}
@media (prefers-reduced-motion: reduce){
  .secp{--secp-scroll:auto;height:auto}
  .secp-frame{position:static;height:auto;display:block;padding-block:var(--sec-pad)}
  .fl-arrow{--go:1}
}
