/* ==================================================================
   Interaction demos
   ==================================================================

   Loaded only on pages that host one. Everything is scoped under a
   demo's own class so nothing here can reach the page around it — a
   demo is a picture of a *different* product, and it has to be able
   to use a white content pane and a light UI without the site's own
   chrome rules following it in.

   Two palettes are in play on purpose:
     - the stage behind the window uses the site's field anchors
       (--navy → --amber), so the demo reads as part of this site
       rather than a screenshot pasted into it;
     - the window itself is the fictional product's palette, which is
       dark chrome around a white document.
   ================================================================== */

/* ------------------------------------------------------------------
   Demo 01 — Keyboard-first navigation
   ------------------------------------------------------------------ */

.kbnav {
  /* The window is drawn at 900×580 inside a 1100×720 stage and the
     whole thing is scaled by JS. Every number below is therefore a
     design pixel, not a screen pixel: they do not need clamping,
     because the transform does the responsive work. */
  --rail: 52px;
  --rail-open: 176px;
  --side-w: 208px;
  --chrome: #08080A;
  --chrome-2: #0C0C0F;
  --chrome-line: rgba(244, 244, 242, 0.09);
  --dim: rgba(244, 244, 242, 0.46);
  --doc: #FFFFFF;
  --doc-ink: #15151A;
  --doc-dim: #76767F;
  --doc-line: #ECECEE;
  --doc-fill: #F5F5F7;

  display: block;
}

/* --- Stage ------------------------------------------------------- */

.kbnav__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1100 / 720;   /* pre-JS, so the box does not jump */
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--hair);
  background:
    radial-gradient(96% 82% at 44% 34%, rgba(16, 58, 140, 0.58), transparent 74%),
    radial-gradient(56% 52% at 84% 92%, rgba(222, 154, 64, 0.20), transparent 74%),
    radial-gradient(130% 120% at 50% 50%, #0B1226 0%, #060A16 58%, #03050C 100%);
}

.kbnav__fit {
  position: absolute;
  top: 0;
  left: 0;
  width: 1100px;
  height: 720px;
  transform-origin: 0 0;
}

/* --- Window ------------------------------------------------------ */

.kbnav__win {
  position: absolute;
  top: 70px;
  left: 100px;
  width: 900px;
  height: 580px;
  display: flex;
  border-radius: 12px;
  background: var(--chrome);
  outline: none;
  overflow: hidden;
  box-shadow:
    0 1px 0 rgba(244, 244, 242, 0.10) inset,
    0 2px 6px rgba(0, 0, 0, 0.30),
    0 24px 70px rgba(0, 0, 0, 0.55);
}

/* The skip link is the first focusable thing inside the window, and
   it is the honest answer to "how does a keyboard user get past the
   navigation" — which is the question this demo exists to answer. */
.kbnav__skip {
  position: absolute;
  top: 10px;
  left: 62px;
  z-index: 6;
  padding: 6px 12px;
  border-radius: 7px;
  background: var(--paper);
  color: #111;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-decoration: none;
  transform: translateY(-160%);
  transition: transform 140ms var(--ease);
}

.kbnav__skip:focus-visible { transform: translateY(0); }

/* --- Icon rail --------------------------------------------------- */

/* Out of flow, so the peek state can lie on top of the sidebar without
   moving it. The sidebar reserves the rail's collapsed width with a
   margin instead, and only the pinned state changes that margin — see
   the rail-width block further down. */
.kbnav__rail {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 3;
  width: var(--rail);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 12px 0;
  background: #050507;
  border-right: 1px solid var(--chrome-line);
  overflow: hidden;
  transition: width 170ms var(--ease), box-shadow 170ms var(--ease);
}

/* The one saturated object inside the window. A product mark is the
   place a real app puts its brand colour, so it is the place this
   fiction gets to borrow the site's navy without the rest of the
   chrome going coloured with it. */
.kbnav__mark {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin: 0 0 12px 13px;
  border-radius: 7px;
  background: linear-gradient(140deg, #1E4FB0 0%, #103A8C 55%, #0B2A66 100%);
  color: var(--paper);
  flex: 0 0 auto;
}

.kbnav__mark svg { width: 15px; height: 15px; }

.kbnav__tabs {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
}

/* Every child of the rail is positioned from the left, never centred.
   Centring would move the icons sideways as the rail widens, and an
   icon that slides while its own label arrives reads as two changes
   instead of one. The icons hold still; only the words appear. */
.kbnav__rail-btn,
.kbnav__rail-toggle {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  margin-left: 10px;
  padding: 0 7px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--dim);
  font-family: inherit;
  cursor: pointer;
  overflow: hidden;
  transition: width 170ms var(--ease),
              background 150ms var(--ease),
              color 150ms var(--ease);
}

.kbnav__rail-btn svg,
.kbnav__rail-toggle svg { flex: 0 0 auto; width: 17px; height: 17px; }

.kbnav__rail-toggle {
  margin-top: auto;
  color: rgba(244, 244, 242, 0.34);
}

.kbnav__rail-toggle svg { transition: transform 170ms var(--ease); }
.kbnav.is-rail-pinned .kbnav__rail-toggle svg { transform: rotate(180deg); }

.kbnav__rail-toggle:hover { color: var(--paper); background: rgba(244, 244, 242, 0.05); }

/* Faded, not hidden: `display:none` would strip the button's only
   accessible name. Opacity leaves the text in the accessibility tree
   the whole time, so the icon-only rail still announces "People". */
.kbnav__label {
  white-space: nowrap;
  font-size: 12.5px;
  letter-spacing: -0.005em;
  opacity: 0;
  transition: opacity 110ms var(--ease);
}

.kbnav__rail-btn:hover { color: var(--paper); background: rgba(244, 244, 242, 0.05); }

.kbnav__rail-btn[aria-selected="true"] {
  color: var(--paper);
  background: rgba(244, 244, 242, 0.10);
  box-shadow: 0 1px 0 rgba(244, 244, 242, 0.10) inset;
}

/* --- Rail width: peek vs pinned ----------------------------------- */

