/* ============================================================
   NDot AI — shared base

   Tokens, type scale, components, motion and the review switcher.
   Everything here is the SAME across rounds: it is what makes the
   rounds read as one brand. The LAYOUT layer that follows this
   block is what differs, and it is the only thing that differs.

   No map, no geography, no dot field, no gradient, no shadow.
   Contrast ratios are computed (WCAG 2.1), not eyeballed.
   ============================================================ */
/* Synonym Semibold, Indian Type Foundry via Fontshare, self-hosted so the
   header name renders without a third font origin. Used for the brand name
   only. `font-display:block`: a logotype should wait for its face, not flash
   in Archivo and then reflow. */
@font-face{font-family:Synonym;font-style:normal;font-weight:600;font-display:block;
           src:url(assets/synonym-semibold.woff2) format("woff2")}
:root{
  --paper:#FAFAF8;
  --paper-2:#F1F1EC;
  --ink:#111110;          /* 18.08:1 on paper */
  --ink-2:#6B6B63;        /*  5.14:1 on paper */
  --rule:#D8D8CE;
  --plate:#C9C9C0;

  --ink-field:#131310;
  --on-ink:#F4F4EE;       /* 16.86:1 on the band */
  --on-ink-2:#9A9A90;     /*  6.56:1 on the band */
  --rule-ink:#6A6A63;     /*  3.38:1, clears SC 1.4.11 */

  /* Editorial chrome, not a brand colour. NEVER use --accent inside an
     inverted band: #A6231A on --ink-field is 2.55:1. Use --accent-ink. */
  --accent:#A6231A;       /* 6.98:1 on paper */
  --accent-ink:#E86A52;   /* 5.80:1 on the band */

  --serif:Newsreader,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
  --sans:Archivo,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --face:var(--serif);
  --brand-face:Synonym,var(--sans);

  --pad:clamp(20px,4vw,64px);
  --section:clamp(56px,7vw,104px);
}

*,*::before,*::after{box-sizing:border-box;border-radius:0}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--face);font-size:17px;line-height:1.62;font-weight:400;
  font-variant-numeric:lining-nums tabular-nums;-webkit-font-smoothing:antialiased;
}
@media (min-width:768px){ body{font-size:19px} }
img,svg{display:block;max-width:100%}
a{color:inherit}
p{margin:0}
a,.btn{transition:color .12s ease,background-color .12s ease,border-color .12s ease}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