/* PEEK — hover, or keyboard focus arriving in the rail. The rail grows
   over the sidebar and nothing else moves: a pointer crossing the edge
   of the window must not be able to reflow the document someone is
   reading. The shadow is what says "this is floating above", and it is
   the only thing that distinguishes the two states visually. */
.kbnav.is-rail-peek .kbnav__rail {
  width: var(--rail-open);
  box-shadow: 16px 0 44px rgba(0, 0, 0, 0.55);
}

/* PINNED — the control, or `\`. The user asked for the labels to stay,
   so the layout genuinely changes and the content pane pays for it. A
   deliberate choice is allowed to cost width; an accidental hover is
   not. */
.kbnav.is-rail-pinned .kbnav__rail { width: var(--rail-open); }
.kbnav.is-rail-pinned .kbnav__side { margin-left: var(--rail-open); }

.kbnav.is-rail-peek .kbnav__rail-btn,
.kbnav.is-rail-pinned .kbnav__rail-btn,
.kbnav.is-rail-peek .kbnav__rail-toggle,
.kbnav.is-rail-pinned .kbnav__rail-toggle {
  width: calc(var(--rail-open) - 20px);
}

.kbnav.is-rail-peek .kbnav__label,
.kbnav.is-rail-pinned .kbnav__label {
  opacity: 1;
  /* Held back until the rail is most of the way open. Words that fade
     in while the box is still moving arrive twice. */
  transition-delay: 70ms;
}

/* --- Section sidebar --------------------------------------------- */

.kbnav__side {
  position: relative;
  z-index: 0;
  flex: 0 0 var(--side-w);
  width: var(--side-w);
  /* The rail is out of flow, so the sidebar reserves its collapsed
     width here. This margin is the only thing the pinned state moves. */
  margin-left: var(--rail);
  padding: 13px 10px;
  background: var(--chrome-2);
  transition: margin-left 170ms var(--ease);
}

.kbnav__side-title {
  margin: 0 0 10px;
  padding: 0 8px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--paper);
}

.kbnav__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.kbnav__item {
  display: block;
  width: 100%;
  padding: 6px 9px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--dim);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.35;
  letter-spacing: -0.005em;
  text-align: left;
  cursor: pointer;
  transition: background 130ms var(--ease), color 130ms var(--ease);

  /* Staggered in when the section changes. 14ms is small enough to
     read as one movement rather than eight, which is the difference
     between a list arriving and a list being dealt out. */
  animation: kbnav-in 220ms var(--ease) backwards;
  animation-delay: calc(var(--i) * 14ms);
}

.kbnav__item:hover { color: var(--paper); background: rgba(244, 244, 242, 0.05); }

.kbnav__item[aria-current="page"] {
  color: var(--paper);
  background: rgba(244, 244, 242, 0.10);
}

@keyframes kbnav-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* --- Content pane ------------------------------------------------ */

.kbnav__content {
  position: relative;
  z-index: 1;               /* rides over the sidebar when it collapses */
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  margin: 8px 8px 8px 0;
  border-radius: 10px;
  background: var(--doc);
  overflow: hidden;

  /* The sidebar does not leave — the document slides over it. 150ms:
     the user asked for this explicitly and already knows the outcome,
     so anything longer is just waiting for the UI to catch up. */
  transition: margin-left 150ms var(--ease);
}

.kbnav.is-collapsed .kbnav__content { margin-left: calc(var(--side-w) * -1); }

.kbnav__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 40px;
  padding: 0 14px;
  border-bottom: 1px solid var(--doc-line);
}

.kbnav__toggle {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--doc-dim);
  cursor: pointer;
  transition: background 130ms var(--ease), color 130ms var(--ease);
}

.kbnav__toggle svg { width: 15px; height: 15px; }
.kbnav__toggle:hover { background: var(--doc-fill); color: var(--doc-ink); }

.kbnav__crumbs {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  letter-spacing: -0.005em;
  color: var(--doc-dim);
}

.kbnav__crumbs > span:last-child { color: var(--doc-ink); }
.kbnav__sep { color: #C9C9CE; }

.kbnav__doc {
  flex: 1;
  padding: 24px;
  overflow: hidden;
  outline: none;
}

.kbnav__doc-title {
  margin: 0;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--doc-ink);
  animation: kbnav-in 240ms var(--ease) backwards;
}

.kbnav__doc-sub {
  margin: 5px 0 22px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #A2A2AA;
  animation: kbnav-in 240ms var(--ease) 30ms backwards;
}

/* Placeholders, not a dashboard. A fully dressed screen in here would
   become the thing people look at, and the thing on show is the way
   you moved between screens. */
.kbnav__tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 14px;
}

.kbnav__tile {
  height: 78px;
  border-radius: 9px;
  background: var(--doc-fill);
  animation: kbnav-in 260ms var(--ease) backwards;
  animation-delay: calc(60ms + var(--i) * 30ms);
}

.kbnav__rows { display: grid; gap: 7px; }

.kbnav__row {
  height: 34px;
  border-radius: 7px;
  background: #FAFAFB;
  animation: kbnav-in 260ms var(--ease) backwards;
  animation-delay: calc(140ms + var(--i) * 26ms);
}

/* --- Keycap HUD --------------------------------------------------- */

.kbnav__hud {
  position: absolute;
  right: 22px;
  bottom: 20px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 6px;
  opacity: 0;
  transform: translateY(6px) scale(0.94);
  pointer-events: none;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}

.kbnav__hud.is-on {
  opacity: 1;
  transform: none;
  transition-duration: 120ms;
}

.kbnav__cap {
  min-width: 46px;
  padding: 9px 13px;
  border-radius: 11px;
  border: 1px solid rgba(244, 244, 242, 0.12);
  background: rgba(20, 20, 24, 0.80);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 21px;
  line-height: 1;
  text-align: center;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

.kbnav__cap-hint {
  padding: 0 4px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(244, 244, 242, 0.66);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .kbnav__cap { background: rgba(20, 20, 24, 0.94); }
}

/* --- Legend ------------------------------------------------------- */

/* Outside the scaled stage, so it stays at real type size whatever the
   window is scaled to. It is also the only route to the shortcuts on a
   touch device, which is why the chips are buttons. */
.kbnav__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  margin-top: 14px;
}