.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}
.skip{position:absolute;left:-9999px;top:0;background:var(--ink);color:var(--on-ink);padding:12px 18px;z-index:99}
.skip:focus{left:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.on-ink :focus-visible,.band-ink :focus-visible{outline-color:var(--accent-ink)}
main:focus{outline:none}

/* ---------- the system layer (mono) ---------- */
.label{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.14em;
       text-transform:uppercase;color:var(--ink-2);line-height:18px;margin:0}
.on-ink .label,.band-ink .label{color:var(--on-ink-2)}
.folio{font-family:var(--mono);font-size:12px;font-weight:500;letter-spacing:.06em;color:var(--accent);margin:0}
.on-ink .folio,.band-ink .folio{color:var(--accent-ink)}

/* ---------- display type ---------- */
h1,h2,h3{margin:0;font-family:var(--face);font-weight:300;text-wrap:balance}
.h-xl{font-size:clamp(34px,1.2rem + 3.4vw,62px);line-height:1.02;letter-spacing:-.022em}
.h-l{font-size:clamp(28px,1rem + 2.2vw,44px);line-height:1.06;letter-spacing:-.018em}
.h-m{font-size:clamp(21px,1rem + 1vw,28px);line-height:1.18;letter-spacing:-.01em;font-weight:400}
.deck{font-size:clamp(19px,1rem + .7vw,24px);line-height:1.44;font-weight:300;color:var(--ink-2);margin:0}
.on-ink .deck,.band-ink .deck{color:var(--on-ink-2)}

/* ---------- buttons ---------- */
.actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
.btn{display:inline-flex;align-items:center;text-decoration:none;
     font-family:var(--mono);font-size:12px;font-weight:500;letter-spacing:.1em;
     text-transform:uppercase;height:48px;padding:0 22px;line-height:18px;
     border:1px solid var(--ink)}
.btn-solid{background:var(--ink);color:var(--paper)}
.btn-solid:hover{background:var(--accent);border-color:var(--accent);color:var(--paper)}
.btn-ghost:hover{background:var(--ink);color:var(--paper)}
.on-ink .btn,.band-ink .btn{border-color:var(--on-ink);color:var(--on-ink)}
.on-ink .btn-solid,.band-ink .btn-solid{background:var(--on-ink);color:var(--ink-field)}
.on-ink .btn-solid:hover,.band-ink .btn-solid:hover{background:var(--accent-ink);border-color:var(--accent-ink);color:var(--ink-field)}
.on-ink .btn-ghost:hover,.band-ink .btn-ghost:hover{background:var(--on-ink);color:var(--ink-field)}

.band-ink{background:var(--ink-field);color:var(--on-ink)}
.band-ink h1,.band-ink h2,.band-ink h3{color:var(--on-ink)}

/* ---------- record components ----------
   The internals are shared; each layout supplies its own container grid. */
.stat .ord{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.1em;
           color:var(--accent);display:block;margin-bottom:12px}
.on-ink .stat .ord,.band-ink .stat .ord{color:var(--accent-ink)}
.stat .fig{font-size:clamp(30px,1rem + 2.6vw,50px);font-weight:300;letter-spacing:-.02em;
           line-height:1;text-wrap:balance;color:inherit}
.stat .label{display:block;margin-top:auto;padding-top:18px}

.cap-num{font-family:var(--mono);font-size:12px;font-weight:500;color:var(--accent);letter-spacing:.06em}
.on-ink .cap-num,.band-ink .cap-num{color:var(--accent-ink)}
.cap-name{font-size:clamp(20px,1rem + .9vw,28px);font-weight:400;letter-spacing:-.012em;line-height:1.2}

/* The photo slot is a PRE-PRESS PLATE: the frame is set, the image is not
   placed yet. Crop marks read as a stage in production, not a broken image. */
.plate{position:relative;aspect-ratio:4/5;background:var(--paper-2);
       border:1px solid var(--rule);display:grid;place-items:center}
.plate .num{font-size:clamp(38px,6vw,60px);font-weight:300;color:var(--plate);letter-spacing:.02em;line-height:1}
.plate i{position:absolute;width:11px;height:11px;--cm:#8F8F86}
.plate .tl{top:8px;left:8px;border-top:1px solid var(--cm);border-left:1px solid var(--cm)}
.plate .tr{top:8px;right:8px;border-top:1px solid var(--cm);border-right:1px solid var(--cm)}
.plate .bl{bottom:8px;left:8px;border-bottom:1px solid var(--cm);border-left:1px solid var(--cm)}
.plate .br{bottom:8px;right:8px;border-bottom:1px solid var(--cm);border-right:1px solid var(--cm)}
.person .name{margin-top:18px;font-size:20px;font-weight:400;letter-spacing:-.01em;line-height:1.2}
.person .role{margin-top:6px;font-family:var(--mono);font-size:11px;font-weight:500;
              letter-spacing:.12em;text-transform:uppercase;color:var(--accent)}
.on-ink .person .role,.band-ink .person .role{color:var(--accent-ink)}
.person .bio{margin-top:12px;font-size:15px;line-height:1.55;color:var(--ink-2)}
.on-ink .person .bio,.band-ink .person .bio{color:var(--on-ink-2)}
.person .name,.person .role,.person .bio,.mailto{overflow-wrap:break-word}

.mailto{display:inline-block;font-weight:400;letter-spacing:-.012em;text-decoration:none;
        font-size:clamp(21px,1rem + 1vw,30px);border-bottom:1px solid currentColor;padding-bottom:6px}
.on-ink .mailto,.band-ink .mailto{color:var(--on-ink);border-bottom-color:var(--rule-ink)}
.on-ink .mailto:hover,.band-ink .mailto:hover{border-bottom-color:var(--accent-ink);color:var(--accent-ink)}

/* ---------- careers prose ---------- */
.prose p{color:var(--ink-2);line-height:1.68}
.on-ink .prose p,.band-ink .prose p{color:var(--on-ink-2)}
.pull{font-size:clamp(25px,1rem + 1.9vw,40px);font-weight:300;line-height:1.16;
      color:var(--ink)!important;letter-spacing:-.014em}
.on-ink .pull,.band-ink .pull{color:var(--on-ink)!important}
.contact-block .mailto{color:var(--ink);border-bottom-color:var(--ink-2);margin-top:12px}
.contact-block .mailto:hover{color:var(--accent);border-bottom-color:var(--accent)}

/* ---------- colophon / footer ---------- */
.site-foot{background:var(--ink-field);color:var(--on-ink);border-top:1px solid var(--rule-ink);
           padding-block:48px}
.site-foot .wrap{max-width:1312px;margin-inline:auto;padding-inline:var(--pad)}
.foot-grid{display:flex;flex-wrap:wrap;gap:30px;justify-content:space-between;align-items:flex-start}
.site-foot svg{height:19px;width:auto;color:var(--on-ink)}
.foot-links{display:flex;flex-wrap:wrap;gap:12px 26px}
.foot-links a{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
              color:var(--on-ink-2);text-decoration:none}
.foot-links a:hover{color:var(--on-ink)}
.colophon{margin-top:36px;padding-top:18px;border-top:1px solid var(--rule-ink);
          display:flex;flex-wrap:wrap;gap:6px 24px;justify-content:space-between}
.colophon span{font-family:var(--mono);font-size:10px;letter-spacing:.1em;color:var(--on-ink-2)}

/* ============================================================
   MOTION: the drift-in

   FAIL-OPEN CONTRACT. A load-time animation, never a scroll-driven
   one: no requestAnimationFrame, no IntersectionObserver, no JS.
   Every keyframe starts AND ends at the resting state, so if the
   animation never runs the copy is simply there. Killed under
   prefers-reduced-motion and in the gallery cards.
   ============================================================ */
@media (prefers-reduced-motion:no-preference){
  .h-xl,.deck,.actions,.stat,.cap-row,.mv > div,.person,.founder,
  .prose > *,.sec-head,.grid-people,.site-foot .foot-grid,.site-foot .colophon{
    animation:drift-in .95s cubic-bezier(.22,.68,.24,1) both;
  }
  @keyframes drift-in{
    from{opacity:0;transform:translateY(15px);filter:blur(9px)}
    55% {filter:blur(0)}
    to  {opacity:1;transform:none;filter:none}
  }
}
/* Cards are framed and lazy-loaded, so a card caught mid-drift smears its
   punctuation. In the gallery every element is pinned to its finished state. */
body.embed .h-xl,body.embed .deck,body.embed .actions,body.embed .stat,
body.embed .cap-row,body.embed .mv > div,body.embed .person,body.embed .founder,body.embed .prose > *,
body.embed .sec-head,body.embed .grid-people,body.embed .site-foot .foot-grid,
body.embed .site-foot .colophon{animation:none}

/* ============================================================
   REVIEW-ONLY VERSION SWITCHER

   Not part of the design. Two pairs: direction and round; the lit
   cell of each pair is where you are. Its own colours rather than
   the theme's so it looks identical on the light and dark rounds.
   Hidden in the gallery cards and in print. Strip before launch:
   search for "vswitch".
   ============================================================ */
.vswitch{position:fixed;right:16px;bottom:16px;z-index:200;display:flex;max-width:calc(100vw - 32px);
  background:#111110;border:1px solid #52524A;
  font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.1em;text-transform:uppercase}
.vswitch a{display:flex;align-items:center;color:#C4C4B9;text-decoration:none;
  padding:0 11px;min-height:34px;white-space:nowrap;
  transition:background-color .12s ease,color .12s ease}
.vswitch a:hover{background:#2B2B24;color:#FFF}
.vswitch a.is-on{background:#F4F4EE;color:#111110}
.vswitch a:focus-visible{outline:2px solid #E86A52;outline-offset:-2px;position:relative}
.vswitch-round{border-left:1px solid #52524A}
body.embed .vswitch{display:none}
@media print{.vswitch{display:none}}
@media (max-width:600px){
  .vswitch{right:10px;bottom:10px;max-width:calc(100vw - 20px)}
  .vswitch a{min-height:44px;padding:0 10px}
  .vswitch-dir{letter-spacing:.06em}
}

/* ---------- mobile floor ----------
   Measured on a touch context, not eyeballed: 44px targets, the mark never
   squeezed out of the header, and mono labels never scaled down. */
.brand{flex:0 0 auto}
@media (max-width:600px){
  .brand{padding-block:11px}      /* 22px of mark + 22px of padding = the 44px target */
  .nav a{padding-block:15px}
  .nav a.is-cta{padding:15px 14px}  /* the CTA's own padding outranks .nav a */
  .foot-links a{padding-block:15px}
  /* the address is a link, so it needs a 44px target. Padding-top is
     invisible (the underline hangs off padding-bottom) and buys the height. */
  .mailto{padding-block:12px}
  section[id]{scroll-margin-top:18px}
}

/* The review switcher is `position:fixed` and sits over the last line of the
   colophon at phone widths. Measured at 320 and 390 it covered "ndot.ai" and
   "(c) 2026 NDot AI" on every page. This padding buys the footer clearance and
   goes away with the switcher: strip it when you strip "vswitch". */
.site-foot{padding-bottom:72px}          /* the bar occupies the bottom 50px */
@media (max-width:600px){
  .site-foot{padding-bottom:114px}       /* and 54px there, on a taller bar */
}

/* ============================================================
   SCROLL REVEAL: section drift

   FAIL-OPEN. The hidden state only exists once the inline page
   script has stamped `jsrv` on <html>, so no-JS renders every
   word, and only under prefers-reduced-motion:no-preference.
   Revealed sections settle to transform:none so position:sticky
   children behave at rest. Print forces everything visible.
   ============================================================ */
@media (prefers-reduced-motion:no-preference){
  html.jsrv .srv{opacity:0;transform:translateY(16px)}
  html.jsrv .srv.srv-in{opacity:1;transform:none;
    transition:opacity .65s ease-out,transform .65s ease-out}
}
@media print{.srv{opacity:1!important;transform:none!important}}

/* ============================================================
   LAYOUT: PANELS  (Systems, round 3)

   One screen at a time. Against the other systems rounds the
   change is structural:

     R1  stacked sections, sticky rail per section, one column
     R2  fixed split, a pinned pane and a streaming pane
     R3  every section is its own full-viewport panel, composed
         to fill it, and the page advances a screen at a time.
         There is no continuous column of text anywhere: each
         panel is a self-contained plate with its own geometry.

   Scroll snapping is MANDATORY, and it is off below 900px wide
   or 680px tall, because a phone is where a viewport-locked
   panel becomes a trap. Panels use min-height, so a panel that
   needs more room simply takes it. See the snap-track block
   below for why mandatory and what keeps it honest.
   ============================================================ */
:root{
  --face:var(--sans);
  --paper:#0C0C0A;
  --paper-2:#19191600;
  --ink:#F4F4EE;          /* 17.6:1 on the ground */
  --ink-2:#A3A399;        /*  7.9:1 on the ground */
  --rule:#45453E;         /*  2.03:1 -- MEASURED. The old comment here said
                             3.0:1 and it was wrong. Fine for a decorative
                             hairline, which is all it was ever used for;
                             NOT enough for anything a reader has to see. */
  --rule-2:#63635A;       /*  3.2:1, clears SC 1.4.11. The unlit half of the
                             dot rails: those dots ARE the "every rail is
                             100" claim, so they have to be visible, and at
                             --rule they were a smudge. 2.1:1 against the lit
                             dots, which read apart on hue as well. */
  --plate:#45453E;

  --ink-field:#050504;
  --on-ink:#F4F4EE;
  --on-ink-2:#9A9A90;
  --rule-ink:#45453E;

  --accent:#FF6A52;       /* 7.6:1 on the ground */
  --accent-ink:#FF6A52;

  --bar:56px;
}
body{font-size:16px;line-height:1.55}
@media (min-width:768px){ body{font-size:17px} }
h1,h2,h3{font-weight:500;letter-spacing:-.024em}
.h-xl{font-size:clamp(30px,1rem + 2.6vw,50px);line-height:1.06;letter-spacing:-.03em}
.h-l{font-size:clamp(26px,1rem + 1.8vw,38px);line-height:1.1}
.h-m{font-size:clamp(19px,1rem + .7vw,25px);line-height:1.24;font-weight:500}
.deck{font-weight:400;font-size:clamp(16px,1rem + .4vw,19px);line-height:1.5}

/* ---------- the snap track ----------
   Mandatory, and only where a full-viewport panel is a reasonable unit —
   off below 900px wide or 680px tall, because a phone is where a
   viewport-locked panel becomes a trap.

   Mandatory rather than proximity, now that nothing pins. With the cover
   stack gone the page scrolled like any other long page and the deck feel
   went with it; proximity was not enough to bring it back. Measured at
   1440x900, a half-screen scroll under proximity comes to rest wherever it
   stopped — half of one panel above half of the next — where the same
   scroll under mandatory resolves onto a panel. That resolution is the
   whole difference.

   `scroll-snap-stop:always` is deliberately NOT set. It would make every
   panel an unskippable stop, and across seven panels that is a lot of
   separate gestures to reach the foot of the page.

   What keeps mandatory honest is that EVERY block down the page is a snap
   area, not just the panels. A mandatory track with a gap in it yanks you
   back out of the gap, which is how the footer and the careers article
   would otherwise become unreachable. The footer snaps to `end` — its
   bottom to the viewport bottom, because it is shorter than a screen and
   `start` would be a position the page can never reach — and the careers
   article snaps to `start`. Anything taller than the screen scrolls freely
   inside itself: a snap area larger than the snapport makes every position
   covering the snapport a valid one, the same rule that lets a long panel
   overrun the fold without being stranded.

   The whole track is opt-in, on `body.is-deck`. Only the home page asks to
   be a deck; a page that does not ask for it — careers today, anything
   added later — is a plain document and scrolls like one, rather than
   inheriting a deck behaviour it never wanted. `:has()` fails open: where
   it is unsupported no page snaps, which is the safe direction. The
   viewport lock rides the same class, because `min-height:100dvh` is the
   other half of "this block is a screen". */
@media (min-width:900px) and (min-height:680px){
  html:has(body.is-deck){scroll-snap-type:y mandatory}
  body.is-deck .panel-full{scroll-snap-align:start;min-height:100dvh}
  body.is-deck .article{scroll-snap-align:start}
  body.is-deck .site-foot{scroll-snap-align:end}
}
.panel-full{
  display:grid;align-content:center;position:relative;
  padding:calc(var(--bar) + clamp(34px,5vw,64px)) var(--pad) clamp(40px,6vw,72px);
  border-bottom:1px solid var(--rule);
}
.plate-inner{max-width:1240px;margin-inline:auto;width:100%}

/* ---------- one panel, one screen ----------
   The panels used to pin and slide over one another. They no longer do:
   the transition sliced a plate's own text in half on the way past, which
   is a high price for a trick. Every panel is now plain flow — it scrolls
   up and off like a page, and the proximity snap above is the only thing
   that still favours landing on a whole one.

   Because nothing pins, nothing has to fit the viewport either: a panel
   that outgrows a short screen simply takes the room and the snap lets go.
   That removes the height carve-outs and the runtime measurement that used
   to keep the stack honest.

   One rule per junction, from `.panel-full`'s own bottom border. */

/* the panel's own folio, set large in the corner: on a page with no
   continuous column, the number is how you know where you are */
.panel-full > .stamp{
  position:absolute;top:calc(var(--bar) + clamp(16px,2.4vw,26px));right:var(--pad);
  font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-2);margin:0;
}
.panel-full > .stamp b{color:var(--accent);font-weight:500}

/* ---------- fixed chrome ---------- */
.topbar{position:fixed;top:0;left:0;right:0;height:var(--bar);z-index:60;
        background:var(--paper);border-bottom:1px solid var(--rule);
        display:flex;align-items:center;justify-content:space-between;
        gap:16px;padding-inline:var(--pad)}
/* The header brand is the mark plus the name set in live text, the way
   NewsBreak's own header pairs its mark with its wordmark. It was the mark
   alone at 17px, which read as a favicon beside 11px nav labels; before that
   it was the drawn lockup, whose two adjacent N's read as "NNdot" at that
   size. Live text solves both: the name is set in Synonym Semibold at -5%
   tracking (self-hosted, docs/assets), with a gap the drawn lockup lacked.
   The mark is sized to the cap height of the name, not the line box. */
/* `align-items:baseline`, not center: the svg has no baseline of its own, so
   flexbox synthesises one from its bottom edge, and that edge lands on the
   text baseline. The mark then stands on the same line the letters do. */
.brand{display:inline-flex;align-items:baseline;gap:9px;color:var(--ink);text-decoration:none}
.topbar .brand svg{height:26px;width:auto;color:var(--ink)}
.brand-name{font-family:var(--brand-face);font-weight:600;font-size:25px;letter-spacing:-.05em;
            line-height:1;color:var(--ink);white-space:nowrap}
/* The CTA sits beside the nav rather than inside it, in a `.bar-end` that
   carries the nav's own gap -- so the desktop bar is laid out exactly as it
   was when Careers was the sixth nav link, to the pixel. It had to come out
   of the nav for the phone, where the nav becomes a drawer and a call to
   action inside a drawer is a call to action most readers never see. */
.bar-end{display:flex;align-items:center;gap:clamp(13px,2.2vw,28px)}
.nav{display:flex;align-items:center;gap:clamp(13px,2.2vw,28px)}
.nav a,.bar-cta{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.12em;
       text-transform:uppercase;color:var(--ink-2);text-decoration:none;white-space:nowrap}
.nav a:hover{color:var(--ink)}
.bar-cta{color:var(--paper);background:var(--ink);padding:8px 13px}
.bar-cta:hover{background:var(--accent)}
.nav a[data-n]::before{content:none}
/* The drawer toggle. Off everywhere but the phone layer, which is the only
   place a drawer exists -- so the desktop bar is untouched by all of this. */
.nav-toggle{display:none;font-family:var(--mono);font-size:11px;font-weight:500;
       letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);
       background:none;border:1px solid var(--rule-2);cursor:pointer;
       align-items:center;padding:11px 14px}
.nav-toggle .lbl-b{display:none}
.nav-toggle:hover{color:var(--ink);border-color:var(--ink-2)}
.topbar.is-open .nav-toggle{color:var(--ink);border-color:var(--ink)}
.topbar.is-open .nav-toggle .lbl-a{display:none}
.topbar.is-open .nav-toggle .lbl-b{display:inline}

/* the tick rail: six marks down the left edge, one per panel */

/* ---------- opening panel ---------- */
.opening h1{max-width:20ch}
/* The opening carries the whole argument on one screen, so it is set a
   step above the rest of the page rather than at the shared h-xl size.
   The measures are in ch, so they grow with the type and the line breaks
   land exactly where they did at the smaller size. */
.opening .h-xl{font-size:clamp(36px,1rem + 3.6vw,68px);line-height:1.04;letter-spacing:-.032em}
.opening .deck{margin-top:clamp(20px,2.6vw,30px);max-width:56ch;color:var(--ink-2);
               font-size:clamp(18px,1rem + .78vw,25px);line-height:1.44}
.opening .actions{margin-top:clamp(26px,3.2vw,40px)}
.opening .rule-t{height:2px;background:var(--accent);width:76px;margin-bottom:clamp(24px,3vw,34px)}

/* Panel 02's three-cell `.stats` plate is gone. It carried "More than a
   decade / Tens of millions / Hundreds of millions", which are ranges, not
   figures; the plate now carries the counts themselves as figure rows. The
   `.stat` internals still live in the shared base for the other rounds --
   nothing in THIS layout uses them any more. */

/* ---------- line breaking ---------- */
.pull,.person .name{text-wrap:balance}
.deck,.prose p,.statement .deck,.person .bio{text-wrap:pretty}

/* ---------- panels 01 and 02: the routes, drawn ----------
   The two plates are a PAIR, and the pairing is the argument.

   Panel 01 draws three rented routes -- audience, a channel box, publisher --
   and thins the traffic on them. Panel 02 draws the same three lanes at the
   SAME colour and the SAME weight, with the same broken second leg, and adds
   a FOURTH: one straight horizontal segment across the bottom of the figure,
   audience to publisher, with no intermediary box and no bend. It is the only
   coloured line on either plate.

   BOTH PLATES RUN AUDIENCE (left) -> CHANNEL -> PUBLISHER (right), because
   what every figure on them counts is REFERRAL traffic, which is inbound to
   the publisher. They used to run publisher-first and so pointed against
   their own numbers. The reversal was done in the two svg LABELS and nowhere
   else: each drawing is a mirror about x=210, so the boxes carry no
   direction, and all of it lives in the travel -- solid dense leg one,
   dashed thin leg two, dots running left to right. That travel is unchanged
   (left to right is the reading direction), and mirroring the coordinates to
   "reverse" the plate would move the dashed leg to the left and claim that
   more arrives than sets out. So NOTHING below moved, and nothing below
   should move if the direction is ever revisited.

   Panel 02's boxes are not panel 01's coordinates any more. Its audience and
   publisher are TALL, spanning the figure, which is the whole structural
   point: a route can leave a tall box high up and go around, or leave it low
   down and go straight, and only then is a line labelled DIRECT actually
   direct. The old version bent down out of the left box, ran along the bottom
   and bent back up -- a detour, which is the opposite of the claim.

   If a later edit ever dims, dashes further, or removes search, social or
   other on panel 02 to make the direct route look better, the plate starts
   saying "NDot replaces your channels", which is the opposite of the offer.
   NDot is an add-on. The direct route wins by being visibly steady (see the
   dots below), never by the other three being spoiled. */

/* The routes sit one step below the boxes so a travelling dot crossing them
   has something to be brighter than. Both still clear 4.5:1 on the ground. */
.wire-rented{stroke:var(--ink-2)}
.wire-direct{stroke:var(--accent)}
.lbl-direct{fill:var(--accent)}
/* The second leg of every rented route -- channel to publisher -- is the same
   wire drawn broken, because what comes out the far side is intermittent. It
   is the static half of the travelling-dot argument: with the motion off the
   drawing still says that less arrives than sets out, which a second identical
   solid line would not. Same colour and same weight as leg one, so the
   contrast is unchanged and it prints.

   BOTH plates carry it now. Panel 02 used to keep its rented legs solid, back
   when its only claim was "a fourth route is added". It now also shows the
   three thinning, exactly as panel 01 does, so the static drawing has to say
   the same thing the dots do. This is panel 01's treatment carried across
   unchanged -- not an extra penalty applied to the rented routes on the plate
   that argues for the direct one. */
.wire-thinned{stroke-dasharray:8 5}

/* ---------- panels 01 and 02: the traffic ----------
   The client asked for the decline to be SHOWN, not stated: "sending dots
   from a faster and steady speed to smaller sparser speed". So the routes
   carry actual traffic. Dots leave the audience dense and quick; most are
   absorbed at the channel box and never come out; the few that do cross
   the far leg slowly, and the publisher end of the plate is visibly emptier
   than the audience end.

   ONE mechanism, used on both plates, to opposite ends. Panel 02 runs the
   same three lanes with the same `.tdot-out` / `.tdot-thru` classes, the
   same keyframes and the same :nth-child delays -- only the coordinates
   differ, and barely even those -- and adds a fourth
   lane on the direct route with `.tdot-direct`: evenly spaced, one constant
   speed, full strength end to end, every dot that departs arrives. Panel 01
   shows traffic dying. Panel 02 shows the same traffic dying on the same
   three lanes and one connection where it does not. A second, separate dot
   mechanism for panel 02 would have thrown that comparison away.

   CSS `offset-path` / `offset-distance`, NOT SMIL `<animateMotion>`. Two
   reasons, both about the fail-open contract this file keeps everywhere:
   SMIL cannot be gated by a CSS media query, so `prefers-reduced-motion`,
   `body.embed` and `@media print` could not switch it off (only hide it,
   with the timeline still running); and `path()` accepts a COMPOUND path,
   so a through-dot's route can be "leg one, a jump across the box, leg
   two" as ONE path with ONE distance, which is what lets a single dot make
   the whole journey and change speed halfway.

   Geometry lives out here; only the animation is behind the motion gate.
   The dots rest at opacity:0: with motion off they are not there at all,
   and the drawing underneath -- every box, both legs of every route, the
   direct line, the labels, the caption -- carries the argument on its own.

   Every ROUTED lane is symmetric (leg one and leg two are the same length),
   so leg one is exactly HALF of every compound path and one keyframe set
   drives all six of them across the two plates. Panel 01: 96.2 units on
   search and other, 66 on social. Panel 02: 96.3 on search, 68.4 on social
   and other. */
.tdot{fill:var(--ink);stroke:none;opacity:0;offset-rotate:0deg}
.tdots-search .tdot-out {offset-path:path("M98 105 L164 35")}
.tdots-search .tdot-thru{offset-path:path("M98 105 L164 35 M256 35 L322 105")}
.tdots-social .tdot-out {offset-path:path("M98 105 L164 105")}
.tdots-social .tdot-thru{offset-path:path("M98 105 L164 105 M256 105 L322 105")}
.tdots-other  .tdot-out {offset-path:path("M98 105 L164 175")}
.tdots-other  .tdot-thru{offset-path:path("M98 105 L164 175 M256 175 L322 105")}

/* Panel 02's lanes. Same class names, so every animation, delay and gate
   above applies unchanged; the coordinates are scoped to `.dgm-added` (the
   svg on that plate) so they out-specify panel 01's by one class and the two
   diagrams cannot read each other's geometry.

   The routed lanes fan out of a node in the UPPER half of the tall audience
   box (94,102) and back into the matching node on the tall publisher box
   (326,102) -- the routing happens up top, which is what leaves the bottom of
   the figure free for a line that does not route at all. No lane is level:
   search climbs 72 and drops back, social and other bow 24 up and 24 down, so
   all three visibly go AROUND and nothing but the direct route is straight.
   Leg lengths land within 4% of panel 01's, which is why the periods below
   are so nearly its own. */
.dgm-added .tdots-search .tdot-out {offset-path:path("M94 102 L158 30")}
.dgm-added .tdots-search .tdot-thru{offset-path:path("M94 102 L158 30 M262 30 L326 102")}
.dgm-added .tdots-social .tdot-out {offset-path:path("M94 102 L158 78")}
.dgm-added .tdots-social .tdot-thru{offset-path:path("M94 102 L158 78 M262 78 L326 102")}
.dgm-added .tdots-other  .tdot-out {offset-path:path("M94 102 L158 126")}
.dgm-added .tdots-other  .tdot-thru{offset-path:path("M94 102 L158 126 M262 126 L326 102")}
/* The direct lane: one segment, 232 units, both ends on a box edge, so a dot
   emerges from the audience and is taken in by the publisher with nothing in
   between to lose it at. Accent, because this is the one route NDot owns and
   the one coloured thing on the plate. */
.dgm-added .tdots-direct .tdot-direct{offset-path:path("M94 192 L326 192")}
.tdot-direct{fill:var(--accent)}

/* The `@supports` gate is the other half of the fail-open contract, and it
   is not optional: a browser without `offset-path` would ignore the paths,
   leave all forty-six dots stacked on the svg origin and then blink them
   there on the opacity keyframes -- a bug in the corner of the plate rather
   than a missing decoration. Behind the gate they stay at opacity:0 and the
   drawing is simply still, exactly as it is under reduced motion. */
@supports (offset-path:path("M0 0 L1 1")){
@media (prefers-reduced-motion:no-preference){
  /* Social's legs are 66 units to search's 96.2, so its period is scaled
     to 0.686 of theirs: same SPEED on every route, which is what makes the
     three read as one system of traffic rather than three timers. The
     phases are nudged apart so the streams never march in step. */
  .tdots       {--out:1.5s;--thru:6s;--ph:0s}
  .tdots-social{--out:1.03s;--thru:4.12s;--ph:-.19s}
  .tdots-other {--ph:-.62s}
  .tdot-out {animation:tdot-out var(--out) linear infinite var(--ph)}
  .tdot-thru{animation:tdot-thru var(--thru) linear infinite var(--ph)}
  /* Delays are fractions of the period, and NEGATIVE, so every stream is
     already populated on the first frame instead of filling up from empty.
     They are addressed by position: each group is four `.tdot-out` then
     two `.tdot-thru`, in that order. Reorder the circles and the rhythm
     goes with them. */
  .tdot-out:nth-child(2){animation-delay:calc(var(--ph) - var(--out) * .25)}
  .tdot-out:nth-child(3){animation-delay:calc(var(--ph) - var(--out) * .5)}
  .tdot-out:nth-child(4){animation-delay:calc(var(--ph) - var(--out) * .75)}
  .tdot-thru:nth-child(6){animation-delay:calc(var(--ph) - var(--thru) * .5)}
  /* Panel 02, same speed. 64.1 units a second is the constant BOTH plates
     hold, so a dot on the proof moves at exactly the speed of a dot on the
     shift and the two diagrams read as one system of traffic rather than as
     two timers. Every value is a leg length over that speed (96.3 on search,
     68.4 on social and other), and `--thru` stays 4x `--out` as it is above.
     All three lanes are named even though search lands back on the `.tdots`
     defaults: a one-class rule up there would otherwise beat a two-class one
     here, and leaving that win to chance is how a geometry change silently
     desynchronises a lane. */
  .dgm-added .tdots-search{--out:1.5s;--thru:6s;--ph:0s}
  .dgm-added .tdots-social{--out:1.07s;--thru:4.28s;--ph:-.19s}
  .dgm-added .tdots-other {--out:1.07s;--thru:4.28s;--ph:-.62s}
  /* The direct lane, and the whole point of the plate. 232 units at the same
     64.1 a second is 3.6s end to end; ten dots at tenth-of-a-period delays
     sit 23 units apart -- the same spacing the routed lanes leave the
     audience at -- and STAY 23 units apart, because the timing function is
     linear and the keyframes hold offset-distance to a straight 0 -> 100. No
     fade in, no fade out, no beat held at a box: nothing on this lane can
     lose a dot. That is the contrast the panel is made of. The routed lanes
     leave at this density and arrive at a fifth of it -- four dots in five
     gone at the channel, the survivor 2.7x slower -- and this one arrives
     exactly as it left. */
  .tdots-direct{--dir:3.6s}
  .tdot-direct{animation:tdot-direct var(--dir) linear infinite}
  .tdot-direct:nth-child(2) {animation-delay:calc(var(--dir) * -0.1)}
  .tdot-direct:nth-child(3) {animation-delay:calc(var(--dir) * -0.2)}
  .tdot-direct:nth-child(4) {animation-delay:calc(var(--dir) * -0.3)}
  .tdot-direct:nth-child(5) {animation-delay:calc(var(--dir) * -0.4)}
  .tdot-direct:nth-child(6) {animation-delay:calc(var(--dir) * -0.5)}
  .tdot-direct:nth-child(7) {animation-delay:calc(var(--dir) * -0.6)}
  .tdot-direct:nth-child(8) {animation-delay:calc(var(--dir) * -0.7)}
  .tdot-direct:nth-child(9) {animation-delay:calc(var(--dir) * -0.8)}
  .tdot-direct:nth-child(10){animation-delay:calc(var(--dir) * -0.9)}
  /* What leaves and does not arrive. One leg, and it is gone before it
     reaches the box: four of these depart per through-dot, so the stream
     out of the audience is roughly four times the stream into the
     publisher. That ratio is a PHENOMENON, not one of the four figures --
     none of them is "the search dropout", and nothing here is tuned to
     any published number. */
  @keyframes tdot-out{
    0%  {offset-distance:0%;opacity:0}
    14% {opacity:1}
    64% {opacity:1}
    93% {opacity:0}
    100%{offset-distance:100%;opacity:0}
  }
  /* What gets through. Leg one in the first quarter of the period (the
     same speed as the dots beside it), a beat held at the channel's door,
     then leg two over the remaining two thirds -- about 2.7x slower. It
     goes out at full strength at the publisher edge: taken in by the
     publisher, which is the one absorption on this plate that is good. */
  @keyframes tdot-thru{
    0%  {offset-distance:0%;opacity:0}
    6%  {opacity:1}
    25% {offset-distance:49.9%}
    33% {offset-distance:49.9%}
    97% {opacity:1}
    100%{offset-distance:100%;opacity:0}
  }
  /* What always arrives. Two stops, no opacity ramp between them: the dot is
     at full strength the instant it leaves the audience's edge and still at
     full strength when the publisher's edge takes it. `opacity:1` is written
     into BOTH stops on purpose -- the resting state of `.tdot` is opacity:0,
     and that is what makes these vanish under reduced motion, print and the
     gallery cards along with every other dot, leaving the drawn line to make
     the case on its own. */
  @keyframes tdot-direct{
    0%  {offset-distance:0%;opacity:1}
    100%{offset-distance:100%;opacity:1}
  }
}
}
body.embed .tdot{animation:none}

/* ---------- the careers article ----------
   The prose is held to a reading measure, which on a wide screen left the
   right half of the plate empty. Above 1000px the heading moves into a
   rail of its own and the column sits beside it, so the plate is used
   edge to edge and the measure is unchanged. The rail is sticky, so the
   heading stays with the passage it names. Below 1000px it stacks back. */
.article .plate-inner{max-width:1240px;margin-inline:auto}
@media (min-width:1000px){
  .article .plate-inner{display:grid;grid-template-columns:minmax(230px,1fr) minmax(0,2fr);
                        column-gap:clamp(36px,5vw,88px)}
  .article .sec-head{grid-column:1;position:sticky;top:calc(var(--bar) + 26px);align-self:start}
  .article .prose{grid-column:2;margin-top:0}
  .article .sec-head .h-l{max-width:12ch}
}

/* systems/3 ships without the review switcher, so it does not need the
   footer clearance the switcher bought in the shared base. */
.site-foot{padding-bottom:clamp(30px,4vw,48px)}
@media (max-width:600px){ .site-foot{padding-bottom:34px} }

/* The heading carries its own wayfinding: the label half is set in the
   accent so "Why Now" / "Why Us" reads as the answer to the nav item that
   brought you here, not as the first two words of the sentence. */
.h-l .why{color:var(--accent)}

/* ---------- panels 01 and 02: ONE ruled grid per plate ----------------
   ONE composition, used twice, because the two plates are an argument in
   two halves and they have to look like it.

   And ONE OBJECT per plate. This started as a bordered diagram box beside
   a loose stack of hairline-separated rows, and it read as two unrelated
   things with a gap down the middle. It is now a single cell grid in the
   idiom this stylesheet already uses elsewhere: the GUTTER IS THE RULE.
   `gap:1px` over a `--rule` background lets the ground show through as a
   hairline, every cell repaints itself in `--paper`, and one border closes
   the outside. Nothing inside carries a border of its own -- that is the
   whole point of the idiom, because per-cell borders double up at every
   junction and you get 2px seams everywhere two cells meet.

   The diagram is a CELL of that grid, spanning the figure rows down the
   left, so its right edge IS the first figure cell's left edge: one shared
   hairline, no gap, nothing floating.

   `> *` rather than a `.cell` class on purpose: a child that forgets the
   class would otherwise show the container's rule colour as a solid block
   instead of a background, which is a loud failure for a silent mistake. */
.plate-grid{display:grid;gap:1px;background:var(--rule);
            border:1px solid var(--rule);margin-top:clamp(26px,4.6vw,66px);
            /* the background IS the rules here, not decoration, so it has
               to survive a print that would otherwise drop backgrounds --
               without this the gutters print as nothing at all */
            -webkit-print-color-adjust:exact;print-color-adjust:exact}
.plate-grid > *{background:var(--paper);margin:0;min-width:0;
                padding:clamp(12px,1.5vw,19px) clamp(14px,1.7vw,24px)}
/* Two columns from 900px, which is exactly where the snap track and the
   viewport lock switch on. Stacking the diagram over the data anywhere
   inside that range makes a locked panel about twice the fold. Below 900
   nothing is locked, the grid runs as one column, and stacking is right.

   The row span is hard-coded and has to track the cell count: four figure
   cells beside the diagram on BOTH plates now. Add a figure, change the
   span. (`.has-lead`, a six-row span for the stacked version panel 02 used
   to run, went with the two cells the client cut and the 2x2 that replaced
   them. Nothing else on the site used it.) */
@media (min-width:900px){
  .plate-grid{grid-template-columns:minmax(0,0.84fr) minmax(0,1.16fr)}
  .plate-grid > .fig-cell{grid-column:1;grid-row:1 / span 4}
  /* Both plates are a 2x2 of figures beside the diagram, not a stack:
     THREE tracks, the two figure columns equal so the four numbers sit on
     two axes. The diagram spans the two rows the 2x2 makes. Panels 01 and
     02 are an argument in two halves and they now share the composition
     exactly -- same tracks, same span, same cell shape -- so the reader
     compares the two DIAGRAMS rather than two layouts. */
  .plate-grid.has-quad{grid-template-columns:minmax(0,0.88fr) minmax(0,0.56fr) minmax(0,0.56fr)}
  .plate-grid.has-quad > .fig-cell{grid-row:1 / span 2}
  /* Panel 02 carries five figures rather than four, so its figure column
     runs three rows deep and the diagram spans all three. The fifth cell
     leaves the sixth slot empty: the ground, not a filler tile. */
  .plate-grid.has-five > .fig-cell{grid-row:1 / span 3}
  /* The flagship figure takes both figure columns of row one; the four
     that follow auto-flow two to a row underneath it. This is what got
     rid of the empty sixth cell -- five figures in a 2x3 left a hole,
     five figures as 1 + 2 + 2 do not. */
  .plate-grid.has-five > .proof-lead{grid-column:2 / span 2}
  /* Spanning both columns gives the lead a full row of width, and stacked it
     was spending none of it: number, label and stamp in a narrow left column
     with the rest of the row empty. Horizontal instead -- figure on the left,
     label and stamp set beside it -- which is what the width is for, and it
     buys height back on a plate that overruns short screens, because the
     figure and its text now share vertical space instead of queueing.
     Grid rather than a flex row so no wrapper element is needed: the figure
     spans both text rows, and label and stamp keep their own. */
  .plate-grid.has-five > .proof-lead{display:grid;align-items:center;
      grid-template-columns:auto minmax(0,1fr);
      column-gap:clamp(20px,2.4vw,42px)}
  .has-five > .proof-lead .fig-num{grid-column:1;grid-row:1 / span 2}
  .has-five > .proof-lead .fig-lab{grid-column:2;grid-row:1;margin-top:0;align-self:end}
  .has-five > .proof-lead .fig-note{grid-column:2;grid-row:2;align-self:start}
}
/* Between the stack gate and 900 the plate is one column wide but still
   wide enough for a pair: the diagram takes the full width and the four
   figures hold their 2x2 under it, rather than becoming a four-deep stack
   of very large numbers. Below 600 it is one column, like everything. */
@media (min-width:600px) and (max-width:899px){
  .plate-grid.has-quad{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .plate-grid.has-quad > .fig-cell{grid-column:1 / span 2}
}
/* The diagram floats in the middle of its own cell when the figure column
   is the taller of the two, with the caption held to the bottom edge. */
.fig-cell{display:flex;flex-direction:column}
.fig-cell svg{width:100%;height:auto;color:var(--ink);margin-block:auto}
.fig-cell .cap{margin-top:14px;padding-top:11px;border-top:1px solid var(--rule)}
/* Below the two-column gate the figure cell spans the whole plate, and an
   svg at width:100% simply grows with it — which is how a 472px diagram
   became a ~750px one that swamped the figures under it. The diagram has a
   natural size; past it nothing is gained and the plate loses its balance.
   Cap it at the size it holds in the two-column layout and centre it. */
@media (max-width:899px){
  .fig-cell svg{max-width:520px;margin-inline:auto}
}
/* The diagram's own labels, on a phone. The svg meets its 520px cap from
   600px up, so from there the 10.5-unit labels render at 13 CSS px. Below
   that the drawing is squeezed by the plate — 320px at 390, 290px at 360 —
   and the SAME labels come out at 8px and 7.3px, smaller than any other
   type on the site (the period line under a figure is 10px). The drawing
   does not change; only the type inside it steps up, so it lands at 9.1px
   at 390 and 8.3px at 360.

   12 is the ceiling, and it is set by the TIGHTEST box on either plate:
   panel 02's end boxes are 88 units wide and one of them holds "PUBLISHER",
   whose box goes from 64.1 units to 72.3 at this size — 7.8 units of
   clearance, or
   about 6px at 390. 13 units would leave under 4 and the word would read
   as touching the rule. Panel 02's DIRECT label already carries its own
   font-size="12" attribute, so this selector sets it to the size it
   already had and that label does not move.

   In SVG content a CSS length is in user units, which is why this is
   `12px` and not a ratio: it is 12 of the viewBox's own units.

   The gate is 600, the step this file already has, and NOT the width where
   the svg actually stops shrinking, which is 598. So across the 598-600
   band the label is 14.9px where 601 gives 13px — 1.9px of glyph, over
   three pixels of viewport width, on a decorative label. A gate at 598
   would be a third breakpoint in a file that deliberately has two, and
   that trade is not worth making. */
@media (max-width:600px){
  .fig-cell svg text{font-size:12px}
}

/* ---------- the figure cells ----------
   `.fig-num` and `.fig-lab` are shared by both plates, and so is the CELL
   now: `.loss-cell` on the shift and `.proof-cell` on the proof are the
   same stacked block, four to a plate, in the same 2x2. They differ in
   exactly two things, and both are meaning rather than styling: the shift's
   figures are losses and take the accent, and they carry a period line
   under the label because four numbers from four studies are not additive.
   No padding and no borders in either: the plate-grid cell owns both.

   (Panel 02 used to run `.proof-row` / `.proof-lead` -- a number in a fixed
   track with the label beside it, six deep. Both classes went with the
   stacked layout; nothing else on the site used them.) */
.fig-num{font-size:clamp(25px,1rem + 1.4vw,36px);font-weight:500;line-height:1;
         letter-spacing:-.03em;color:var(--ink);white-space:nowrap}
.fig-num .pct{font-size:.6em}
.fig-lab{font-size:clamp(13px,.6rem + .3vw,15px);line-height:1.4;
         letter-spacing:-.008em;color:var(--ink)}

/* ---------- panel 01's four losses, as a 2x2 ----------
   These were rows: a number, a label and a rail of a hundred dots showing
   the same quantity a second time. The rails are GONE -- the dots moved
   into the diagram, where they are the traffic itself and mean something
   the number does not -- and with the rail out of the cell the figure is
   free to set at the size a headline figure wants. Number, label, period,
   stacked and centred in the cell; the period stays with its figure,
   because four numbers from four studies over four periods are not
   additive and a reader who cannot see the periods is being misled.
   Every figure here is a loss, so every one takes the accent.

   Panel 02's four proofs are the same block, at the same size, so the two
   plates read as a pair: with the room the deleted cells gave back, its
   figures set as large as the shift's rather than as a column of rows.
   They stay in `--ink`. They are not losses, and the accent is spent on
   the one thing this plate is arguing for -- the direct route and its
   stream, the only coloured thing in the drawing. */
.loss-cell,.proof-cell{display:flex;flex-direction:column;justify-content:center}
.loss-cell .fig-num,
.proof-cell .fig-num{font-size:clamp(34px,1rem + 2.6vw,58px)}
.loss-cell .fig-num{color:var(--accent)}
.loss-cell .fig-lab,
.proof-cell .fig-lab{font-weight:500;margin-top:clamp(8px,1vw,13px);
                    font-size:clamp(14px,.65rem + .4vw,17px);text-wrap:balance}
/* The annotation stamp. Panel 01 uses it for the period under a loss
   (`.loss-when`); panel 02 uses it to carry the one claim a figure needs
   beside it (`.fig-note`) -- what NewsBreak is, and that CrimeRadar is the
   newest launch and still climbing. A stamp rather than a longer label,
   deliberately: the claim gets its own typographic slot instead of trailing
   off the label as a comma clause, and the two DAU cells then set to the
   same three lines, which is the thing that makes them align. Both stay in
   `--ink-2` -- the accent on this plate is spent on the direct route in the
   drawing and nowhere else. */
.loss-when,.fig-note{font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.12em;
           text-transform:uppercase;color:var(--ink-2);margin-top:7px;line-height:1.5}

/* Below 600px everything is one column and the figures no longer have to
   fit two to a line, so they take a size that suits a stack. */
@media (max-width:600px){
  .fig-num{font-size:30px}
  .loss-cell .fig-num,.proof-cell .fig-num{font-size:34px}

  /* ---------- two rows, not three ----------
     Stacked, a cell spent three rows on figure, label and stamp, and the
     figure's row was mostly empty to the right of a four-character number.
     Two rows now: the figure and its label share the first, the stamp has
     the second. The label reads as a caption ON the number rather than a
     line under it, and the cell is shorter for it -- which on a phone is
     the whole plate's budget.

     Both plates, not just the losses. `.loss-cell` and `.proof-cell` are
     deliberately the same block so panels 01 and 02 read as a pair; giving
     one of them a different shape here would break the pairing at exactly
     the width where the two plates are most alike.

     Grid rather than a flex row, so no wrapper is needed around the label
     and stamp: the stamp simply spans both columns of the second row.

     `center`, not `baseline`. On a baseline the figure sat on the label's
     FIRST line, so a one-line label read centred and a two-line one read
     top-heavy -- which is what made 2,500+ and 1,500+ look wrong next to
     the rows above them. Centred, the figure holds the middle of whatever
     the label turns out to be, and the two cases match.

     The figure holds the left edge and the label the right, so every row
     has the same two anchors down the plate. It has to be `text-align`
     rather than a shrunk box: the label keeps the full column, so it wraps
     where it would have wrapped and only its setting changes. */
  .loss-cell,.proof-cell{display:grid;align-items:center;
       grid-template-columns:auto minmax(0,1fr);column-gap:14px}
  .loss-cell .fig-num,.proof-cell .fig-num{grid-column:1;grid-row:1}
  .loss-cell .fig-lab,.proof-cell .fig-lab{grid-column:2;grid-row:1;margin-top:0;
       text-align:right;text-wrap:pretty}
  .loss-when,.fig-note{grid-column:1 / -1;grid-row:2}
}

/* ---------- short screens ----------
   Panels 01 and 02 carry a diagram AND a column of figures, which is more
   than any other plate here, and they are the two that overran the fold
   first: measured at 1280x720 the shift ran 17px past it and the proof 7px.
   The fix is a type step, not a scroll -- same shape, one size down --
   which is what the founder plate already does at max-height:820px. The
   deck steps on `.plate-deck` rather than on `.sec-head .deck`, so a plate
   elsewhere that happens to nest a deck in its head is not dragged along. */
@media (min-width:900px) and (max-height:800px){
  .plate-grid{margin-top:clamp(18px,2.8vw,40px)}
  /* cell padding is the biggest single cost on these plates, and it is paid
     five times over on each of them. This is where the fold is won back. */
  .plate-grid > *{padding:clamp(6px,.95vw,12px) clamp(12px,1.5vw,20px)}
  .fig-cell .cap{margin-top:10px;padding-top:8px}
  .plate-deck{font-size:clamp(15px,1rem + .2vw,17px)}
  .fig-num{font-size:clamp(23px,1rem + 1.1vw,31px)}
  .loss-cell .fig-num,
  .proof-cell .fig-num{font-size:clamp(31px,1rem + 2.1vw,48px)}
  .loss-cell .fig-lab,
  .proof-cell .fig-lab{margin-top:clamp(6px,.7vw,10px)}
  .loss-when,.fig-note{margin-top:5px}
}

/* ---------- panel 03: four workstreams as frosted boxes over a gear set ----------
   The seven capability cells that stood here are gone. They were seven
   nouns and seven marks -- no verb, no promise -- and the client's audience
   read them as vague. The plate carries the four things NDot does instead,
   each as a verb set large with one sentence beside it.

   THE RULES BETWEEN THE ROWS ARE GONE AND THE ROWS ARE BOXES. They used to
   be four bands of a ruled list -- a hairline on `.work-list` above the
   first and one on each `.work-row` below. The client asked for boxes with
   the drawing blurring through them, so the rule IS the box edge now: the
   list lost its border, the row's `border-bottom` became a full `border`,
   and each row carries a frosted ground. Nothing separates the rows but the
   gap between two boxes, which is what the client meant by "remove the
   lines".

   FOUR ROWS, ONE COLUMN, AT EVERY WIDTH -- 4x1 stacked, not 1x4 across. A
   row of four across was built first and measured: at 1440 each box came out
   251px wide, which sets the longest sentence ("Automated marketing
   campaigns...", 121 characters) in five lines and reads as a column of
   confetti. One box per row hands each sentence the full measure and it
   sets in two lines at every locked viewport.

   THE GROUP IS NARROWER THAN THE PLATE: 800px against the plate's 1240 at
   1440. That is the whole point of the cap -- the drawing has to be visible
   BESIDE the boxes as well as through them, and a full-bleed band of frost
   covers the mechanism it is supposed to be frosting. 800 is not arbitrary
   either: `.work-say` holds a 62ch measure, which is 533px at 1440, and
   800 is the group width at which the label column, the gutter, the box
   padding and 62ch add up with nothing left over. Wider and the sentence
   stops short of its own box; narrower and it wraps to three lines. It is
   LEFT ALIGNED on the same edge as the heading and the deck, so the plate
   keeps one left margin and every pixel of slack falls on the right, which
   is where the gear set is. Below about 950px the plate is itself narrower
   than 800 and the group simply takes what there is.

   The label sits BESIDE its sentence from 601px up and above it below that.
   601 rather than 900 because the side-head only needs about 40 characters
   left over for the sentence, and a 601px plate still has them; a phone
   does not.

   COLOUR IS LOAD-BEARING HERE. The label and the sentence are --ink, not
   the --ink-2 this layout usually gives supporting copy, because the gear
   set passes behind them. Measured on the ground: --ink over a drawn line
   (--rule) is 8.76:1; --ink-2 over the same line is 3.80:1 and FAILS.
   THE DECK NOW TAKES --ink FOR THE SAME REASON. It used to be --ink-2 and
   that was safe only because the drawing was parked in the bottom-right
   corner, well clear of it. The drawing has moved up and across the plate
   to sit behind the boxes, it crosses the deck's measure on the way, and
   --ink-2 over it measured 3.74:1 on rendered pixels -- a fail. Two ways
   out: keep the drawing off the deck, or brighten the deck. Keeping it off
   costs the composition (the drawing then has to start below the deck,
   which crops the mechanism to its sparse top, or sit right of it, which
   leaves the first two boxes on bare ground). Brightening costs one step of
   tonal hierarchy and the panel had already made exactly this call for the
   row copy. Anything added to this plate that a reader must see goes to
   --ink, or the drawing goes away from underneath it.

   THE FOUR LABELS ARE SMALLER THAN THE PANEL HEADING. That is what the
   size below is for. `.h-l` runs clamp(26px,1rem + 1.8vw,38px); the labels
   ran clamp(25px,1rem + 1.55vw,38px), so at 1440 both landed on 38px and
   the plate carried two headings of equal rank. They are ~0.65 of the
   heading at every width now -- 24.93px against 38px at 1440, 19px against
   26px at 390 -- and the label column narrows to match. They also lost a
   word: "Engage and Retain" is now just "Retain", and one word a row can
   afford to be quieter and still name the row. */
#solution .plate-deck{color:var(--ink)}
.work{margin-top:clamp(28px,4.4vw,62px);max-width:min(100%,800px)}
.work-list{list-style:none;margin:0;padding:0;position:relative;z-index:1;
           display:grid;grid-template-columns:minmax(0,1fr);
           gap:clamp(8px,.8vw,13px)}
.work-row{padding:clamp(11px,1.05vw,16px) clamp(13px,1.25vw,19px);
          border:1px solid var(--rule-2);background:rgba(12,12,10,.9)}
.work-verb{margin:0;font-size:clamp(19px,1rem + .62vw,26px);line-height:1.12;
           font-weight:500;letter-spacing:-.018em;color:var(--ink)}
.work-say{margin:5px 0 0;max-width:62ch;color:var(--ink);text-wrap:pretty;
          font-size:clamp(15px,1rem + .18vw,17.5px);line-height:1.5}
@media (min-width:601px){
  /* baseline, not start: the label is a side-head and its baseline is the
     line the sentence starts on. */
  .work-row{display:grid;align-items:center;column-gap:clamp(18px,2.6vw,40px);
            grid-template-columns:clamp(96px,10vw,150px) minmax(0,1fr)}
  .work-say{margin-top:0}
}
/* On a phone these boxes were the largest body copy on the page -- 16.7px
   of sentence under a 19px verb, against a 15.5px panel deck and 14px
   figure labels. Nothing about this plate earns a bigger setting than the
   ones on either side of it, and four boxes at that size push the plate
   past a screen for no reason. The sentence takes the deck's size and the
   verb stays a clear step above it, which is the same relationship it has
   at every other width. */
@media (max-width:600px){
  .work-verb{font-size:18px}
  .work-say{font-size:15.5px}
}

/* ---------- the frost ----------
   Real frosted glass, not a picture of it: `backdrop-filter` blurs the gear
   drawing where it passes behind a box, so the mechanism is SHARP outside
   the pane and SOFT inside it. That difference across the box edge is the
   only thing that makes a translucent rectangle read as glass; a flat
   translucent fill on top of the same drawing reads as a smudge, because
   nothing about the backdrop changes at the boundary.

   Three numbers, and each is doing separate work:

     blur(9px)        big enough that a 1px hairline is unmistakably soft
                      inside the pane. Under about 5px the drawing just
                      looks badly rendered; over about 12px the hairlines
                      dissolve entirely -- a 1px line spread over 12px is
                      an eighth of its own contrast -- and the gear the
                      client asked to see through the glass is gone.
     brightness(1.75) the drawing is --rule on --paper, which is 2.03:1.
                      Blur it and the peak drops further. Without the lift
                      there is nothing left inside the pane to look at.
                      This is why the ratio below is measured and not
                      assumed: brightness raises the BACKDROP, which is the
                      one direction that costs contrast.
     rgba(...,.05)    a 5% white film. It is what makes the pane visible at
                      all where nothing is behind it -- at 390px the
                      drawing is gone and the film is the whole box.

   MEASURED, on rendered pixels, with the text hidden and the box decoded:
   the worst backdrop under any label or sentence is about #4a4a44, which
   against --ink is 8.4:1. There is a lot of room here and that is
   deliberate: the ground is near-black, so even a fully unfrosted --rule
   line under --ink is 8.76:1. The frost cannot fail this panel by being
   too transparent. It could fail it by being too BRIGHT, so if the
   brightness above ever goes up, re-measure rather than reason about it.

   The gate is `@supports`, and the fallback is the rule above it: an
   opaque-enough `rgba(12,12,10,.9)` ground and a --rule-2 edge. A browser
   without `backdrop-filter` gets four solid boxes over a drawing, which is
   the same panel with the trick removed -- not a broken one. Print drops
   both halves at the foot of this file. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .work-row{background:rgba(244,244,238,.05);border-color:rgba(244,244,238,.19);
            -webkit-backdrop-filter:blur(9px) brightness(1.75);
            backdrop-filter:blur(9px) brightness(1.75)}
}

/* ---------- the gear set behind the boxes ----------
   A flat field of thirteen 2D gears used to lie across this whole plate.
   It is now ONE axonometric gear set drawn in the register of a wireframe
   engineering drawing: shafts, pillow blocks, bearing collars, bores,
   keyways, lightening holes, centre lines, and a dashed line wherever a
   shaft runs behind a body.

   IT NO LONGER LIVES IN THE BOTTOM-RIGHT CORNER. It was parked there while
   the workstreams were a ruled list, because the list ran the full plate
   and the drawing had to keep out from under the copy. The workstreams are
   frosted boxes now and the drawing is what they are frosting, so it has
   moved into the middle of the plate and grown: 72vw against the old 52vw,
   and centred a little right of the panel's midline rather than hard into
   the corner. It crosses the box stack from top to bottom, and because the
   stack is capped at 800px and left aligned, the mechanism is also visible
   in the open ground to its RIGHT -- sharp there, soft inside the panes.
   That contrast across the box edge is the effect; see the frost block.

   The projection is in the markup, not here -- see the comment on the
   `<svg>` in index.html.

   IT STILL HANGS PAST AN EDGE. `bottom:-20%` runs a fifth of the drawing
   off the bottom of the panel at every width, so it reads as a mechanism
   larger than the frame rather than as a picture of one.
   `#solution{overflow:hidden}` is what stops the overhang from opening a
   horizontal scrollbar beside the vertical one -- it is load-bearing, not
   housekeeping.

   The overhang is in PERCENT of the panel, not vw, so the crop is the
   same fraction of the drawing at every width instead of swinging from a
   nick to a third of the mechanism between 900px and 1920px. The
   horizontal offset is a percent of the DRAWING (`translate`), for the
   same reason: the crop and the framing both stay proportional.

   `height:auto` is right here where it was wrong before. The old field
   used preserveAspectRatio=slice and needed both axes stated or it had
   nothing to crop; this one meets its viewBox, so the intrinsic ratio is
   the correct height and stating one would only distort it.

   Three ceilings on the width, and the VH ONE IS THE IMPORTANT ONE. The
   drawing is very nearly square, so width drives height: left on 72vw
   alone it stands over 1000px tall at 1280x720 and swallows the folio in
   the far corner. 104vh caps the height through the width, which is the
   lever that exists when `height` has to stay `auto`; `max-height` would
   clamp the box without touching the stated width and simply squash the
   mechanism. 1040px is the last stop, so a 1920px screen does not get a
   1400px machine behind four boxes; it binds on that screen alone, because
   below about 1450x1000 one of the other two is always smaller. There is
   still no width FLOOR, but the rim keywords have put a minimum legible
   size back into the drawing, so their type is sized against the drawing
   in the rim block below rather than left to scale with it. */
#solution{overflow:hidden}
#solution > .plate-inner{position:relative;z-index:1}
.work-gear{position:absolute;z-index:0;pointer-events:none;max-width:none;
           left:50%;right:auto;translate:-8% 0;bottom:-20%;
           width:min(72vw,104vh,1040px);height:auto}
/* Below the viewport lock the plate stacks and grows past a screen, the
   panel is no longer one fixed height, and a 72vw drawing on a tablet is a
   thumbnail of a mechanism. It takes a width nearer the plate's own and
   sits a little higher, so it still runs behind the box stack rather than
   under its last row alone. */
@media (max-width:899px){
  .work-gear{width:min(100vw,600px);translate:-26% 0;bottom:-10%}
}
/* Below 600px it used to go entirely, on the argument that a whole 390px
   mechanism behind four full-width boxes reads as dirt rather than as
   atmosphere. That was right about the whole mechanism and wrong about the
   drawing: what makes it atmosphere on a wide plate is that it is CROPPED
   -- the eye takes a fragment of a machine as ground and a complete little
   diagram as clutter. So it comes back scaled UP rather than down, half as
   wide again as the screen and pushed off the right edge, and what shows
   is a corner of a gear set rather than a model of one.

   Legibility was never the constraint here and still is not. The boxes
   frost whatever passes behind them, and that was measured on rendered
   pixels: the worst backdrop under any label or sentence is about #4a4a44,
   8.4:1 against --ink. The phone gets the same frost, so it gets the same
   ratio. Print still drops the drawing, at the foot of this file. */
@media (max-width:600px){
  .work-gear{width:min(152vw,600px);translate:-42% 0;bottom:-4%}
}

/* Hairlines, and they stay hairlines. `non-scaling-stroke` takes the
   stroke out of the user-unit space the drawing is authored in: the
   viewBox is 306 units wide and renders anywhere between 300 and 680px,
   so a plain stroke-width:1 would be a 1px line on a phone and a 2.2px
   line on a desktop -- the difference between a drawing and a diagram.
   The weights below are therefore CSS pixels at every size. It also means
   the dash patterns are in pixels, which is why they are short. */
.work-gear .gt,.work-gear .gh,.work-gear .gb,
.work-gear .gs,.work-gear .gp,.work-gear .gd{vector-effect:non-scaling-stroke}
.work-gear .gt{stroke:var(--rule);stroke-width:1.2}      /* part outline    */
.work-gear .gh{stroke:var(--rule);stroke-width:1}        /* bore, hub, shaft*/
.work-gear .gs{stroke:var(--rule);stroke-width:1}        /* bearing block   */
/* the far face of every body, one thickness down the axis: the same
   outline, lighter, so the pair reads as a cylinder rather than as two
   drawings on top of each other */
.work-gear .gb{stroke:var(--rule);stroke-width:.6}
/* pitch circles, axis centre lines and lines of centres are drafting
   construction, so they are drawn as construction: dash-dot, thinner */
.work-gear .gp{stroke:var(--rule);stroke-width:.6;stroke-dasharray:9 3 1.5 3}
/* hidden line: the stretch of shaft that runs inside a gear body */
.work-gear .gd{stroke:var(--rule);stroke-width:.6;stroke-dasharray:5 3}

/* ---------- the rim keywords ----------
   Five retired capability names ride five of the six wheels. The geometry
   is in the markup and so is the reason it works; what is left here is the
   type.

   THEY ARE GUIDES, NOT PARTS. The `#rw-*` ellipses exist only to carry a
   textPath, so they take no stroke -- the drawing gains five words, not
   five more circles. `fill` has to be stated on the text because the svg
   element itself carries `fill="none"`, which every child inherits; a
   textPath with no fill is a textPath you cannot see.

   --rule, the same tone as every line in the drawing, so the words belong
   to it rather than sit on it. That is 2.03:1 on the ground, which is
   nowhere near enough for copy and exactly right for this: the svg is
   aria-hidden, nothing legible depends on it, and a word set at --ink here
   would out-shout the four sentences it is behind. Solid glyphs already
   read a little stronger than a 1px hairline of the same colour, which is
   why they hold up at this size at all.

   SIZE IS IN USER UNITS AND THAT IS THE POINT. There is no
   `vector-effect` for text, so 4.2px here means 4.2 units of a 306-unit
   viewBox and the word scales with the mechanism, which is what keeps it
   looking engraved rather than pasted on. The catch is the range: the
   drawing renders about 3.06 units-to-pixels at 1440 and 1.96 below the
   viewport lock, so one stated size would swing from 12.8px to 8.2px. The
   step at 899 puts that back -- 5.2 units at 1.96 is 10.2px -- and 899 is
   already where the drawing changes width, so it is the same breakpoint,
   not a new one. Tracking scales with it.

   Below 600px the whole svg is `display:none` and these go with it, which
   is the only reason there is no third size. */
.work-gear .rim path{stroke:none;fill:none}
.work-gear .rim-t{font-family:var(--mono);font-weight:500;
                  font-size:4.2px;letter-spacing:.5px;
                  fill:var(--rule);stroke:none}
@media (max-width:899px){
  .work-gear .rim-t{font-size:5.2px;letter-spacing:.62px}
}

/* ---------- the mesh ----------
   Six wheels, four shafts, three meshes, and the numbers are the drawing's
   own. The lower stage is set at 10s PER TOOTH and everything else falls
   out of the ratios:

     a1  54T  r 81    540s     cw    shaft A, output cluster
     a2  18T  r 27    540s     cw    shaft A -- same shaft, same period
     b1  24T  r 36    240s     ccw   meshes a1
     b2  34T  r 51    240s     ccw   shaft B -- same shaft, same period
     c1  16T  r 24    112.941s cw    meshes b2
     d1  20T  r 30    200s     cw    meshes b1

   Meshed wheels turn OPPOSITE ways and their periods stand in the ratio
   of their radii, or the teeth grind:

     a1 : b1    540/240     = 2.25    = 81/36    cw  vs ccw
     b1 : d1    240/200     = 1.2     = 36/30    ccw vs cw
     b2 : c1    240/112.941 = 2.125   = 51/24    ccw vs cw

   Note the pitch lines do NOT all run at one speed, and they should not:
   this is a COMPOUND train, and a compound stage exists precisely to
   change the speed. a1/b1/d1 all sit at 10s per tooth and share a pitch
   line of 2.15px a second at 1440; b2/c1 sit at 7.06s per tooth and share
   3.06px a second. Within a mesh the speeds match, which is the condition
   that matters. Across the shaft-B break they do not, which is the
   reduction doing its job.

   So the sense flips exactly once, on shaft B, and `reverse` is set on its
   two wheels alone. a2 meshes nothing inside the frame: it is the output
   pinion on the cluster that bleeds off the corner, and it takes shaft A's
   period because it is keyed to shaft A.

   `transform-box:fill-box` is the load-bearing line. Each `.sp` sits
   INSIDE its own isometric matrix, so its bounding box is the gear's own
   plane and the box centre is the axis -- the turn comes out foreshortened
   with the drawing. The old field used `view-box` with a hand-computed
   origin per gear, which cannot work here: under an ancestor matrix the
   viewBox origin is not a point in the gear's space. Every tooth count is
   EVEN so the outline has two-fold symmetry and the box centre is exactly
   the axis; an odd count would put it off-axis and the wheel would wobble.

   Fail-open like the rest: with the animation off the drawing is simply
   still, which is what it is under reduced motion, in the gallery cards
   and in print. A still technical drawing is the fallback, not a loss. */
.work-gear .sp{transform-box:fill-box;transform-origin:50% 50%}
.work-gear .a1,.work-gear .a2{animation-duration:77.143s}
.work-gear .b1,.work-gear .b2{animation-duration:34.286s;animation-direction:reverse}
.work-gear .c1{animation-duration:16.134s}
.work-gear .d1{animation-duration:28.571s}
@media (prefers-reduced-motion:no-preference){
  .work-gear .sp{animation-name:gear-turn;animation-timing-function:linear;
                 animation-iteration-count:infinite}
  @keyframes gear-turn{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
  /* the drift-in listed .cap-row, which no longer exists */
  .work-row{animation:drift-in .95s cubic-bezier(.22,.68,.24,1) both}
}
body.embed .work-gear .sp,body.embed .work-row{animation:none}

/* Panel 03 on a short screen. Same shape, one size down, the same move the
   other plates make at this height. Measured at 1100x680 the untouched
   plate ran 59px past the fold. */
@media (min-width:900px) and (max-height:800px){
  .work{margin-top:clamp(14px,1.8vw,22px)}
  .work-list{gap:clamp(7px,.65vw,10px)}
  .work-row{padding-block:clamp(8px,.78vw,11px)}
  /* still well under `.h-l`, which does not shrink on a short screen:
     22.4px against 38px at 1280x720, 20.5px against 32.2px at 900x680 */
  .work-verb{font-size:clamp(18px,1rem + .5vw,23px)}
  .work-say{font-size:clamp(14px,1rem + .1vw,16px)}
}

/* ---------- panel 04: mission & vision, ONE plate, two statements ----------
   They were two panels for a while, because the nav names them separately
   and the snap track is MANDATORY: an anchor to a mid-panel element gets
   yanked back to the panel top the moment the scroll settles, so the Vision
   link looked dead. Splitting the panel was one way to fix that; resolving
   every anchor to its enclosing .panel-full is the better one, and that now
   lives in the nav script in index.html. With the jerk gone the two can
   share a screen again, which is what they do here.

   VERTICALLY stacked, never side by side. Two statements in two columns
   reads as a comparison -- this against that -- and mission and vision are
   not in opposition. Stacked, they read in order.

   The pair is a ZIGZAG: mission holds the left edge, vision the right, both
   on the SAME measure so the shift between them reads as one move rather
   than as two different block widths. The measure is a percentage of the
   plate rather than a ch count, so the offset that carries the zigzag stays
   proportional at every width instead of collapsing on a wide screen.

   Below 900px -- the width where the snap track and the viewport lock both
   switch off -- the zigzag goes away and both blocks run full width from the
   left edge. On a phone there is no room left over for an offset, so a right
   rag would read as a mistake rather than as a move.

   WIDE measures are the whole reason two statements fit one screen: width is
   what buys the line count back. See the type block below for the cap. */

/* The gap between the two blocks is height-driven for the same reason the
   type is: it is the one measurement that has to give when the screen is
   short. It stays smaller than the panel's own top and bottom padding, so the
   pair groups as one plate rather than reading as two panels that failed to
   separate. */
/* The interval between mission and vision. They are two separate claims,
   not two paragraphs of one, and at 64px they sat close enough to read as a
   single block of type. The panel has the room -- it is the only plate on
   the site holding two elements -- so spend it here. */
.statement-pair .plate-inner{display:grid;gap:clamp(30px,6vh,72px)}
/* The generous interval only where the screen can pay for it. This panel is
   locked to one viewport, so a gap big enough to separate two claims on a
   900px screen pushed the plate 23px past the fold on a 720px one -- the
   space has to come from somewhere and there is nothing else on the plate
   to take it from. Above 860px tall it opens up; below, it holds at a size
   that still separates them without costing the fold. */
@media (min-height:860px){
  .statement-pair .plate-inner{gap:clamp(72px,11vh,132px)}
}
.statement{max-width:100%}
/* Both statements set LEFT, on one shared left edge. This plate has been
   through a zigzag (mission left, vision right) and a ranged-right pair
   before landing here. Two elements do not need two edges, and left is the
   edge the rest of the site reads from, so the pair stops being a device
   and just reads. The accent rule and the tag sit on the same edge as the
   text they belong to. */
@media (min-width:900px){
  .statement{max-width:84%}
}

.statement .rule-t{height:2px;background:var(--accent);width:76px;
                   margin-bottom:clamp(12px,1.7vh,20px)}
/* The tag, stepped up from the 11px mono default. It is a real tag now, in
   accent and roughly half again the size, because on a plate carrying two
   statements the tag is the only thing telling you which is which -- at 11px
   grey that job was being done by a whisper. Still nowhere near the
   statement: the gap between them is a factor of three. */
.statement .label{font-size:clamp(13px,.55rem + .42vw,17px);letter-spacing:.2em;
                  color:var(--accent);line-height:1.25}
/* THE CAP, and why the preferred term is a min() of a width and a HEIGHT.
   The client asked for the opening panel's h1 scale. Two statements on one
   locked screen cannot have all of it, and a panel taller than the screen it
   is locked to is the bug we just spent a day removing elsewhere -- so this
   is the largest scale that clears the fold everywhere it has to.

   A width-only clamp is the wrong instrument for a panel that is locked to a
   HEIGHT. Measured: 1440x900 has 707px of content box and 1100x680 has 503px,
   yet a pure vw ramp reads those two as nearly the same screen and hands the
   short one type it has no room for. 1100x680 is the binding case and it was
   the one that overran. Taking the smaller of a width ramp and a height ramp
   lets the wide-and-tall screens have the size the client asked for while a
   short window steps itself down, with no media query and nothing to keep in
   sync. The 60px ceiling is reached at 1920x1080.

   MEASURED sizes: 60px at 1920x1080, 55.8 at 1600x900 and 1440x900, 51.2 at
   1440x800, 49.7 at 1366x768, 47.5 at 1280x720, 45.7 at 1100x680, 40.3 at
   900x680. Panel heights and slack are in the report. Across every one of
   those the mission sets in 3 lines and the vision in 2, so the pair keeps
   the same shape at every size rather than reflowing into a different
   composition halfway down the range.

   The height ramp is deliberately NOT pushed to the last pixel that fits.
   4.6vh leaves 20-30px of slack on the two tightest screens where 5vh left
   5-11; the extra pixel of type is not worth a panel that overruns the moment
   the webfont fails and the fallback metrics come in a hair wider. */
.statement .h-l{margin-top:clamp(6px,1.2vh,14px);max-width:100%;
                font-size:clamp(26px,min(1rem + 2.85vw,.9rem + 4.6vh),60px);
                line-height:1.07;letter-spacing:-.03em}
/* The subtext takes the opening panel's deck treatment rather than the page's
   19px one, on the same height-aware ramp as the statement and held to the
   same measure, so each block is one clean rectangle against its own edge.
   Measured 24.5px at 1920x1080, 21.9 at 1440x900, 19.2 at 1280x720 and 18.7
   at 1100x680; two lines under each statement at every desktop width. */
.statement .deck{margin-top:clamp(10px,1.7vh,20px);max-width:100%;color:var(--ink-2);
                 font-size:clamp(16px,min(1rem + .7vw,.55rem + 1.45vh),25px);
                 line-height:1.44}
/* On a phone the height ramp handed this 18.7px, which made the statement's
   subtext the largest paragraph on the page -- larger than the panel decks at
   15.5 and the figure labels at 14. The ramp is sized for a screen the panel
   is LOCKED to, and below 600px it is not locked to anything. */
@media (max-width:600px){
  .statement .deck{font-size:16px}
}

/* ---------- the footer ----------
   The six section links are gone from the markup. They repeated the top
   bar's nav item for item, on a page where that bar is fixed and always
   within reach, so the footer was spending its whole height restating a
   catalogue the reader already has -- and on a phone it was spending it
   badly, three clusters of links separated by voids.

   What stands there instead is the wordmark at the width of the page. It
   is the last thing on the site and the only place the mark is ever set
   large, which is the one job a footer can do that no other band can. The
   ramp is capped at 880px rather than run to the 1312px wrap: the mark is
   2.9:1, so a full-bleed setting would be a 450px-tall letterform on a
   desktop, which is a poster and not a footer.

   Under it, the three things that are NOT anywhere else on the page: where
   careers live, where to write, and whose site this is.

   This is the panels layer, not the shared base. Sixteen other pages carry
   the old footer markup and none of them is under review; `.foot-grid` and
   the base's `.foot-links` rules still serve them untouched. */
/* The mark runs the full measure and is the LAST thing on the page, with the
   legal row above it -- so the footer reads as a sign-off and the wordmark
   is what the page ends on. It fills the wrap rather than bleeding to the
   viewport: every edge on this site lines up with the page margin, and a
   mark that ignored it would be the only element that does. */
/* THE CUT AND THE FADE. The mark loses its bottom 30% and dissolves into the
   ground before it gets there, so the page ends on a letterform going dark
   rather than on a hard edge.

   The crop is a negative bottom margin on the svg inside an `overflow:hidden`
   box, and the number is derived, not guessed: a percentage margin resolves
   against the containing block's WIDTH, and this mark is 929.111 x 320, so
   30% of its height is 0.3 / 2.9036 = 10.33% of its width.

   The fade has to finish BEFORE the cut or the cut is visible as a straight
   edge across the letters. Fully opaque to 38% of the mark's own height,
   gone by 70% -- which is exactly where the crop lands, so what meets the
   bottom of the footer is nothing rather than a line.

   No `@supports` gate. Where masks are unsupported the mark simply sets
   whole and cropped, which is the same footer with the effect removed. */
.site-foot .foot-mark{display:block;color:var(--on-ink);margin-top:clamp(20px,2.6vw,34px);
                      position:relative;overflow:hidden;line-height:0}
.site-foot .foot-mark svg{display:block;width:100%;height:auto;max-width:100%;
                      margin-bottom:-10.33%;
                      -webkit-mask-image:linear-gradient(to bottom,#000 38%,transparent 70%);
                      mask-image:linear-gradient(to bottom,#000 38%,transparent 70%)}
/* ---------- the mark IS dots ----------
   Not a hover trick over a solid letterform: the wordmark is a dot field
   whenever the canvas can be built, and the cursor only stirs it. The svg
   underneath stays in the layout at `opacity:0` -- it is what sizes the box
   and it is what shows if the canvas never builds, so the footer degrades
   to a plain solid mark rather than to nothing.

   `has-dots` is added by the script only after a field has actually been
   sampled and painted, so the swap can never leave an empty footer.

   `pointer-events:none` on the canvas: it sits over an anchor, and the
   whole mark stays one click target.

   Print takes the svg, which is why the swap is opacity and a class rather
   than a removal. */
.site-foot .foot-dots{position:absolute;left:0;top:0;pointer-events:none;opacity:0;
                      -webkit-mask-image:linear-gradient(to bottom,#000 38%,transparent 70%);
                      mask-image:linear-gradient(to bottom,#000 38%,transparent 70%)}
.site-foot .foot-mark.has-dots .foot-dots{opacity:1}
.site-foot .foot-mark.has-dots svg{opacity:0}
@media print{
  .site-foot .foot-dots{display:none}
  .site-foot .foot-mark.has-dots svg{opacity:1}
}

.site-foot .colophon{margin-top:0;padding-top:0;border-top:0;
                     padding-bottom:clamp(14px,1.8vw,22px);
                     border-bottom:1px solid var(--rule-ink);
                     align-items:baseline;gap:6px 30px}
.site-foot .colophon .foot-links{display:flex;gap:26px}
.site-foot .colophon a{font-family:var(--mono);font-size:10px;letter-spacing:.1em;
                       text-transform:uppercase;color:var(--on-ink-2);text-decoration:none}
.site-foot .colophon a:hover{color:var(--on-ink)}
@media (max-width:600px){
  /* One column, and the 44px floor comes back on the two links -- they are
     the only tappable things left down here. */
  .site-foot .colophon{flex-direction:column;align-items:flex-start;gap:0}
  .site-foot .colophon .foot-links{gap:24px}
  .site-foot .colophon a{padding-block:15px}
  .site-foot .colophon .foot-dom{margin-top:4px}
}

/* The drift-in listed .mv > div, which no longer exists. The plate drifts as
   one block instead, and the .deck inside it gives up its own copy of the
   animation so the paragraph does not arrive twice. */
@media (prefers-reduced-motion:no-preference){
  .statement{animation:drift-in .95s cubic-bezier(.22,.68,.24,1) both}
  .statement .deck{animation:none}
}
body.embed .statement{animation:none}

/* ---------- founder ----------
   One person, composed to fill the plate rather than a roster grid: the
   portrait and its caption hold the left rail, the quote leads the right,
   and the bio runs in two columns beneath it so a long biography still
   lands inside one screen. The panel must never outgrow the viewport —
   the cover stack pins it, and a pinned panel holds its overflow below
   the fold permanently. */
.founder{display:grid;gap:clamp(24px,3.4vw,52px);align-items:start;
         margin-top:clamp(20px,2.6vw,32px)}
@media (min-width:820px){ .founder{grid-template-columns:clamp(200px,21vw,300px) 1fr} }
.founder-plate{margin:0}
@media (max-width:819px){ .founder-plate{max-width:240px} }
.founder-plate .plate{background:#171714;border-color:var(--rule);overflow:hidden}
.founder-plate .plate i{--cm:#57574F}
/* The slot is labelled, not numbered: one founder is not row 01 of a roster. */
.founder-plate .plate .slug{font-family:var(--mono);font-size:10px;font-weight:500;
         letter-spacing:.22em;text-transform:uppercase;color:#6E6E66}
.founder-plate .shot{position:absolute;inset:0;width:100%;height:100%;
         object-fit:cover;object-position:50% 30%;display:block}
.founder-plate figcaption .name{margin-top:16px;font-size:clamp(19px,1.6vw,23px);font-weight:400;
         letter-spacing:-.01em;line-height:1.15}
/* Role and link share a line. The link used to be a third block under the
   name, reading "Follow Dr. Jeff Zheng on LinkedIn" -- his name a second
   time, three lines under the heading that already says it. It is the
   LinkedIn bug now, sitting on the role's own baseline. */
.founder-plate figcaption .role{margin-top:6px;display:flex;align-items:center;
         gap:11px;font-family:var(--mono);font-size:11px;font-weight:500;
         letter-spacing:.12em;text-transform:uppercase;color:var(--accent)}

.founder-link{display:inline-flex;align-items:center;position:relative;
         color:var(--accent);text-decoration:none;transition:color .14s ease}
.founder-link svg{display:block;width:15px;height:15px}
.founder-link:hover,.founder-link:focus-visible{color:var(--ink)}
/* 15px of glyph is not a touch target. The `::after` is part of the link, so
   it takes the hit area to 45x31 without moving the mark; the left inset
   stops short of the 11px gap so it cannot swallow the end of the role. */
.founder-link::after{content:"";position:absolute;inset:-15px -8px -15px -5px}

.founder-quote{margin:0;border-left:2px solid var(--accent);padding-left:clamp(14px,1.4vw,20px)}
/* The quote and the bio are BOTH a step up from where they were, and the step
   is paid for by the copy, not by the layout. The bio that stood here was five
   paragraphs of third-person profile; it is three short first-person ones now,
   which left the right column ending 179px above the foot of the portrait rail
   at 1440x900 -- a hole under the text, and the panel reading half-empty. The
   answer to slack on a plate is bigger type, not more words: at 30px/18px the
   same two blocks close that gap to 40px and the bio stops being fine print.
   Measured: this costs NOTHING vertically. The portrait rail is the taller of
   the two columns at every width, so it, not the text, sets the panel height —
   564px of content against a 707px box at 1440x900, unchanged by the step. */
/* No measure cap on the quote. It carried `max-width:46ch`, which set it as a
   narrow column against a bio that runs the full width in two columns below
   it -- so the plate read as a short paragraph with a hole to the right of it
   the height of the quote. Full width across the column makes it a masthead
   over the two-column bio, which is the shape the rest of the page already
   uses, and it costs nothing: the quote drops from nine lines to four and the
   portrait rail is still the taller of the two columns. */
.founder-quote p{margin:0;font-size:clamp(15px,1rem + .42vw,20px);line-height:1.4;
         letter-spacing:-.014em;color:var(--ink);text-wrap:pretty}

.founder-bio{margin-top:clamp(14px,1.6vw,20px);color:var(--ink-2);
         font-size:clamp(14px,.6rem + .55vw,18px);line-height:1.6}
/* Two columns from 900px — the width where the viewport lock switches on, not
   1000px. Gated at 1000 the bio ran one long column across 900-999px and the
   founder plate measured 734px against a 680px screen: a panel overrunning the
   very screen it is locked to. The snap gate is the only correct breakpoint
   for anything that has to fit one. */
@media (min-width:900px){
  .founder-bio{columns:2;column-gap:clamp(24px,2.6vw,42px)}
}
.founder-bio p{margin:0}
.founder-bio p + p{margin-top:.9em}

/* Short screens: the quote and the bio step down so the founder still lands
   on one screen instead of spilling onto a second for two lines. Gated at 900
   for the same reason as the columns above. */
@media (min-width:900px) and (max-height:820px){
  .founder{margin-top:clamp(14px,1.6vw,20px);gap:clamp(20px,2.6vw,36px)}
  .founder-quote p{font-size:clamp(14px,.85rem + .3vw,17px);line-height:1.36}
  .founder-bio{margin-top:12px;font-size:clamp(12px,.5rem + .32vw,13.5px);line-height:1.5}
  .founder-plate figcaption .name{margin-top:12px;font-size:19px}
}

/* ---------- the close ---------- */
.closing{text-align:center}
.closing h3{max-width:18ch;margin-inline:auto}
.closing .deck{margin-top:clamp(20px,2.6vw,28px);max-width:42ch;margin-inline:auto;color:var(--ink-2)}
.closing .actions{justify-content:center}
/* The pull is part of the centred axis, not an exception to it. Left as a
   bare max-width it kept its 22ch box hard against the left edge while its
   text centred inside — three competing alignments on one plate, and the
   rules stopped halfway across. Centred, and widened so the rules measure
   something. */
.closing .pull{margin-inline:auto;max-width:34ch;
               margin-block:clamp(26px,3.4vw,40px) clamp(28px,3.6vw,44px)}
.closing .mailto{margin-top:clamp(26px,3.4vw,38px);color:var(--ink);border-bottom-color:var(--rule)}
.closing .mailto:hover{color:var(--accent);border-bottom-color:var(--accent)}

/* ---------- careers page ----------
   Two panels: the ask, then the article. The article panel does NOT snap
   and is not viewport-locked, because a wall of prose held to one screen
   is unreadable. This is the one place the layout deliberately lets go. */
.panel-open{min-height:auto}
.article{padding:calc(var(--bar) + clamp(30px,4vw,52px)) var(--pad) clamp(44px,6vw,76px)}
.article .plate-inner{max-width:900px;margin-inline:0}
.prose{display:grid;gap:clamp(17px,2vw,24px);max-width:68ch;margin-top:clamp(26px,3.4vw,40px)}
.pull{margin-block:clamp(12px,1.6vw,18px);padding-block:clamp(19px,2.4vw,27px);
      border-top:2px solid var(--ink);border-bottom:2px solid var(--ink);
      font-weight:500;font-size:clamp(22px,1rem + 1.2vw,30px);letter-spacing:-.022em;max-width:22ch}
.contact-block{margin-top:clamp(28px,3.4vw,40px);padding:clamp(22px,2.6vw,30px);
               border:1px solid var(--rule)}
.contact-block .mailto{color:var(--ink);border-bottom-color:var(--rule)}
.contact-block .mailto:hover{color:var(--accent);border-bottom-color:var(--accent)}

/* ---------- gallery card: the opening panel, unsnapped ---------- */
body.embed{--bar:34px}
body.embed html,body.embed{scroll-snap-type:none}
/* the panel is the anchor for its absolutely-positioned .stamp */
body.embed .panel-full{min-height:0;padding:calc(var(--bar) + 16px) 16px 18px;scroll-snap-align:none;position:relative}
body.embed .topbar{position:static;height:34px;padding-inline:16px}
body.embed .topbar .brand svg{height:16px}
body.embed .brand-name{font-size:15px}
body.embed .brand{gap:6px}
body.embed .nav,body.embed .nav-toggle{display:none}
body.embed .panel-full > .stamp{top:12px;right:16px;font-size:9px}
body.embed .opening .rule-t{width:52px;margin-bottom:14px}
body.embed .h-xl{font-size:17px;line-height:1.14;max-width:26ch}
body.embed .opening .deck{font-size:10.5px;line-height:1.46;margin-top:11px;max-width:50ch}
body.embed .opening .actions{margin-top:13px}
body.embed .btn{font-size:9px;padding:7px 11px}

/* ---------- the bar is a touch target up to the snap gate ----------
   The shared base gives the nav a 44px tap target below 600px. That was the
   wrong ceiling: a tablet is a touch context too, and between 601 and 899 —
   the whole band where this layout is already NOT a deck — the links were
   back to their bare 17px of glyph. Measured at 768 and at 820 every nav
   item was 17px tall, well under the 44px floor.

   Padding-block only, and nothing else. The bar is 56px tall through this
   band and the items are centred in it, so this changes the HIT AREA and
   not one pixel of the layout: a link goes from 17px to 47px tall inside a
   56px bar and the glyphs stay exactly where they were. The CTA keeps its
   own 13px of side padding, so the nav's total width is unchanged too and
   the six items still fit one line down to 601.

   Below 600 the block that follows restates `padding:15px 14px` on the CTA
   and wins on source order, so the phone behaviour is untouched. Nothing
   here applies at 900 or above. */
@media (max-width:899px){
  .nav a{padding-block:15px}
  .bar-cta{padding-block:15px}
}

@media (max-width:600px){
  /* six items overrun a 390px bar on one line. Wrap the row and tighten the
     gutter rather than hiding links the desktop has — the bar is static down
     here, so a second line costs nothing but its own height. */
  .nav{flex-wrap:wrap;justify-content:flex-end;column-gap:clamp(11px,3.2vw,18px);row-gap:0}
  .nav a{padding-block:15px}
  .bar-cta{padding:15px 14px}
  .topbar{height:auto;position:static;flex-wrap:wrap;padding-block:6px}
  .panel-full{padding-top:clamp(30px,7vw,44px)}
  .panel-full > .stamp{top:clamp(10px,3vw,16px)}
  :root{--bar:0px}

  /* ---------- the opening is a screen on a phone too ----------
     It was not. The wrapped bar took 168px of an 844px screen and the
     opening took 413 more, so the first thing a reader saw was a third of
     a hero with the next panel's heading already under it -- the one
     arrangement that reads as an accident rather than a title page.
     `svh` and not `vh`: the small viewport is the one that survives the
     browser chrome hiding on scroll, so the hero does not jump when it
     does. `.panel-full` already centres its own content, so the height is
     the whole fix. */
  .opening{min-height:100vh;min-height:100svh}
  .opening .h-xl{font-size:clamp(38px,10.4vw,52px);line-height:1.02}
  .opening .deck{font-size:17px;margin-top:22px}
  /* The buttons sat 30px under the deck, close enough to read as part of the
     same block. They are a separate move and want separating space; a hero
     that is a whole screen has it to spend. Phone only -- the desktop
     opening is locked to a 680px screen and has none to give. */
  .opening .actions{margin-top:46px}
}

/* ---------- the phone bar, where scripting is available ----------
   Six links and a wordmark do not fit a 390px line, and the fallback
   above wraps them: three stacked rows, 168px of a phone screen, with the
   CTA stranded alone on the last one. This layer replaces that with the
   bar the reader expects on a phone -- wordmark and one toggle, 54px,
   fixed -- and puts the six links in a drawer under it.

   Gated on `html.js` so the wrap above stays the no-scripting fallback:
   a drawer whose button does nothing would be a nav that cannot be
   reached at all, which is worse than a tall bar. The class is set by a
   one-line script in the head, before first paint, so no reader sees the
   fallback flash past.

   The drawer is absolutely positioned, so opening it moves nothing in the
   document -- the in-page nav's scroll maths runs against an unchanged
   layout whether the drawer is open or shut. */
@media (max-width:600px){
  :root.js{--bar:54px}
  .js .topbar{position:fixed;height:var(--bar);flex-wrap:nowrap;padding-block:0;
              gap:12px;overflow:visible}
  .js .topbar .brand svg{height:22px}
  .js .brand-name{font-size:21px}
  .js .brand{gap:8px}
  .js .nav-toggle{display:inline-flex}

  /* `flex-wrap:nowrap` is load-bearing, not tidiness. The fallback block
     above sets `flex-wrap:wrap` for the wrapped desktop-style row, and a
     WRAPPING COLUMN inside a max-height:0 box does not stack -- it spills
     into new columns sideways, six links laid out to 887px on a 600px
     screen. Hidden by the overflow, but the drawer only looked right open
     because that was the one state tall enough not to wrap. */
  .js .nav{position:absolute;top:100%;left:0;right:0;z-index:59;
           flex-direction:column;flex-wrap:nowrap;align-items:stretch;gap:0;
           background:var(--paper);border-bottom:1px solid var(--rule);
           max-height:0;overflow:hidden;visibility:hidden;
           transition:max-height .22s ease,visibility 0s linear .22s}
  .js .topbar.is-open .nav{max-height:min(70svh,520px);overflow-y:auto;visibility:visible;
           transition:max-height .26s ease,visibility 0s}

  /* One rule per row, from the row's own top border -- the same junction
     idiom the panels use. The folio number rides in the gutter so the list
     reads as an index of the deck rather than a stack of links. */
  .js .nav a{display:flex;align-items:baseline;gap:14px;
             padding:15px var(--pad);border-top:1px solid var(--rule);
             font-size:12px;color:var(--ink)}
  .js .nav a[data-n]::before{content:attr(data-n);color:var(--accent);
             font-size:10px;letter-spacing:.16em;flex:0 0 auto}
  /* The same box in two weights -- outline for the catalogue, solid for the
     one thing this bar is asking for -- and a shorter box than the padding
     alone would give. 15px of padding either side made a 45px button, which
     cleared the 44px touch floor by being as tall as the floor; at 34 the
     bar reads as a bar rather than as two slabs.

     The floor is kept anyway, by separating the DRAWN box from the TAPPED
     one: the `::after` is part of the button, so it extends the hit area
     6px past the border on every side without moving a pixel of the paint.
     46px of target inside a 34px control. */
  .js .nav-toggle,.js .bar-cta{padding:9px 13px;position:relative}
  .js .nav-toggle::after,.js .bar-cta::after{content:"";position:absolute;inset:-6px}

  /* No rule under the phone bar. The bar is opaque `--paper` and the panels
     it sits over are the same ground, so the hairline was drawing a join
     between two things that are already one colour -- on a wide screen it
     separates the bar from a plate that starts right under it, but here the
     first thing below it is the folio and 50px of air. */
  .js .topbar{border-bottom:0}

  /* The folio and the heading were on the same band. The stamp sits at
     bar+14 and runs 13px, so it ended at 81 -- and the panel's content
     started at 80, which put "01 WHY NOW" level with the first line of the
     title it labels rather than above it. 50px of padding puts 22px of air
     under the stamp, which is the least that reads as two things.

     The opening is exempt: it centres its content in a whole screen, so
     nothing of its is near the folio, and the extra top padding would only
     push the hero off centre. */
  .js .panel-full{padding-top:calc(var(--bar) + 50px)}
  .js .opening{padding-top:calc(var(--bar) + 26px)}
  .js .panel-full > .stamp{top:calc(var(--bar) + 14px)}

  /* A panel deck runs five or six lines on a 390px screen, and every line
     of it is a line the figure below does not get. One step down, and the
     space it gives back is spent on the gap above the figure -- 26px was
     tight enough that the paragraph and the drawing read as one block. */
  .js .plate-deck{font-size:15.5px;line-height:1.5}
  .js .plate-grid{margin-top:40px}
}

@media print{
  :root{--paper:#fff;--ink:#000;--ink-2:#444;--rule:#bbb;--rule-2:#c9c9c9;--accent:#000}
  /* these have to out-specify the `body.is-deck` snap track above, or the
     deck rules win on specificity and every panel prints a screen tall */
  html:has(body.is-deck){scroll-snap-type:none}
  body.is-deck .panel-full{min-height:auto}
  .panel-full{min-height:auto;padding-block:24px;break-inside:avoid}
  /* print does not stop an animation from running, and a travelling dot
     frozen wherever the page happened to be is a blob on a wire. Drop them:
     they are decoration, and the routes underneath are already drawn. */
  .tdot{display:none}
  /* Panel 03's gear set goes the same way. It re-themes through the tokens
     above, so it would print as hairlines on white rather than white on
     white -- but --rule prints at #d8d8ce, which against paper is 1.2:1,
     and a mechanism this dense at that contrast is a grey bruise in the
     corner rather than a drawing. It is also ambience: the four
     workstreams are the panel, and ambience does not survive a printer.
     Dropped rather than lightened, because there is nothing lighter to go
     to. */
  .work-gear{display:none}
  /* The four boxes are frosted glass on screen -- a translucent white film
     over a `backdrop-filter` blur of the drawing behind them. NEITHER HALF
     SURVIVES A PRINTER. `backdrop-filter` does not print at all, so the box
     would keep the film with nothing under it; and the film is a white tint
     sized for a near-black ground, which on paper is a grey wash under black
     text. Both go, and the boxes print as plain ruled boxes on white -- which
     is what a frosted box is once the drawing it was frosting is gone.
     Restated at this specificity because the `@supports` block that sets
     them also matches `.work-row` and would otherwise tie on order and win
     on being... no, lose on order -- but the reset has to be explicit either
     way, because `@supports` does not add specificity and the cascade here
     is decided by source order alone.
     The group's 800px cap goes too: it exists to leave the drawing room on
     screen, and there is no drawing on paper. A page column is narrower than
     the cap anyway, so this only matters if someone prints very wide. */
  .work-list,.work-row{border-color:var(--rule)}
  .work-row{background:transparent;
            -webkit-backdrop-filter:none;backdrop-filter:none}
  .work{max-width:none}
  /* The plate grid goes to one column on paper and the diagram gives up
     its row span with it, or it would reserve empty rows of a single
     column. Its gutters survive on `print-color-adjust:exact` above.
     `has-quad` needs naming here as well: it out-specifies the bare
     `.plate-grid` reset and would otherwise print three tracks.
     The travelling dots are dropped just above; the routes underneath,
     including the broken second leg and the direct line, print as drawn. */
  .plate-grid,.plate-grid.has-quad{grid-template-columns:minmax(0,1fr)}
  .plate-grid > .fig-cell,
  .plate-grid.has-quad > .fig-cell{grid-column:auto;grid-row:auto}
  .site-foot{background:#fff!important;color:#000!important}
  .site-foot a,.colophon span{color:#000!important}
}

/* ---------- careers: one continuous document, left aligned ----------
   `body.is-doc` is the other half of `body.is-deck` at the top of this
   file. The home page is a deck of screens and opts into the snap track
   and the viewport lock; this page is a document and takes neither, so
   the hero is no longer a panel — it is the lede of the article beneath
   it, and the two run together with nothing but white space between.

   Everything here is scoped to the class, because `.closing` is shared
   with panel 07 on the home page and panel 07 stays centred. Scoping is
   cheaper than a second copy of the plate, and it keeps the difference
   between the two pages readable in one block instead of scattered.

   No seam: the hairline was the join between two panels, and there are
   no panels here to join. */
body.is-doc .panel-full{border-bottom:0}

/* ONE left edge down the page. `.panel-full` and `.article` share a
   padding-left at every width — var(--pad) — so the only thing that
   pushed the hero off that edge was
   its plate centring itself inside a box wider than 1240px. Same box for
   both blocks, hard left, and a vertical line now touches the label, the
   heading, the deck, the pull's rules, the button and the article's
   heading alike.

   The one thing that line does NOT touch is the prose, above 1000px: the
   article's own two-column rail puts the heading in column 1 and the column of text in
   column 2, roughly 350px in. That is the rail doing its job — without it
   a 68ch measure strands in the left half of a wide screen — and the rail
   itself starts on the shared edge. Below 1000px the rail stacks and the
   prose joins the line too. */
body.is-doc .plate-inner{width:auto;max-width:66ch;margin-inline:auto}

/* off the centred axis. This is the complete inverse of `.closing`'s
   centring — its `text-align`, all three of its `margin-inline:auto`s and
   its `justify-content` — deliberately including `h3`, which today's lede
   is not (it is the page `h1`), so that changing the heading level cannot
   silently pull it back to the middle. Not one of these changes a measure:
   the deck still holds 42ch and the pull still holds 34ch. They grow
   rightwards from the edge now instead of outwards from the middle. */
body.is-doc .closing{text-align:start}
body.is-doc .closing h3,
body.is-doc .closing .deck{margin-inline:0}
body.is-doc .closing .actions{justify-content:flex-start}
body.is-doc .closing .pull{margin-inline:0}

/* ---------- one left edge, all the way down ----------
   The article ran a two-column rail above 1000px: the section heading in a
   sticky left column, the prose in a wider right one. On a page whose whole
   point is a single left-aligned document that put a second left edge 348px
   inboard of the first — the hero started at x=134 and the prose at x=482,
   with a dead column under the heading and an empty third to the right of
   the text. Two edges reads as a mistake however you justify the rail.

   One column. The heading sits above its passage on the same edge as
   everything else, the prose keeps its own reading measure, and a vertical
   line down the left of the page touches every element on it. */
body.is-doc .article .plate-inner{display:block;width:auto;max-width:66ch;margin-inline:auto}
/* the measure now lives on the plate, so the prose must not re-cap it or
   the ink shrinks inside a wider box and stops being centred again */
body.is-doc .article .prose{max-width:none}
body.is-doc .article .sec-head{position:static;max-width:none}
body.is-doc .article .sec-head .h-l{max-width:20ch}
body.is-doc .article .prose{margin-top:clamp(22px,2.8vw,32px)}

/* The article lost its own heading, so the hero now runs straight into the
   prose. Without that heading the old top padding was a hole rather than a
   gap, and the two halves read as a page with something missing from
   between them. */
body.is-doc .article{padding-top:0}
/* The hero keeps .panel-full's bottom padding, which is sized for a panel
   ending a screen -- 72px at 1440. On a page that is one continuous run of
   copy that is not a paragraph break, it is a missing section. But it must
   not close past a paragraph either, or the standfirst fuses onto the body
   and stops reading as a standfirst. Measured at 1440: a paragraph break in
   this prose is 42px, the old plate gap was 89px, and this sits at 55px --
   more than a paragraph, well short of a plate. */
body.is-doc .closing{padding-bottom:clamp(22px,3.8vw,56px)}
body.is-doc .article .prose{margin-top:0}

/* "That's what we're building at NDot AI." is 38 characters and was set on
   a 22ch measure, so it broke after "at" and the sentence landed as two
   ragged lines inside its rules. It is a one-line statement; give it the
   room to be one. The measure is in ch, so it holds as the type scales. */
body.is-doc .article .pull{max-width:42ch}

/* The ask moved out of the hero to sit immediately above the contact block,
   where it hands off to the address instead of interrupting the opening. */
body.is-doc .pull-ask{max-width:34ch;margin-top:clamp(30px,4vw,46px)}