.kbnav__key {
  min-width: 28px;
  padding: 4px 8px;
  border: 1px solid var(--hair-strong);
  border-radius: 6px;
  background: transparent;
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: background 130ms var(--ease), border-color 130ms var(--ease);
}

.kbnav__key--static { cursor: default; color: var(--mist); }
button.kbnav__key:hover { background: rgba(244, 244, 242, 0.08); }

.kbnav__then,
.kbnav__hint {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mist);
}

.kbnav__hint { margin-right: 12px; }

/* --- Walkthrough caption ------------------------------------------ */

/* Demo 01 narrates itself with keycaps, because there the input is
   the subject. Here the input is a click anyone could guess at and
   the *outcome* is the subject, so the same corner of the same kind
   of pill carries a sentence instead. Identical position, identical
   material — one series, two demos. */
.bmk__hud {
  position: absolute;
  right: 22px;
  bottom: 6px;
  z-index: 6;
  margin: 0;
  padding: 9px 14px;
  border-radius: 11px;
  border: 1px solid rgba(244, 244, 242, 0.12);
  background: rgba(20, 20, 24, 0.82);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px) scale(0.96);
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

.bmk__hud.is-on {
  opacity: 1;
  transform: none;
  transition-duration: 140ms;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bmk__hud { background: rgba(20, 20, 24, 0.94); }
}

/* --- Focus ------------------------------------------------------- */

/* Amber on the dark chrome, navy on the white document. One ring
   colour cannot clear 3:1 against both, and a focus ring that is only
   *nearly* visible on half the UI is the failure this demo is about. */
.kbnav :focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: 7px;
}

.kbnav__content :focus-visible { outline-color: #1E4FB0; }

/* The window itself is a `tabindex="-1"` landing pad — focus is moved
   there after a click so the next keypress reaches the demo instead of
   the page. Tab never stops on it, so a ring around the whole window
   would only ever be noise. */
.kbnav__win:focus-visible { outline: none; }

/* --- Screen-reader only ------------------------------------------ */

.kbnav__sr,
.kbnav__status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Reduced motion ---------------------------------------------- */

/* The tour does not run at all under this setting (handled in JS), and
   every state change lands instantly. The HUD still appears, because it
   is information, not decoration. */
@media (prefers-reduced-motion: reduce) {
  .kbnav *,
  .kbnav *::before,
  .kbnav *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
  }

  .kbnav__hud { transition: opacity 0.01ms; transform: none; }
}

/* ==================================================================
   Demo 02 — Benchmarks AI: comparing groups
   ==================================================================

   A build of an unshipped Time Doctor screen, drawn at 1366×842 design
   pixels and scaled to the column by one transform in JS. Every number
   below is therefore a design pixel, not a screen pixel: they are read
   off the Figma frame and they do not need clamping, because the
   transform does the responsive work.

   The window is the product's palette — Time Doctor's PDS tokens, a
   warm near-black chrome over a paper-white document — and none of it
   is allowed to leak out past `.bmk`.
   ------------------------------------------------------------------ */

/* Roboto, self-hosted next to the site's own faces, because the design
   system this screen belongs to is specified in Roboto and a demo that
   claims to be pixel-accurate cannot be set in a substitute with
   different metrics. One variable file covers 400/500/700. */
@font-face {
  font-family: 'Roboto Demo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                 U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Roboto Demo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
                 U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

.bmk {
  /* PDS tokens, by their names in the product. */
  --pds-surface: #F1EFED;
  --pds-paper: #FFFFFF;
  --pds-ink: #000000;
  --pds-ink-2: #574841;
  --pds-ink-3: #9A918D;
  --pds-nav: #342B27;
  --pds-sub: #574841;
  --pds-nav-ink: #B3AEAB;
  --pds-accent: #04D464;

  display: block;
}

/* --- Stage ------------------------------------------------------- */

/* The same field demo 01 sits in: the site's navy and amber anchors
   under a near-black ground, so the piece reads as part of this site
   rather than a screenshot pasted into it. Two palettes are in play
   on purpose — the field is the site's, the window is the product's.

   The inset is 64×68 against demo 01's 100×70. That is a legibility
   decision: this app is 1366 design pixels wide to demo 01's 900, so
   every pixel spent on the field is taken off the type inside the
   window. 64 buys the frame its proportions — 1494:978 against demo
   01's 1100:720 — while keeping the 12px caption above the size it
   stops being readable at. */
.bmk__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1494 / 978;   /* pre-JS, so the box does not jump */
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--hair);
  background:
    radial-gradient(96% 82% at 44% 34%, rgba(16, 58, 140, 0.58), transparent 74%),
    radial-gradient(56% 52% at 84% 92%, rgba(222, 154, 64, 0.20), transparent 74%),
    radial-gradient(130% 120% at 50% 50%, #0B1226 0%, #060A16 58%, #03050C 100%);
}

.bmk__fit {
  position: absolute;
  top: 0;
  left: 0;
  width: 1494px;
  height: 978px;
  transform-origin: 0 0;
}

/* The window, floating in the field. Same shadow recipe as demo 01's:
   a hairline of light along the top edge, a tight drop to seat it,
   and a long soft one to lift it off the ground. */
.bmk__app {
  position: absolute;
  top: 68px;
  left: 64px;
  width: 1366px;
  height: 842px;
  border-radius: 12px;
  overflow: hidden;
  box-shadow:
    0 1px 0 rgba(244, 244, 242, 0.10) inset,
    0 2px 6px rgba(0, 0, 0, 0.30),
    0 24px 70px rgba(0, 0, 0, 0.55);
  background: var(--pds-surface);
  color: var(--pds-ink);
  font-family: 'Roboto Demo', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  -webkit-font-smoothing: antialiased;
}

.bmk__app svg { display: block; }

/* --- Top nav ----------------------------------------------------- */

.bmk__nav {
  position: absolute;
  inset: 0 0 auto 0;
  height: 56px;
  display: flex;
  align-items: center;
  padding: 0 27px;
  background: var(--pds-nav);
}

.bmk__logo { display: inline-flex; margin-right: 10px; }

.bmk__nav-items { display: flex; align-items: stretch; height: 56px; }

.bmk__nav-item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 0 13px;
  /* PDS/BUTTON — 14px Medium, 0.75px tracking. Measured off the frame
     rather than guessed: at 13px the whole bar comes up ~5% short and
     every gap after DASHBOARD drifts left. */
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--pds-nav-ink);
  white-space: nowrap;
}

/* The active marker is 3px of the productivity green, the same token
   the charts use for "productive". In the product it is the one accent
   colour on the whole chrome, which is why it can mean "you are here"
   without a second signal. */
.bmk__nav-item.is-on { color: #FFFFFF; }
.bmk__nav-item.is-on::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 0;
  height: 3px;
  background: var(--pds-accent);
}

.bmk__nav-right {
  display: flex;
  align-items: center;
  gap: 22px;
  margin-left: auto;
  color: #FFFFFF;
}

.bmk__nav-icon { display: inline-flex; color: #FFFFFF; }
.bmk__nav-who { font-size: 14px; letter-spacing: 0.25px; white-space: nowrap; }

.bmk__avatar {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #C8FFE2;
  color: #006B31;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.4px;
}

/* --- Sub nav ----------------------------------------------------- */

.bmk__sub {
  position: absolute;
  top: 56px;
  left: 0;
  right: 0;
  height: 56px;
  display: flex;
  align-items: center;
  padding: 0 27px;
  background: var(--pds-sub);
  color: #FFFFFF;
}

.bmk__sub-title { font-size: 20px; font-weight: 700; letter-spacing: 0.15px; }

/* The group picker for the whole report. Inert in this demo on
   purpose: it is the other half of the same feature, and two live
   controls answering one question would split the reader's attention
   across both instead of putting it on the chart. */
.bmk__sub-groups {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: 26px;
  font-size: 14px;
  letter-spacing: 0.25px;
}

.bmk__sub-right { display: flex; align-items: stretch; height: 56px; margin-left: auto; }

.bmk__sub-tz,
.bmk__sub-range { display: inline-flex; align-items: center; font-size: 14px; letter-spacing: 0.25px; white-space: nowrap; }
.bmk__sub-tz { gap: 2px; }
.bmk__sub-range { padding: 0 5px 0 12px; }
.bmk__sub-step { display: inline-flex; align-items: center; padding: 0 14px; }

.bmk__sub-scale {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 0 16px;
  /* PDS/BUTTON 2 — the smaller of the two button styles. The date
     scale sits one level down from the main nav and is set that way. */
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.64px;
  text-transform: uppercase;
  color: var(--pds-nav-ink);
  white-space: nowrap;
}
/* The last tab's own right padding would push it 16px off the frame's
   right margin, so it gives that padding up and the bar's padding
   carries the edge instead. */
.bmk__sub-scale:last-child { padding-right: 0; }
.bmk__sub-tz { padding-right: 10px; }
.bmk__sub-scale:first-of-type { margin-left: 5px; }
.bmk__sub-scale.is-on { color: #FFFFFF; }
.bmk__sub-scale.is-on::after {
  content: '';
  position: absolute;
  left: 16px;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--pds-accent);
}

/* --- Page furniture ---------------------------------------------- */

.bmk__help {
  position: absolute;
  top: 120px;
  left: 27px;
  width: 1318px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.25px;
}
.bmk__help-icon { display: inline-flex; }

.bmk__crumb {
  position: absolute;
  top: 156px;
  left: 24px;
  margin: 0;
  font-size: 20px;
  font-weight: 500;
  line-height: 24px;
  letter-spacing: 0.15px;
}

/* --- The board and its cards ------------------------------------- */

.bmk__board {
  position: absolute;
  top: 196px;
  left: 24px;
  width: 1318px;
  height: 595px;
}

.bmk__card {
  position: absolute;
  top: 0;
  left: 0;
  width: 651px;
  background: var(--pds-paper);
  border-radius: 4px;
  /* PDS elevation 01dp, the three-shadow Material recipe. */
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.20),
    0 2px 1px -1px rgba(0, 0, 0, 0.12),
    0 1px 1px rgba(0, 0, 0, 0.14);
  /* 320ms, and a curve that overshoots slightly on the way in. A card
     changing places is a layout change the reader asked for and
     already knows the outcome of, so the motion only has to carry the
     eye across — but it is also the only confirmation that the drop
     landed, which is what the small overshoot is for. */
  transition: transform 320ms cubic-bezier(0.2, 0.9, 0.25, 1.06),
              box-shadow 180ms ease;
  will-change: transform;
}

/* Both cards are the same size. The bar-chart card is 595 and stays
   there; the line-chart card treats 595 as a floor and is measured in
   JS, because its chip row wraps and the footer link under it has a
   24px gutter to keep. */
.bmk__card--hours { height: 595px; }
.bmk__card--share { height: 595px; }

/* The card in hand leaves the plane. It is the only thing on the
   board that casts a real shadow, which is what tells the reader the
   other card is not going to move under their finger by accident. */
.bmk__card.is-grabbed {
  z-index: 10;
  transition: box-shadow 180ms ease;
  box-shadow:
    0 12px 20px -6px rgba(0, 0, 0, 0.26),
    0 24px 48px -12px rgba(0, 0, 0, 0.22);
}

/* --- Card header (and the grab region) --------------------------- */

.bmk__grab {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 64px;
  cursor: grab;
  /* Without this the browser claims the horizontal gesture for
     scrolling and the drag never starts on a touch screen. */
  touch-action: none;
}

.bmk.is-dragging .bmk__grab { cursor: grabbing; }
/* The whole app, not just the card: once a drag is in flight, a text
   selection dragged out from under the card is the most common way to
   make the gesture feel broken. */
.bmk.is-dragging .bmk__app { user-select: none; -webkit-user-select: none; }

.bmk__head {
  position: absolute;
  top: 24px;
  left: 24px;
  height: 28px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.bmk__title {
  margin: 0;
  font-size: 20px;
  font-weight: 500;
  line-height: 24px;
  letter-spacing: 0.15px;
  white-space: nowrap;
}

.bmk__info { display: inline-flex; color: var(--pds-ink-3); }

/* Absolutely placed rather than pushed to the end of a flex row: the
   view switch now sits between it and the title, and the frame puts
   the glyph 14px in from the card edge rather than at the 24px
   gutter the title uses. */
.bmk__cardmenu {
  position: absolute;
  top: 22px;
  right: 11px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--pds-ink-2);
  cursor: pointer;
  transition: background 130ms ease;
}
.bmk__cardmenu:hover { background: rgba(87, 72, 65, 0.08); }

/* --- The view switch --------------------------------------------- */

/* A segmented control: a track the width of its two labels, with the
   selected option as a filled pill inside it. Not two buttons with a
   border between them — the pill is what says "one of these is true",
   and it is the only part that has to move when the answer changes.

   Right-aligned to 40px from the card edge, which is where the frame
   puts it: hard against the overflow menu, as far from the title as
   the card allows. It is a property of this chart, not of the page. */
.bmk__seg {
  position: absolute;
  top: 24px;
  right: 40px;
  display: flex;
  height: 28px;
  padding: 2px;
  border-radius: 14px;
  background: var(--pds-surface);
}

.bmk__seg-btn {
  display: inline-flex;
  align-items: center;
  padding: 0 18px;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: var(--pds-ink-3);
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  line-height: 24px;
  letter-spacing: 0.1px;
  white-space: nowrap;
  cursor: pointer;
  transition: color 160ms ease, background 200ms var(--ease);
}

.bmk__seg-btn[aria-checked='true'] {
  background: var(--pds-sub);
  color: #FFFFFF;
  cursor: default;
}

.bmk__seg-btn[aria-checked='false']:hover { color: var(--pds-ink-2); }

/* --- The line chart ---------------------------------------------- */

.bmk__plot {
  position: absolute;
  top: 88px;
  left: 24px;
  width: 605px;
  height: 317px;
}

.bmk__svg { position: absolute; top: 0; left: 0; overflow: visible; }

.bmk__grid { stroke: rgba(87, 72, 65, 0.11); stroke-width: 1; }

/* The percentile markers are dashed and darker than the gridlines
   because they are not part of the scale — they are the benchmark,
   and the whole reason the chart exists is to see which lines sit
   inside them. */
.bmk__band { stroke: var(--pds-ink-3); stroke-width: 2; stroke-dasharray: 8 6; }

.bmk__line {
  fill: none;
  stroke: var(--c);
  /* Half strength. The dots carry the colour at full saturation
     because they are where the values actually are; the line between
     them is interpolation, and drawing interpolation as loudly as
     data is a small lie that gets louder with every group added. */
  stroke-opacity: 0.5;
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bmk__pt { fill: var(--pds-paper); stroke: var(--c); stroke-width: 4; }

/* A line arriving draws itself left to right and its dots land behind
   it, so the eye follows the new series across the chart once and
   then has it. A fade would put ten lines' worth of new ink on screen
   at the same instant and leave the reader to find which one moved. */
@keyframes bmk-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes bmk-pop  { from { opacity: 0; transform: scale(0.2); } to { opacity: 1; transform: none; } }

/* Every polyline carries pathLength="1" (set in the module), so one
   dash of one unit is exactly the whole path however long it really
   is — the same 460ms wipe whether the line is flat or a zigzag. */
.bmk__series.is-entering .bmk__line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: bmk-draw 460ms cubic-bezier(0.32, 0.72, 0.25, 1) forwards;
}

.bmk__series.is-entering .bmk__pt {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: bmk-pop 260ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(140ms + var(--i) * 80ms);
}

.bmk__series.is-leaving { opacity: 0; transition: opacity 170ms ease; }

/* --- Reading a point --------------------------------------------- */

/* Invisible, and deliberately much larger than the dot it covers.
   These stack four deep in the middle of the band, so the disc is the
   difference between hovering a value and hunting one. */
.bmk__hit { fill: transparent; cursor: pointer; }

/* The rest of the chart drops back rather than the read line coming
   forward: the line the reader wants may already be drawn on top, and
   there is nowhere further forward for it to go. */
.bmk__series { transition: opacity 140ms ease; }
.bmk__series.is-muted { opacity: 0.16; }
.bmk__series.is-read .bmk__line { stroke-opacity: 0.85; }

/* The dot under the pointer gets a ring in its own colour, so the
   tooltip and the point it belongs to are tied together without an
   arrow that would have to be re-pointed on every flip. */
.bmk__hit.is-on {
  fill: none;
  stroke: var(--c, #574841);
  stroke-width: 2;
  stroke-opacity: 0.32;
  r: 13;
}

.bmk__tip {
  position: absolute;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 132px;
  padding: 8px 10px 9px;
  border-radius: 4px;
  /* Dark, where the menus are white. A tooltip lands on top of the
     data it describes, and white-on-white would need a border to stay
     separate from the card — which is one more line on a chart that
     already has ten. */
  background: var(--pds-nav);
  color: #FFFFFF;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 110ms ease, transform 110ms var(--ease);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}
.bmk__tip.is-below { transform: translateY(-3px); }
.bmk__tip.is-on { opacity: 1; transform: none; }

.bmk__tip-head {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.25px;
  color: rgba(255, 255, 255, 0.72);
}

/* The percentile is the answer, so it is the only thing in the
   tooltip set at reading size. The group and the week are the
   question, and they are already on screen. */
.bmk__tip-val {
  font-size: 15px;
  font-weight: 500;
  line-height: 20px;
  letter-spacing: 0.1px;
}

.bmk__tip-sub {
  font-size: 11px;
  line-height: 15px;
  letter-spacing: 0.3px;
  color: rgba(255, 255, 255, 0.62);
}

.bmk__dot--sm { width: 9px; height: 9px; }

.bmk__band-cap {
  position: absolute;
  padding: 0 3px;
  background: var(--pds-paper);
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-2);
  white-space: nowrap;
}

.bmk__ytick {
  position: absolute;
  left: 1px;
  width: 22px;
  text-align: right;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-3);
}

.bmk__xtick {
  position: absolute;
  top: 293px;
  width: 90px;
  text-align: center;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-3);
}

/* --- The compare chips ------------------------------------------- */

/* `display: flex` on a class beats the UA stylesheet's rule for the
   `hidden` attribute, so hiding the chip row in the aggregated view
   needs saying explicitly. Worth the line: `hidden` is what takes the
   row out of the accessibility tree and the tab order at the same
   time, which a class toggling opacity would not. */
.bmk__compare[hidden] { display: none; }

.bmk__compare {
  position: absolute;
  top: 421px;
  left: 24px;
  width: 603px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 8px;
}

.bmk__compare-label {
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-2);
}

.bmk__chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  padding: 0 8px;
  border: 0;
  border-radius: 12px;
  background: var(--pds-surface);
  color: var(--pds-ink);
  font-family: inherit;
  font-size: 13px;
  line-height: 16px;
  letter-spacing: 0.25px;
  white-space: nowrap;
}

.bmk__dot { width: 14px; height: 14px; border-radius: 50%; flex: 0 0 auto; }

.bmk__chip-x {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  margin-right: -2px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--pds-ink-2);
  cursor: pointer;
}
.bmk__chip-x:hover { background: rgba(87, 72, 65, 0.12); }

.bmk__chip--add {
  padding: 0 9px;
  color: var(--pds-ink-2);
  cursor: pointer;
  transition: background 130ms ease;
}
.bmk__chip--add:hover { background: #E4E0DD; }

/* Disabled rather than gone at the ceiling — it still answers when
   pressed, which is the only way the reader learns a limit exists. */
.bmk__chip--add[aria-disabled='true'] { color: var(--pds-ink-3); cursor: default; }
.bmk__chip--add[aria-disabled='true']:hover { background: var(--pds-surface); }

.bmk__more {
  position: absolute;
  left: 24px;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-2);
}
/* 555 puts the link's baseline box 24px above the bottom of a 595
   card — the same gutter the title gets at the top, so the card reads
   as a block with one margin rather than two. The line-chart card can
   push it down if its chips ever need the room; the bar card cannot,
   so it is written once here. */
.bmk__card .bmk__more { top: 555px; }
.bmk__card--hours .bmk__more { top: var(--footer-top, 555px); }
.bmk__more-arrow { display: inline-flex; }

/* --- The stacked-bar card ---------------------------------------- */

.bmk__share {
  position: absolute;
  top: 88px;
  left: 24px;
  width: 603px;
  height: 300px;
}

.bmk__share-grid span {
  position: absolute;
  top: 90px;
  width: 2px;
  height: 142px;
  background: rgba(87, 72, 65, 0.12);
}

.bmk__share-label {
  position: absolute;
  left: 0;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
}

.bmk__share-bar { position: absolute; height: 40px; display: flex; }

.bmk__bar-seg {
  display: flex;
  align-items: center;
  height: 40px;
  padding-left: 8px;
  color: #FFFFFF;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  overflow: hidden;
}

.bmk__share-x span {
  position: absolute;
  top: 245px;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  color: var(--pds-ink-2);
}

.bmk__key {
  position: absolute;
  top: 413px;
  left: 36px;
  width: 591px;
  display: flex;
  align-items: center;
  gap: 16px;
}

.bmk__key-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.4px;
  white-space: nowrap;
}

.bmk__key-sw { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }

/* --- Popovers ---------------------------------------------------- */

.bmk__pop-layer { position: absolute; inset: 0; z-index: 30; pointer-events: none; }

.bmk__pop {
  position: absolute;
  min-width: 208px;
  padding: 8px 0;
  border-radius: 4px;
  background: var(--pds-paper);
  /* PDS elevation 08dp — a menu has to sit clearly above a card that
     already carries a shadow of its own. */
  box-shadow:
    0 5px 5px -3px rgba(0, 0, 0, 0.20),
    0 8px 10px 1px rgba(0, 0, 0, 0.14),
    0 3px 14px 2px rgba(0, 0, 0, 0.12);
  pointer-events: auto;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 120ms ease, transform 120ms cubic-bezier(0.22, 1, 0.36, 1);
}
.bmk__pop.is-open { opacity: 1; transform: none; }

.bmk__pop-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 36px;
  padding: 0 16px;
  border: 0;
  background: transparent;
  color: var(--pds-ink);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 0.25px;
  text-align: left;
  cursor: pointer;
  transition: background 110ms ease;
}
.bmk__pop-item:hover,
.bmk__pop-item.is-hot { background: rgba(87, 72, 65, 0.08); }
.bmk__pop-item:focus-visible { background: rgba(87, 72, 65, 0.12); }

.bmk__pop-item[aria-disabled='true'] { color: var(--pds-ink-3); cursor: default; }
.bmk__pop-item[aria-disabled='true']:hover { background: transparent; }

/* --- Focus ------------------------------------------------------- */

/* Navy, not the site's amber: everything focusable in this demo sits
   on white or on near-white, and amber does not clear 3:1 against
   either. Demo 01 needs both colours because it has both surfaces. */
.bmk :focus-visible {
  outline: 2px solid #1E4FB0;
  outline-offset: 2px;
  border-radius: 6px;
}

/* --- Hint strip and live region ---------------------------------- */

.bmk__hints {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  margin-top: 14px;
}

.bmk__hint {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mist);
}

.bmk__hint strong {
  display: inline-block;
  min-width: 18px;
  margin-right: 5px;
  padding: 3px 6px;
  border: 1px solid var(--hair-strong);
  border-radius: 6px;
  color: var(--paper);
  font-weight: 400;
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: none;
}

.bmk__status,
.bmk__table {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Reduced motion ---------------------------------------------- */

/* The tour does not run at all under this setting (handled in JS) and
   every state change lands instantly. The beckon is decoration and
   stops entirely; the live region still speaks, because it is
   information. */
@media (prefers-reduced-motion: reduce) {
  .bmk *,
  .bmk *::before,
  .bmk *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==================================================================
   Demo 03 — The number field
   ==================================================================

   A picture of this site's own landing page, drawn at 900 design
   pixels — the width a small laptop gives it — inside the same
   1100×720 stage demo 01 uses, and scaled to the column by one
   transform in JS. Every number below is therefore the number the
   real page uses at that width, read out of style.css rather than
   invented here, and none of them need clamping because the
   transform does the responsive work.

   The two palettes the other demos run — the site's field outside,
   somebody else's product inside — collapse into one here. The
   product *is* this site, so the window carries the landing's own
   atmosphere and the stage carries the same field the other two sit
   in, one a little brighter than the other so the frame still reads
   as a frame.
   ------------------------------------------------------------------ */

.numfld { display: block; }

/* --- Stage ------------------------------------------------------- */

.numfld__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1100 / 720;   /* pre-JS, so the box does not jump */
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--hair);
  background:
    radial-gradient(96% 82% at 44% 34%, rgba(16, 58, 140, 0.58), transparent 74%),
    radial-gradient(56% 52% at 84% 92%, rgba(222, 154, 64, 0.20), transparent 74%),
    radial-gradient(130% 120% at 50% 50%, #0B1226 0%, #060A16 58%, #03050C 100%);
}

.numfld__fit {
  position: absolute;
  top: 0;
  left: 0;
  width: 1100px;
  height: 720px;
  transform-origin: 0 0;
}

/* --- The page in the window -------------------------------------- */

/* Demo 01's window geometry exactly — 900×580 at 100,70 — and its
   shadow recipe: a hairline of light along the top edge, a tight drop
   to seat it, a long soft one to lift it off the ground.

   The fill is the landing's own `body[data-field]::before`, which is
   the darker half of what the stage is painted with. The field draws
   inside it, so the window has to be the ground that field belongs
   to rather than a neutral pane with a canvas on top. */
.numfld__win {
  position: absolute;
  top: 70px;
  left: 100px;
  width: 900px;
  height: 580px;
  overflow: hidden;
  border-radius: 12px;
  background:
    radial-gradient(96% 82% at 44% 36%, rgba(16, 58, 140, 0.22), transparent 76%),
    radial-gradient(54% 50% at 84% 92%, rgba(222, 154, 64, 0.08), transparent 76%),
    radial-gradient(130% 120% at 50% 50%, #060A16 0%, #03050C 60%, #000000 100%);
  box-shadow:
    0 1px 0 rgba(244, 244, 242, 0.10) inset,
    0 2px 6px rgba(0, 0, 0, 0.30),
    0 24px 70px rgba(0, 0, 0, 0.55);
}

/* The same three-layer order the landing runs, for the same reason:
   some digits drift behind the type and some in front of it, and that
   split is what gives the field depth.

     z 0  back canvas    far motes
     z 1  the page       the headline, and the words the egg leaves
     z 2  front canvas   near motes and the pile
     z 6  the caption */
.numfld__canvas {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
  opacity: 0;
  transition: opacity 900ms var(--ease);
}

.numfld.is-field .numfld__canvas { opacity: 1; }

.numfld__canvas--back  { z-index: 0; }
.numfld__canvas--front { z-index: 2; }

.numfld__page {
  position: relative;
  z-index: 1;
  height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 0 38px;            /* the gutter clamp, resolved at 900px */
}

.numfld.is-nova .numfld__page {
  opacity: 0;
  transition: opacity 300ms var(--ease);
}

/* --- Masthead and colophon --------------------------------------- */

.numfld__bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-top: 30px;
}

.numfld__name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--paper);
}

.numfld__nav {
  display: flex;
  gap: 22px;
}

.numfld__nav span,
.numfld__meta span,
.numfld__foot span {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.numfld__nav span { color: var(--paper); }

.numfld__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 26px;
  padding-top: 18px;
  border-top: 1px solid var(--hair);
  color: var(--mist);
}

/* --- The headline ------------------------------------------------ */

/* The landing's hero is one centred block, not two columns: the
   headline and the two mono lines under it are one thing, and the
   flex here is only doing the vertical centring `.hero` does. */
.numfld__hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  min-height: 0;
  padding: 12px 0;
}

/* The signature, and the whole reason this demo exists: the sentence
   sits dim under the field and is *uncovered* rather than faded in.
   Two stacked copies — a muted base and a lit one masked to a window
   that follows the well — and field.js writes --mx / --my / --reveal
   onto this element every frame.

   85px is `clamp(3.2rem, 9.5vw, 10rem)` at 900px, and 244px is
   `clamp(190px, 27vw, 500px)` at the same width. Both are the real
   page's own numbers, resolved once rather than re-derived. */
.numfld__title {
  --reveal-r: 244px;
  --mx: 50%;
  --my: 50%;
  --reveal: 0;

  position: relative;
  display: block;
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 105%;
  font-size: 85px;
  line-height: 0.9;
  letter-spacing: -0.042em;
  max-width: 20ch;
}

/* Bright by default, dimmed only once the field is actually running —
   so a browser without canvas is left a fully readable headline
   rather than a ghost, exactly as on the landing. */
.numfld__layer {
  display: block;
  color: var(--paper);
}

.numfld.is-field .numfld__layer { color: rgba(244, 244, 242, var(--veil)); }

.numfld__layer--lit,
.numfld.is-field .numfld__layer--lit {
  position: absolute;
  inset: 0;
  color: var(--paper);
  opacity: var(--reveal);
  /* No text-shadow here, and that is a performance decision. This is
     the one element whose mask centre is rewritten every frame, so it
     re-rasterises on every frame the well moves; re-rasterising plain
     text is cheap and re-rasterising text over a Gaussian blur cost
     the landing two thirds of its frame rate. See the note on
     .hero__title__layer--lit in style.css for the measurements. */
  -webkit-mask-image: radial-gradient(circle var(--reveal-r) at var(--mx) var(--my),
    #000 0%, rgba(0, 0, 0, 0.86) 38%, rgba(0, 0, 0, 0) 78%);
  mask-image: radial-gradient(circle var(--reveal-r) at var(--mx) var(--my),
    #000 0%, rgba(0, 0, 0, 0.86) 38%, rgba(0, 0, 0, 0) 78%);
}

.numfld__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  align-items: baseline;
  margin-top: 22px;
  color: var(--mist);
}

/* --- The easter egg ---------------------------------------------- */

/* Deliberately the same layer as the page, not above it: the front
   canvas is at z 2, so the pile of digits draws over these words the
   way it draws over the headline. The words are read *through* the
   numbers, like everything else here. */
.numfld__nova {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  padding: 0 38px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 420ms var(--ease);
}

.numfld__nova.is-on { opacity: 1; }

/* No transform on this element, and not the headline's line-height
   either. field.js rasterises it to find where the digits go:
   getBoundingClientRect reports a transformed box, and at 0.9 the
   line box is shorter than the type inside it, so the bottom of every
   letter would fall outside the sample. Both bit during development
   of the landing; the note is in style.css next to .nova__word. */
.numfld__word {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 105%;
  font-size: 99px;            /* 11vw at 900px */
  line-height: 1.15;
  letter-spacing: -0.045em;
  text-align: center;
  color: rgba(244, 244, 242, 0.46);
  text-shadow: 0 0 60px rgba(222, 154, 64, 0.22);
}

/* --- Caption ------------------------------------------------------ */

/* Same pill, same corner, same material as demo 02's — one series,
   three demos. It sits in the strip of stage below the window, where
   demo 01 puts its keycaps, and outside the scaled box so it stays at
   real type size whatever the stage is fitted to. */
.numfld__hud {
  position: absolute;
  right: 24px;
  bottom: 22px;
  z-index: 6;
  margin: 0;
  padding: 9px 14px;
  border-radius: 11px;
  border: 1px solid rgba(244, 244, 242, 0.12);
  background: rgba(20, 20, 24, 0.82);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px) scale(0.96);
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

.numfld__hud.is-on {
  opacity: 1;
  transform: none;
  transition-duration: 140ms;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .numfld__hud { background: rgba(20, 20, 24, 0.94); }
}

/* On a phone the stage is drawn at a third of its size but the caption
   is not, so a pill sized for a 1100px stage lands across the middle of
   a 340px one. It goes small and full-width along the floor instead —
   still the demo's own corner of its own box, just the only shape that
   fits there. */
@media (max-width: 720px) {
  .numfld__hud {
    left: 10px;
    right: 10px;
    bottom: 8px;
    padding: 7px 10px;
    font-size: 9px;
    letter-spacing: 0.06em;
    text-align: center;
    white-space: normal;
  }
}

/* --- Hints ------------------------------------------------------- */

/* Outside the stage, so they stay at real type size whatever the
   window is scaled to — and the code is a button because typing it is
   not available to a touch screen, and hunting for an easter egg you
   cannot press is not a demonstration of anything. */
.numfld__hints {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  margin-top: 14px;
}

.numfld__hint {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mist);
}

.numfld__hint strong {
  display: inline-block;
  margin-right: 5px;
  padding: 3px 6px;
  border: 1px solid var(--hair-strong);
  border-radius: 6px;
  color: var(--paper);
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: none;
}

.numfld__key {
  padding: 4px 9px;
  border: 1px solid var(--hair-strong);
  border-radius: 6px;
  background: transparent;
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 130ms var(--ease), border-color 130ms var(--ease);
}

.numfld__key:hover { background: rgba(244, 244, 242, 0.08); }

/* --- Focus ------------------------------------------------------- */

/* Amber, not demo 02's navy: everything focusable here sits on the
   dark field, which is the surface amber was chosen against. */
.numfld :focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: 7px;
}

/* --- Screen-reader only ------------------------------------------ */

.numfld__status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Reduced motion ---------------------------------------------- */

/* The walkthrough does not run at all under this setting and the
   field renders one static frame with no pile on it — both handled
   where they belong, in JS and in field.js. What is left here is the
   page chrome, which still has to be able to clear for the easter
   egg: that is an opacity swap, and the query has already reduced it
   to an instant one. */
@media (prefers-reduced-motion: reduce) {
  .numfld *,
  .numfld *::before,
  .numfld *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
  }

  /* The same rule the real headlines get in style.css: the field is one
     static frame and never repels, so the sentence must not depend on a
     cursor it will never see. The lit copy has nothing left to reveal
     and is taken out rather than left at opacity 0. */
  .numfld.is-field .numfld__layer { color: var(--paper); }
  .numfld__layer--lit { display: none; }
}

/* ==================================================================
   Lab layout — the prototype, then the reasoning
   ==================================================================

   One column at every width. The 70/30 split this started as put a
   900px window into a 7-of-10 column and the writing into a sticky
   3 — which made the piece smaller than it deserves to be and the
   prose narrower than it reads well at. Since the phone layout was
   already stacking them, the desktop now does the same thing: the
   prototype at full width, the reasoning underneath.

   That reasoning is a .rows--case block from style.css — the same
   label-and-body pair the written case studies use — so a project
   with a prototype and a project without one read as one set rather
   than two. Only the stage below is particular to these pages.

   Each project is its own page at its own URL rather than an overlay.
   A case study is something people send each other a link to, and a
   static site gets real URLs, browser history and back-button
   behaviour for free the moment it stops trying to be an app.
   ================================================================== */

.lab { margin-top: clamp(28px, 4vw, 56px); }

/* The stage scales its 1100×720 design to whatever box it is handed,
   so on a 1440px content width it would upscale by 1.3× and stand
   ~940px tall — taller than most laptop viewports, and a window drawn
   larger than life reads as a screenshot blown up rather than as an
   app. Capped at its own design width, it sits 1:1.

   Left-aligned, not centred: the masthead, the eyebrow, the title and
   the lede all start at the same edge, and a centred stage under them
   would be the only thing on the page that does not. */
.lab__main { max-width: 1100px; }

