/* Duplex World flight page.

   Committed to a single LIGHT world on purpose, and the reason changed with the artwork.
   The first build had knocked-out globes on transparency, so a night vitrine was right.
   The page now plays a rendered chain shot on a plain white studio floor - measured across
   the five stops, that backdrop runs #f7f4f9 to #d6d7da - so a dark page forced a scrim over
   a bright film and turned it grey. Here the page ground IS the studio floor: the diorama
   floats in it and the copy sits below on the same surface.

   Ink and accents are sampled from the artwork's own purple base and magenta paths, then
   darkened until they hold against a light ground rather than a dark one. */

:root {
  /* --bg-base is the sheet's answer; --bg is what is actually painted.
     They differ because the film's floor is not one colour. Sampled off flight.mp4's own
     border pixels it runs #e6e5e6 at the opening, #f5f2f1 at the third stop and #fefefe at
     the close - so against any fixed value there is a visible seam somewhere, and at the
     closing stop it was 22 levels, landing exactly where the reader stops moving and looks.
     flight.js samples the live frame and eases --bg toward it, then eases back to this when
     the film is not the stage. Nothing else in this sheet may write --bg. */
  /* THE PALETTE IS THE DECK'S, not a second one invented beside it. Lifted verbatim from
     speech_sync_8.html so the page and the talk are visibly the same piece of work: five
     semantic families, each with a base, a dark that holds on light ground, a tinted
     background and a line. */
  --ink:       #17171f;
  --ink2:      #1C1F45;
  --text:      #21233F;
  --soft:      #55586F;
  --grey:      #6d6f87;
  /* Defined here so no element depends on a JS fallback for its colour. */
  --accent:    #4F277C;   /* 10.17:1 on the page ground; the green it replaced was 4.95 */
  /* Accents are now the paper's three PILLAR hues and nothing else. The magenta and
     violet that used to sit here were decoration: they encoded nothing, appeared nowhere
     in the paper, and collided with the vendor marks drawn on top of them. Every rule
     downstream keeps its old token name and simply resolves to a pillar colour.
     The agentic hue is the brand purple, the same #4F277C the art and the film already
     carry and that --world-pathfinding has always used, rather than a green that
     appeared in neither. (Deliberately unnamed here: this file ships byte-identical into
     both variants, so spelling the organisation out is a de-anonymising leak - the guard
     in make_site.py rejects the build for it, which is how this comment got reworded.) */
  --mag:       #4F277C;   --magd: #3B1C5E;  --magbg: #F0EBF6;  --magln: #D5C8E4;
  --vio:       #4285F4;   --viod: #1F63D6;  --viobg: #E9F1FE;  --violn: #C9DDFB;
  --teal:      #4F277C;   --teald: #3B1C5E; --tealbg: #F0EBF6; --tealln: #D5C8E4;
  --amb:       #F28C00;   --ambd: #B36800;  --ambbg: #FFF3E2;  --ambln: #F6D6A6;
  --red:       #C0392B;   --redbg: #FDEDEB; --redln: #F2C9C3;
  --paper:     #ffffff;
  --bg-base:   #f6f6f9;   /* the deck's surface, and near the film's own studio floor */
  --bg:        var(--bg-base);
  /* The page's own names, mapped onto the deck's. Kept so every existing rule reads the
     same and only these five lines decide what colour anything is. */
  --ink-soft:  var(--text);
  --ink-dim:   var(--grey);
  --magenta:   var(--magd);
  --line:      #E5E5EF;
  --glass:     rgba(255, 255, 255, 0.74);
  --font-ui:   ui-sans-serif, system-ui, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  /* One scale, five steps. The page previously carried twelve distinct sizes between 11px
     and 20px, including half-pixel values, which is the tell that they were nudged one at
     a time rather than derived. --t-ctrl is deliberately off-scale: controls read better
     slightly smaller than body. */
  /* Raised from 12px. The floor on this project is 14px and this token fed the
     eyebrows, tags, rail and hint, which is most of the small type on the page.
     The scale had already collapsed to 14 nearly everywhere, so this closes the
     gap rather than opening a new step. */
  --t-micro:   14px;                        /* eyebrows, tags, rail, hint */
  --t-ctrl:    14px;                      /* buttons and links */
  --t-body:    17px;
  --t-lead:    20px;                        /* pair column titles */
  --t-display: clamp(32px, 2.6vw, 44px);    /* 38 is the widest the longest title fits */
  /* How much of the viewport the film occupies. The copy gets the rest, and because the
     two never overlap there is no scrim anywhere on this page.

     The 320px floor is not decorative: a pair panel measures 265px, so on a 1366x768
     laptop a flat 60vh left the primary CTA below the fold of a stage that is
     overflow:hidden - it vanished silently. Below about 743px tall the film yields
     instead. --film-w/--film-h are the film's REAL painted box, which the mask needs. */
  /* The classic scrollbar's width, measured and written by layout(). 100vw includes it and
     documentElement.clientWidth does not, so every width derived from raw 100vw overshot by
     15px - at 430px that put the film at x = -7 with both edges clipped by the stage. */
  --sbw:    0px;
  --vw:     calc(100vw - var(--sbw));
  --film:   min(60vh, calc(100vh - 320px));
  --film-w: min(var(--vw), calc(var(--film) * 16 / 9));
  --film-h: min(var(--film), calc(var(--vw) * 9 / 16));
  color-scheme: light;
}

* { box-sizing: border-box; }

/* Smooth applies to reader-initiated jumps (a rail dot, an anchor). Every programmatic
   write in flight.js passes behavior:'instant' explicitly, because a two-argument scrollTo
   resolves to THIS value and would animate the autoplay accumulator instead of setting it.
   The single copy of the narrow-rail rule now lives with the rail, further down. */
html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ------------------------------------------------------------------ topbar */

.topbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px clamp(16px, 3vw, 34px);
  pointer-events: none;
  /* A ground of its own. The bar was fully transparent, so everything that scrolled
     underneath printed straight through the nav: on a phone the author names and the
     wordmark occupied the same pixels.
     Now OPAQUE rather than 78% translucent. A band panel taller than the viewport travels
     through its band - that is what makes its tail reachable - so its heading necessarily
     passes behind this bar on the way up. Measured: the at-a-glance heading reaches -12px
     and four of the results headings reach between 7px and 61px against a 64px bar. At 78%
     they ghosted through it half-legible, which reads as content dissolving rather than as
     content scrolling past. Solid, it simply goes behind the bar, which is what a fixed
     header is for. The blur stays for the rounded ends. */
  background: var(--bg-base);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  backdrop-filter: saturate(1.6) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease;
}
/* Only once something has actually gone under it. */
.is-scrolled .topbar { border-bottom-color: var(--grid); }
@supports not (backdrop-filter: blur(2px)) {
  .topbar { background: var(--bg-base); }
}
.topbar > * { pointer-events: auto; }

/* Below the phone breakpoint there is not room for the wordmark and three pills on one
   line: the bar grew to 98px, wrapped one label onto three lines, cut another at the right
   edge and then covered 40 per cent of the film. The secondary links stand down; both
   destinations are still reachable from the closing panel. */
@media (max-width: 900px) {
  .topbar { gap: 8px; padding: 8px 12px; }
  .topbar .linkbtn:not(:last-child) { display: none; }
  .wordmark .sub { display: none; }
}

.wordmark {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-weight: 700;
  letter-spacing: 0.015em;
  font-size: 14px;
}
.wordmark .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--magenta);
  box-shadow: 0 0 0 4px rgba(11, 122, 86, 0.16);
  align-self: center;
}
.wordmark .sub {
  color: var(--ink-dim);
  font-weight: 600;
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.topbar .spacer { flex: 1; }

.linkbtn {
  display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: var(--t-ctrl); font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass);
  transition: color .18s, border-color .18s, background .18s;
}
.linkbtn:hover { color: var(--ink); border-color: rgba(11, 122, 86, .5); background: #fff; }
.linkbtn:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; }

/* ------------------------------------------------------------------- stage */

.fw-stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: 1;
  /* The copy band is floor in slight shadow, so the film above it reads as lit. */
  background: var(--bg);
}

/* Only reachable in the computed-camera fallback, where there is no film to sit on. */
.fw-sky {
  position: absolute; inset: -10%;
  /* It is decoration spanning the whole stage, so it must never take a click. On the served
     page it is display:none and this never mattered; in the single-file export it is live,
     and it was swallowing hit-testing across the entire viewport. */
  pointer-events: none;
  background:
    radial-gradient(58% 48% at 22% 18%, rgba(99, 47, 128, 0.10), transparent 62%),
    radial-gradient(52% 44% at 82% 74%, rgba(11, 122, 86, 0.08), transparent 60%);
}

.fw-hero {
  position: absolute;
  top: 0; left: 0;
  transform-origin: 0 0;
  will-change: transform, filter;
  user-select: none;
  -webkit-user-drag: none;
}

/* The rendered flight, held in the upper band rather than full-bleed.

   Measured on the real frames, there is no reliably empty region inside this film: at the
   healthcare+travel stop it is 81 percent ink with ZERO empty columns across the lower half.
   So no amount of clever text placement avoids collision while the film fills the viewport -
   the film has to stop filling it. */
.fw-video {
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: var(--film-w);
  height: var(--film-h);
  /* `contain`, not `cover`: cover was discarding 48 percent of a 16:9 frame in a 3.4:1
     band and beheading the dome at the opening stop. The element is now itself 16:9, so
     nothing is letterboxed and the mask below lands on the film's true edges. */
  object-fit: contain;
  object-position: 50% 50%;
  /* `left` is in the list because the split shell moves it when --left changes: transitioned
     width but not position, the film eased its size while teleporting sideways, and at the
     table stops it stepped partly off the viewport edge for a frame. */
  transition: height .55s cubic-bezier(.3,.7,.3,1),
              width .55s cubic-bezier(.3,.7,.3,1),
              left .55s cubic-bezier(.3,.7,.3,1);
  z-index: 2;
  pointer-events: none;
  /* All four edges dissolve into the page. The studio tone drifts between stops (#f7f4f9
     to #d6d7da measured), so a feather beats colour-matching: whatever grey a frame ends
     on, no rectangle edge is visible.

     The numbers are measured against the frames: the tightest side margin is the umbrella
     globe at 13.4 percent in, so 76px clears it; the dome reaches 1.8 percent from the
     top at the opening stop, so the top ramp has to stay under 26px; 64px at the bottom
     dissolves the contact shadows but leaves the globe bases solid, which is what sells
     them as standing on the floor.

     Those px values are CLAMPED against the film's own box, because the film is not one
     size: fitFilm gives each stop its own, anywhere from 22vh to 74vh, and the split shell
     scales it a further 1.14x. At the small end a flat 64px bottom ramp was swallowing over
     a third of the frame. The clamps hold the measured values wherever there is room and
     shrink proportionally where there is not.

     Widened once the page floor started tracking the film (see --bg-base). The two work
     together: the ground removes most of the step, and a longer feather puts whatever is
     left inside a gradient instead of on an edge, where a two-level difference is invisible
     rather than a visible rectangle. */
  --ramp-t: clamp(26px, calc(var(--film-h) * 0.095), 44px);
  --ramp-b: clamp(34px, calc(var(--film-h) * 0.125), 92px);
  --ramp-x: clamp(64px, calc(var(--film-w) * 0.14), 150px);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0, #000 var(--ramp-t), #000 calc(100% - var(--ramp-b)), transparent 100%),
    linear-gradient(to right, transparent 0, #000 var(--ramp-x), #000 calc(100% - var(--ramp-x)), transparent 100%);
  -webkit-mask-composite: source-in;
  -webkit-mask-repeat: no-repeat;
  mask-image:
    linear-gradient(to bottom, transparent 0, #000 var(--ramp-t), #000 calc(100% - var(--ramp-b)), transparent 100%),
    linear-gradient(to right, transparent 0, #000 var(--ramp-x), #000 calc(100% - var(--ramp-x)), transparent 100%);
  mask-composite: intersect;
  mask-repeat: no-repeat;
}

/* The decoder gave up. Retire the element outright rather than leave a dead poster sitting
   over the computed-camera fallback that has just taken its place. */
.fw-video-dead .fw-video { display: none; }
.fw-has-video .fw-hero,
.fw-has-video .fw-sky { display: none; }
/* paintCopy stops updating the floating tiles once the film decodes, but nothing was ever
   hiding them: any config carrying both `still` and `video` would freeze every globe at its
   last computed opacity, stacked centre-screen on top of the film. */
.fw-has-video .fw-tile { display: none; }

/* Both retired by the light treatment: they existed to rescue white text off a bright
   backdrop, and nothing is layered over anything any more. Kept as nodes so the engine can
   go on writing opacity to them without a null check. */
.fw-veil, .fw-scrim { display: none; }

.fw-tile {
  z-index: 4;
  position: absolute;
  left: 50%; top: calc(var(--film-h) * 0.5);
  height: min(44vh, 360px);
  width: auto;
  transform: translate3d(-50%, -50%, 0);
  filter: drop-shadow(0 22px 40px rgba(14, 16, 41, 0.22));
  will-change: transform, opacity;
  user-select: none;
  -webkit-user-drag: none;
}

/* The stop announcer. A live region has to be in the accessibility tree and out of the
   visual one, and display:none or visibility:hidden would take it out of both. */
.fw-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Inline vendor marks carry no width/height attributes, so without a rule they fall back to
   the SVG default of 300x150 and blow the layout apart. This is the floor that stops that
   happening no matter which sheet matched.

   Written as a bare `svg` under the mark wrappers rather than one selector per block. The
   list form has now failed twice - each new block that prints a vendor mark was a new
   selector somebody had to remember, and both times the symptom was a 300px logo wedged
   through a table. Anything that mounts a mark inherits the floor. */
.fw-col-model svg,
.fw-case-model svg,
.fw-bar-name svg,
.fw-m-sysin svg { width: 1em; height: 1em; flex: none; display: block; }
/* The vendors' real marks. Same 1em floor as the drawn ones they replaced, and the same
   reason: an <img> with no width in a table cell takes its intrinsic size. */
.fw-vmark { width: 1em; height: 1em; flex: none; display: block; object-fit: contain; }
.fw-col-mk .fw-vmark, .fw-open-nm .fw-vmark { width: 14px; height: 14px; }

/* --------------------------------------------------------------- autoplay */

.fw-auto {
  position: absolute;
  left: clamp(16px, 3vw, 34px);
  bottom: clamp(16px, 3vh, 30px);
  z-index: 9;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px 8px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.78);
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .18s, border-color .18s, background .18s;
}
.fw-auto:hover { color: var(--ink); border-color: rgba(14, 16, 41, .3); background: #fff; }
.fw-auto:focus-visible { outline: 2px solid var(--magenta); outline-offset: 3px; }
.fw-auto.is-on { color: var(--ink); border-color: var(--magenta); }

/* A triangle that becomes two bars, drawn in borders so there is no icon font and no SVG
   to keep in sync with the palette. */
.fw-auto-icon {
  width: 0; height: 0;
  border-style: solid;
  border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent currentColor;
  transition: border-width .18s, width .18s, height .18s;
}
.fw-auto.is-on .fw-auto-icon {
  width: 8px; height: 10px;
  border-width: 0 0 0 8px;
  border-left-color: currentColor;
  box-shadow: inset 3px 0 0 rgba(255, 255, 255, .001);
  background:
    linear-gradient(currentColor, currentColor) left / 3px 100% no-repeat,
    linear-gradient(currentColor, currentColor) right / 3px 100% no-repeat;
  border-left-width: 0;
}

@media (prefers-reduced-motion: reduce) {
  .fw-auto-icon { transition: none; }
  /* The transcript highlights tween so a turn change does not snap. A reader who has asked
     for less motion wants the state, not the tween: the class still lands, it just lands
     instantly. The JS follow-scroll makes the same choice off the same query. */
  .fw-turn, .fw-turn.is-now,
  .fw-tp-turn, .fw-tp-turn.is-now { transition: none; }
}

/* ------------------------------------------------------------- world index */

/* The six worlds, always present. Vertical on the right, where the stacked shell leaves a
   clear margin: the film is a centred 16:9 box and the copy is bound to its width, so this column never has to fight either of them. */ .fw-globes{
  position: absolute;
  right: clamp(10px, 1.6vw, 26px);
  top: 50%;
  transform: translateY(-50%);
  z-index: 8;
  display: flex;
  flex-direction: column;
  gap: clamp(2px, 0.8vh, 10px);
}

.fw-globe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.0px;
  background: none;
  border: 0;
  padding: 4px 2.0px;
  cursor: pointer;
  font: inherit;
  transition: transform .3s;
}
/* Dimmed and desaturated until the flight reaches it. Greyed-out reads as "not yet", which
   is exactly the state, and it keeps the lit pair unmistakable.

   The fade is on the CHILDREN, never on the button. `opacity` composites the element's own
   outline too, so with .55 on the button the focus ring computed to 2.6:1 against the page -
   under the 3:1 that WCAG 2.2 SC 1.4.11 requires of a non-text indicator. */
.fw-globe img { opacity: 1; filter: none; transition: opacity .3s, filter .3s; }
/* .82, not .5. At .5 this measured 2.08:1 - less than half the AA minimum - on the page's
   only wayfinding for the six worlds. */
.fw-globe .fw-globe-label { opacity: 1; transition: opacity .3s, color .3s; }
.fw-globe img {
  width: clamp(30px, 3.4vw, 52px);
  height: auto;
  display: block;
}
/* Named all the way down, not only on hover. The strip is the page's answer to "six
   worlds", and an unlabelled thumbnail of a snow globe does not say Insurance. */
.fw-globe-label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
}
.fw-globe:hover img { opacity: 1; filter: saturate(1.1); }
.fw-globe:hover .fw-globe-label { opacity: 1; filter: none; }
.fw-globe.is-on { transform: scale(1.14); }
.fw-globe.is-on img { opacity: 1; filter: none; }
.fw-globe.is-on .fw-globe-label { opacity: 1; filter: none; color: var(--ink); }
.fw-globe:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; border-radius: 8px; }

@media (prefers-reduced-motion: reduce) {
  .fw-globe, .fw-globe img, .fw-globe .fw-globe-label { transition: none; }
}

/* -------------------------------------------------------------------- copy */

/* Below the film, never over it. This is the whole fix for the overlap: the two own
   separate bands of the viewport and cannot collide at any scroll position. */
.fw-copy {
  position: absolute;
  left: 50%;
  top: var(--film-h);
  bottom: 0;
  transform: translateX(-50%);
  /* Bound to the film's width so the two share an outer edge - the page gets a spine,
     and the pair columns drop from a 79-character measure to about 51.
     FLOORED at 680px, and that floor is load-bearing rather than cosmetic. Without it the
     two sizes chase each other: a tall panel shrinks the film, the narrower film narrows
     this band, the narrower band rewraps the panel taller, and fitFilm's three passes
     converge on a degenerate state - measured at 1920 wide, the results panel reached
     1425px and drove the film down to its 22vh floor. The floor breaks the loop. */
  width: min(calc(var(--vw) - clamp(32px, 8vw, 140px)), max(var(--film-w), 680px));
  z-index: 6;
  display: grid;
  align-items: start;
  padding: clamp(16px, 2.6vh, 28px) 0 clamp(14px, 2vh, 24px);
  /* At high zoom the band can still lose the race. Scrolling it is a far better
     failure than the stage silently clipping the primary CTA. */
  overflow-y: auto;
  /* Chaining, not containment. If this band ever does overflow, containment stops the
     leftover wheel reaching the page, so the flight freezes and the wheel reads as broken.
     Chaining degrades instead: the band takes its slack, then the page carries on. */
  overscroll-behavior: auto;
  overflow-x: hidden;
  /* No visible bar. This is a safety net for the rare overflow, not a scroller anyone is
     meant to drive, and a 15px gutter reserved for 6px of overflow reads as a second
     scrollbar on a page whose whole mechanic is the page's own. */
  scrollbar-width: none;
  /* The panels inside decide their own layout from THIS width, not the viewport's. On the
     split shell the band is roughly half the window, so a viewport media query is the wrong
     instrument: it would set a two-column results panel inside a 615px column. */
  container-type: inline-size;
}
.fw-copy::-webkit-scrollbar { width: 0; height: 0; }

.fw-panel {
  grid-area: 1 / 1;
  align-self: start;
  width: 100%;
  will-change: transform, opacity;
}

/* Opening and closing statements are centred and narrow; the pair stops are not, because
   two columns under two globes want the full band. */
.fw-panel-hero {
  text-align: center;
  justify-self: center;
  max-width: 780px;
}
/* The opening statement is a lede, not body copy: set a step larger and on a wider measure.
   At the shared 52ch it broke into six short centred lines, which is the one place ragged
   symmetry looks like an accident rather than a choice. */
.fw-panel-hero .fw-body {
  margin-inline: auto;
  font-size: 17px;
  line-height: 1.52;
  max-width: min(68ch, 100%);
  color: var(--ink-soft);
  text-wrap: pretty;
}
.fw-panel-hero .fw-tags,
.fw-panel-hero .fw-cta { justify-content: center; }

.fw-eyebrow {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 8px;
}
.fw-title {
  margin: 0 0 8px;
  font-size: var(--t-display);
  line-height: 1.06;
  letter-spacing: -0.024em;
  font-weight: 700;
  text-wrap: balance;
  color: var(--ink);
}
.fw-body {
  margin: 0;
  font-size: var(--t-body);
  line-height: 1.58;
  color: var(--ink-soft);
  max-width: 52ch;
}
.fw-note {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-dim);
  border-left: 2px solid var(--line);
  padding-left: 12px;
  max-width: 46ch;
}

/* ------------------------------------------------------------- paired copy */

.fw-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 3vw, 44px);
  margin-top: 12px;
}
.fw-col-eyebrow {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 8px;
}
.fw-col-title {
  margin: 0 0 8px;
  font-size: var(--t-lead);
  line-height: 1.22;
  letter-spacing: -0.012em;
  font-weight: 600;
  text-wrap: balance;
  color: var(--ink);
}
.fw-col-model {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  padding: 3.0px 8px 3.0px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.55);
  font-variant-numeric: tabular-nums;
}
.fw-col-model svg { width: 12px; height: 12px; flex: none; }
/* A column with no run says so in the same slot, rather than leaving a gap that would read
   as an oversight. Dashed mark, dimmed, so it cannot be mistaken for a result. */
.fw-col-model.is-none { color: var(--ink-dim); border-style: dashed; background: none; }

/* The leading system and the figure it leads on, on one line. Naming a model without the
   number it earned is exactly the loose framing this page exists to correct, so the two
   are bound into a single row that cannot be set without both. */
.fw-col-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 8px;
  margin: 0 0 8px;
}
.fw-col-line .fw-col-model { margin: 0; }
.fw-col-stat { display: flex; align-items: baseline; gap: 4px; }
.fw-col-stat strong {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.fw-col-stat span {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.fw-col-body {
  margin: 0;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
  /* Measured at 1920 this column ran to 84 characters a line with no cap at all, against
     the 45-75 that reads comfortably. The cap binds only on wide screens. */
  max-width: 62ch;
}

/* ------------------------------------------------------------------- cases */

/* The grid holds runs from one world only, so it has to say which - otherwise six
   Pathfinding walks sit under a heading naming two worlds and read as evidence for both. */
.fw-cases-label {
  margin: 16px 0 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.fw-cases-label + .fw-cases { margin-top: 8px; }

.fw-cases {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 16px;
}

.fw-case {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 12px 12px;
  background: rgba(255, 255, 255, 0.62);
  /* Every card is in the DOM from the start and revealed by step, so nothing loads or
     reflows mid-scroll and the grid never jumps. */
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .45s, transform .45s;
  grid-column: span 1;
  min-width: 0;
}
/* A pass card carries a magenta edge, a fail card a grey one. The colour is doing the same
   job as the word next to it, never on its own. */
.fw-case.is-ok  { border-left: 3px solid var(--magenta); }
.fw-case.is-bad { border-left: 3px solid var(--ink-dim); }

/* Step 0 - the base case alone, full width. It is the control, and it should be read before
   anything is compared to it. */
[data-step="0"] .fw-case.is-ok[data-at="0"] { opacity: 1; transform: none; grid-column: span 3; }
/* Step 1 - it contracts to a third and the roadblocked run arrives beside it. */
[data-step="1"] .fw-case.is-ok[data-at="0"],
[data-step="1"] .fw-case.is-ok[data-at="1"] { opacity: 1; transform: none; }
/* Step 2 - all three conditions, still only the runs that worked. */
[data-step="2"] .fw-case.is-ok[data-at="0"],
[data-step="2"] .fw-case.is-ok[data-at="1"],
[data-step="2"] .fw-case.is-ok[data-at="2"] { opacity: 1; transform: none; }
/* Step 3 - the failures drop in underneath, so each column is the same job done both ways. */
[data-step="3"] .fw-case { opacity: 1; transform: none; }

.fw-case-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-bottom: 8px;
}
.fw-case-cond {
  font-size: 14px; font-weight: 700; letter-spacing: -0.005em; color: var(--ink);
}
.fw-case-verdict {
  font-size: 14px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-dim); white-space: nowrap;
}
.fw-case.is-ok .fw-case-verdict { color: var(--magenta); }

.fw-case-model {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 14px; font-weight: 600; color: var(--ink-soft);
  margin-bottom: 8px;
}
.fw-case-model svg { width: 11px; height: 11px; flex: none; }

/* A fixed two-up grid, not a wrapping flex row. Wrapping meant the second figure sat beside
   the first on one card and under it on the next, purely on label length, so three cards
   meant to be read against each other had their numbers on three different baselines. */
.fw-case-nums { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 8px; }
.fw-case-num { display: flex; flex-direction: column; gap: 1.0px; min-width: 0; }
.fw-case-num strong {
  font-size: 14px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.fw-case-num span {
  font-size: 14px; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-dim);
  line-height: 1.25;
}

/* The mechanism, in the paper's own words, under the numbers it explains. Figure 6 states
   the mechanism beneath each failing excerpt for the same reason: a block count on its own
   says a run was bad, not what went wrong in it. */
.fw-case-note {
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  font-size: 14px;
  line-height: 1.42;
  color: var(--ink-dim);
}

/* Dark shells reuse the whole grid, only the surfaces change. */
:is(.fw-shell-theatre, .fw-shell-canvas) .fw-case {
  background: rgba(246, 241, 249, 0.05);
  border-color: rgba(246, 241, 249, 0.16);
}

@media (prefers-reduced-motion: reduce) {
  .fw-case { transition: none; }
}

/* ----------------------------------------------------------------- metrics */

/* Four columns is the fewest that still shows a different system winning each one, which
   is the paper's headline and the reason no composite is ever formed. Bold marks the best
   in a column and is computed from the values, so it cannot end up on the wrong row. */
/* Prose left, table right. The hero panel is centred everywhere else on the page; here it
   is not, because a right-aligned numeric column under a centred paragraph reads as two
   unrelated objects. Both halves go left-aligned and share a baseline. */
.fw-panel-metrics {
  text-align: left;
  max-width: none;
  justify-self: stretch;
}
/* Side by side only where the prose still gets a readable measure. Below this the table
   goes back under the paragraph, which is taller but never cramped. */
@container (min-width: 660px) {
  .fw-panel-metrics {
    display: grid;
    /* max-content, not a fraction: the numeric cells are nowrap, so a fractional track that
       lands narrower than the table's natural width does not compress it, it lets it spill -
       which is how the caption ended up cut mid-word at the column edge. */
    grid-template-columns: minmax(0, 1fr) max-content;
    gap: clamp(22px, 3vw, 44px);
    align-items: start;
  }
  .fw-panel-metrics .fw-metrics { margin-top: 4px; }
}
.fw-panel-metrics .fw-body { margin-inline: 0; max-width: 46ch; }
/* The prose column is roughly half a hero panel, so the display size that balances a
   full-width statement sets four-word lines here. */
.fw-panel-metrics .fw-title { font-size: clamp(20px, 1.9vw, 32px); }
.fw-panel-metrics .fw-tags,
.fw-panel-metrics .fw-cta { justify-content: flex-start; }
.fw-panel-metrics .fw-metrics { margin-top: 4px; }

.fw-metrics {
  width: 100%;
  margin-top: 16px;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
}
.fw-metrics caption {
  caption-side: bottom;
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-dim);
  text-align: left;
}
.fw-metrics th,
.fw-metrics td {
  padding: 8px 4px;
  border-bottom: 1px solid var(--line);
  text-align: right;
  white-space: nowrap;
}
.fw-metrics thead th {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-dim);
  border-bottom-color: rgba(14, 16, 41, 0.28);
}
.fw-metrics tbody tr:last-child th,
.fw-metrics tbody tr:last-child td { border-bottom: 0; }
.fw-metrics .fw-m-sys {
  text-align: left;
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
}
.fw-metrics .fw-m-sysin {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fw-metrics td { color: var(--ink-soft); }
.fw-metrics td.is-best { color: var(--ink); font-weight: 700; }

:is(.fw-shell-theatre, .fw-shell-canvas) .fw-metrics thead th {
  border-bottom-color: rgba(246, 241, 249, 0.32);
}
/* -------------------------------------------------------------- tags / cta */

.fw-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.fw-cases-label + .fw-tags { margin-top: 8px; }
.fw-tag {
  font-size: var(--t-micro); font-weight: 600; letter-spacing: 0.02em;
  color: var(--ink-soft);
  padding: 4px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.66);
}
/* A tag the note qualifies. Marked by a leading rule and the accent, never by colour alone -
   the note names the same two by name. */
.fw-tag.is-marked {
  color: var(--ink);
  border-color: rgba(11, 122, 86, 0.42);
  box-shadow: inset 3px 0 0 var(--magenta);
  padding-left: 12px;
}
/* A run of eleven names is a list, not a row of buttons: set tighter so it reads as one
   object rather than eleven. */
.fw-tags.is-labelled { gap: 8px; }
.fw-tags.is-labelled .fw-tag { font-size: 14px; padding: 4px 8px; }
.fw-tags.is-labelled .fw-tag.is-marked { padding-left: 12px; }
.fw-cta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.fw-btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--t-ctrl); font-weight: 600;
  text-decoration: none;
  color: var(--ink);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.82);
  transition: transform .16s, border-color .16s, background .16s;
}
.fw-btn:hover { transform: translateY(-1px); border-color: rgba(14, 16, 41, .3); background: #fff; }
.fw-btn-primary {
  background: var(--accent);
  border-color: transparent;
  color: #fff;
}
.fw-btn-primary:hover { filter: brightness(1.1); }
.fw-btn:focus-visible { outline: 2px solid var(--magenta); outline-offset: 3px; }

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

.fw-rail[hidden] { display: none !important; }
.fw-rail {
  position: absolute;
  left: clamp(16px, 3vw, 34px);
  /* never allowed to reach the copy column */
  max-width: calc(50% - var(--film-w) / 2 - 24px);
  top: calc(var(--film-h) + clamp(16px, 2.6vh, 28px));
  display: flex; flex-direction: column; gap: 2.0px;
  z-index: 8;
}
.fw-dot {
  display: flex; align-items: center; gap: 8px;
  background: none; border: 0; cursor: pointer;
  padding: 8px 0;
  font: inherit;
  color: var(--ink-dim);
  transition: color .2s;
}
.fw-dot-mark {
  width: 9px; height: 9px; border-radius: 50%;
  background: currentColor;
  opacity: .8;
  transition: transform .22s, opacity .22s, background .22s;
}
.fw-dot-label {
  font-size: var(--t-micro); font-weight: 600; letter-spacing: .04em;
  opacity: .5;
  transition: opacity .22s;
  white-space: nowrap;
}
.fw-dot:hover { color: var(--ink-soft); }
.fw-dot:hover .fw-dot-label { opacity: 1; }
.fw-dot.is-on { color: var(--ink); }
.fw-dot.is-on .fw-dot-mark { background: var(--magenta); opacity: 1; transform: scale(1.5); }
.fw-dot.is-on .fw-dot-label { opacity: 1; }
.fw-dot:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; border-radius: 6px; }

/* Under the film, on its axis. Hung off the right edge it landed on top of the globe index -
   which is also right-anchored - and printed "FLY IN" across the Travel globe and its label
   on the one frame every reader sees first. */
.fw-hint {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  top: calc(var(--film-h) - 46px);
  font-size: var(--t-micro); font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-dim);
  transition: opacity .4s;
  z-index: 8;
  pointer-events: none;
}
.fw-hint::after {
  content: ""; display: block; margin: 8px auto 0;
  width: 1px; height: 20px;
  background: linear-gradient(to bottom, var(--ink-dim), transparent);
}

.fw-spacer { position: relative; z-index: 0; }

/* ------------------------------------------------------------------ mobile */

@media (max-width: 900px) {
  :root { --film: min(46vh, calc(100vh - 300px)); }
  /* On a phone the copy is the full width of the window, so the index cannot stay in a side
     margin that no longer exists - at 430px it sat at z-index 8 directly over the copy's
     right-hand words. Index and rail share one strip between the film and the copy instead,
     and the copy starts below it. */
  .fw-copy {
    width: calc(var(--vw) - 30px);
    top: calc(var(--film-h) + 38px);
    align-items: start;
    padding-top: 8px;
  }
  .fw-panel { align-self: start; }
  .fw-title { font-size: clamp(20px, 6.4vw, 32px); }
  .fw-pair { grid-template-columns: 1fr; gap: 16px; margin-top: 12px; }
  .fw-pair .fw-col + .fw-col { border-top: 1px solid var(--line); padding-top: 12px; }
  .fw-globes {
    left: auto; right: 10px;
    top: calc(var(--film-h) + 4px);
    transform: none;
    flex-direction: row;
    gap: 4px;
  }
  .fw-globe { padding: 0; }
  .fw-globe img { width: clamp(24px, 6.6vw, 32px); }
  .fw-globe-label { display: none; }
  .fw-rail[hidden] { display: none !important; }
.fw-rail { left: 10px; top: calc(var(--film-h) + 12px); flex-direction: row; gap: 8px; max-width: none; }
  .fw-dot-label { display: none; }
  .fw-hint { top: calc(var(--film-h) - 54px); }
}

/* Between the phone breakpoint and a wide desktop there is not enough left margin for a
   labelled rail without it reaching the copy. Dots only, until there is room. */
@media (min-width: 901px) and (max-width: 1340px) {
  .fw-dot-label { display: none; }
}

/* ===========================================================================
   The stage layers
   ---------------------------------------------------------------------------
   The left half is not always the film. A page about a paper opens on the paper,
   and the six per-world results stops want the world's NAME, not another frame
   of a flight that has already landed. Each layer occupies the same box and is
   cross-faded by flight.js from the panels' own visibility, so a layer is never
   up under copy that has gone.
   ========================================================================= */

/* A per-section still, in the film's box. */
.fw-art {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  object-fit: contain;
  /* The same feather the film gets. This tile is a rendered JPEG on a white ground, and
     unmasked it reads as a white rectangle pasted onto the page rather than as a picture
     standing on the same floor as everything else. */
  --ramp-t: clamp(18px, 8%, 40px);
  --ramp-b: clamp(22px, 11%, 70px);
  --ramp-x: clamp(40px, 12%, 120px);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0, #000 var(--ramp-t), #000 calc(100% - var(--ramp-b)), transparent 100%),
    linear-gradient(to right, transparent 0, #000 var(--ramp-x), #000 calc(100% - var(--ramp-x)), transparent 100%);
  -webkit-mask-composite: source-in;
  -webkit-mask-repeat: no-repeat;
  mask-image:
    linear-gradient(to bottom, transparent 0, #000 var(--ramp-t), #000 calc(100% - var(--ramp-b)), transparent 100%),
    linear-gradient(to right, transparent 0, #000 var(--ramp-x), #000 calc(100% - var(--ramp-x)), transparent 100%);
  mask-composite: intersect;
  mask-repeat: no-repeat;
}
.fw-art[hidden] { display: none; }
.fw-foot {
  margin: 12px 0 0;
  font-size: 14px;
  letter-spacing: 0.05em;
  color: var(--ink-dim);
}

.fw-logo, .fw-mark {
  position: absolute;
  pointer-events: none;
  display: flex;
  flex-direction: column;
}
.fw-logo {
  z-index: 2;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.fw-logo[hidden], .fw-mark[hidden] { display: none; }

/* --logo-t travels 0 -> 1 across the opening leg, written by flight.js from the same band
   geometry as the camera. At 0 the mark is small and sits with the title; at 1 it has grown
   and taken the left half. One number, so the growth is scroll-driven rather than a
   transition that would run at its own speed and arrive whenever it liked. */
:root { --logo-t: 1; }

.fw-logo {
  /* Three things travel together on --logo-t, which is why they are written as one
     interpolation each rather than as two keyframed states: at 0 the mark is small and sits
     ABOVE the title, the way a paper's own front matter carries it; at 1 it has grown and
     taken the left half. Anything in between is a real position, so the reader sees it
     move rather than jump. */
  --logo-sm: clamp(76px, 9vw, 116px);
  --logo-lg: min(var(--left, 50vw) * 0.62, 46vh);
  --logo-w: calc(var(--logo-sm) + (var(--logo-lg) - var(--logo-sm)) * var(--logo-t));
  top: calc(15% + 35% * var(--logo-t));
  left: calc(50% + (var(--left, 50vw) / 2 - 50%) * var(--logo-t));
  width: var(--logo-w);
  transform: translate(-50%, -50%);
}
.fw-logo-img {
  width: 100%;
  height: auto;
  display: block;
  /* The dome is a knocked-out render on transparency, so it needs its own contact shadow
     or it reads as pasted onto the page rather than standing on it. */
  filter: drop-shadow(0 calc(var(--logo-w) * 0.045) calc(var(--logo-w) * 0.07) rgba(14, 16, 41, .17));
}

/* A NAME PLATE over the picture, the way a documentary titles a place: the film keeps
   flying and the plate says which world you have arrived in. It used to be a full-height
   panel that replaced the film, which is what froze the flight for six consecutive stops
   and made the whole middle of the page read as a slide deck.
   Anchored to the bottom of the film so it reads as a caption on the image rather than as
   a floating heading, and z-index above the film for the same reason. */
.fw-mark {
  z-index: 3;
  left: 0;
  right: 0;
  bottom: 8%;
  align-items: flex-start;
  gap: 4px;
  padding: 0 clamp(20px, 3vw, 48px);
  text-align: left;
}
.fw-mark-globe { display: none; }         /* the film already shows the world */
.fw-mark-name {
  font-size: clamp(20px, 3.4vw, 44px);
  font-weight: 700;
  letter-spacing: -0.018em;
  line-height: 1.04;
  color: var(--ink);
  text-wrap: balance;
}
.fw-mark-name::before {
  /* A short rule above the name, the width of the accent. It gives the plate a top edge to
     sit against, which is what stops a heading floating over a pale film looking dropped in
     rather than placed. */
  content: "";
  display: block;
  width: 34px;
  height: 3px;
  margin-bottom: 8px;
  border-radius: 2px;
  background: var(--accent, var(--magenta));
}
.fw-mark-line {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 42ch;
  text-wrap: pretty;
}

/* ===========================================================================
   Front matter
   ========================================================================= */

.fw-abstract {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.fw-abstract-h {
  margin: 0 0 8px;
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.fw-abstract-p {
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 1.62;
  color: var(--ink-soft);
  max-width: 68ch;
  text-wrap: pretty;
}
.fw-abstract-p:last-child { margin-bottom: 0; }

/* ===========================================================================
   THE OPENING
   ---------------------------------------------------------------------------
   Three by three, the mark in the middle cell, one viewport tall, in normal flow.
   It scrolls up and off with the reader - it does not fade, it does not translate,
   and nothing inside it moves. The flight starts underneath it.
   ========================================================================= */

.fw-open {
  position: relative;
  z-index: 20;
  background: var(--bg-base);
  /* Exactly one viewport. `min-height` let the tiles push it to 1689px - nearly two
     screens - so the reader met half a matrix and had to scroll to see the rest of what is
     supposed to be one screen. The cells clip instead; each one is a summary and the page
     below is where the detail lives. */
  height: 100vh;
  max-height: 100vh;
  /* A grid of title-then-matrix, and overflow hidden.
     As a centred flex column it overflowed BOTH ways when the tiles were taller than the
     viewport, so the title was pushed off the top of the screen and the transparent middle
     cell showed the fixed stage behind it - the film, not the mark. */
  /* A flex column, not a two-row grid. The grid was authored for exactly two children,
     title and matrix; the byline made three and the matrix fell into an implicit third row
     that the fixed 100vh had no space for, so the byline was painted underneath it. A
     column takes however many children it is given and hands the leftover to the matrix. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: clamp(70px, 8.5vh, 100px) clamp(16px, 2.4vw, 40px) clamp(16px, 2.6vh, 32px);
}
.fw-open-in { display: contents; }
/* The matrix takes whatever the title and byline leave. */
.fw-open-grid { flex: 1 1 auto; min-height: 0; }

/* One word, two capitals, and a capital on the question. Set in the display size and in
   the page's heaviest weight: at book weight this sat on a screen of bold tiles and looked
   like a caption for them. */
.fw-open-title {
  margin: 0 0 clamp(10px, 1.6vh, 20px);
  font-size: clamp(32px, 2.9vw, 44px);
  font-weight: 800;
  letter-spacing: -0.022em;
  line-height: 1.1;
  color: var(--ink);
  text-wrap: balance;
}
.fw-open-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* The middle row is the widest of the three because the mark lives in it, and a mark
     letterboxed into a third of the height reads as a thumbnail rather than as the thing
     the other eight tiles are about. */
  grid-template-rows: minmax(0, 1fr) minmax(0, 1.45fr) minmax(0, 1fr);
  gap: clamp(8px, 0.9vw, 14px);
  flex: 1 1 auto;
  min-height: 0;
}
.fw-open-cell {
  min-width: 0;
  min-height: 0;
  padding: clamp(10px, 1vw, 16px) clamp(11px, 1.1vw, 18px);
  border-radius: 16px;
  background: var(--paper);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* The mark, in the middle. No card around it: it is the object the other eight describe. */
.fw-open-cell.is-mark {
  background: none;
  border: 0;
  padding: 0;
  position: relative;
}
/* Absolute, so the artwork can never decide the row's height. As a flex child with
   width:100% it took its own square aspect and spilled two rows deep through the tiles
   above and below it. */
.fw-open-mark {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* EVERYTHING on this screen is set bold. Bold OR capitals, never both, so the labels are
   the only thing in caps and they are the only thing that is not bold-and-cased. */
.fw-open-lab {
  font-size: clamp(14px, 0.72vw, 14px);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--grey);
  margin-bottom: 8px;
}
.fw-open-text {
  margin: 0;
  font-size: clamp(14px, 0.88vw, 14px);
  font-weight: 600;
  line-height: 1.5;
  color: var(--text);
  overflow: hidden;
}
.fw-open-cell:has(.fw-open-fig) .fw-open-i { display: inline-flex; margin-right: 16px; }
.fw-open-i { display: flex; gap: 8px; font-size: clamp(14px, 0.85vw, 14px); line-height: 1.5; }
.fw-open-i b { color: var(--ink); font-weight: 800; flex: none; }
.fw-open-i span { color: var(--soft); font-weight: 600; min-width: 0; }
.fw-open-chips { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start; }
.fw-open-chip {
  font-size: clamp(14px, 0.72vw, 14px);
  font-weight: 700;
  line-height: 1;
  padding: 8px 8px;
  border-radius: 100px;
  background: var(--bg-base);
  color: var(--text);
}
.fw-open-fig {
  flex: 1 1 auto;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  margin-top: 8px;
}
.fw-open-worlds { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.0px 8px; }
.fw-open-w { display: flex; align-items: center; gap: 8px; min-width: 0; }
.fw-open-wimg { width: clamp(22px, 2vw, 34px); height: auto; flex: none; }
.fw-open-w span { font-size: clamp(14px, 0.85vw, 14px); font-weight: 700; color: var(--ink); }


/* One colour per SYSTEM, not one per chart. A reader who learns the colour on the opening
   plot then reads it on every bar, every tile and every table mark for the rest of the page,
   which is the only way five systems across nine figures stay legible. Vibrant on purpose:
   these have to survive a projector as well as a laptop. */
:root {
  /* THE PAPER'S COLOURS. Hand-picking a colour anywhere else in this file is a bug: a
     system's hue is its identity and has to survive every chart on the page unchanged.
     The pink and violet that used to live here carried no meaning, are not in the paper,
     and sat directly under the vendors' own brand marks, which they fought. */
  --sys-grok:   #17171f;   /* Voice Think Fast      */
  --sys-gpt:    #0B7A56;   /* GPT-Realtime-2.1      */
  --sys-mini:   #41C596;   /* GPT-Realtime-2.1-mini */
  --sys-gemini: #4285F4;   /* Gemini-3.1-Flash-Live */
  --sys-nova:   #F28C00;   /* Nova 2 Sonic          */

  /* Pillar accents. Section headers and metric cards only, never a system bar. */
  --pillar-agentic:  #4F277C;
  --pillar-dynamics: #4285F4;
  --pillar-natural:  #F28C00;
  /* The same three hues, darkened until they clear 4.5:1 as TEXT on both grounds.
     A fill can be vivid; a 12px label in the same colour cannot. */
  --pillar-agentic-t:  #4F277C;
  --pillar-dynamics-t: #376fcc;
  --pillar-natural-t:  #a66000;

  --ink: #17171f;  --sub: #4c4c58;  --muted: #6f6f79;
  --grid: #ececf1; --surface: #ffffff; --canvas: #f6f6f9;

  /* The old names, kept so every existing rule resolves, but pointed at the paper's
     hues. This is what removes pink and violet from the page in one place. */
  --sys-vtf: var(--sys-grok);
  --sys-o21: var(--sys-gpt);
  --sys-gem: var(--sys-gemini);
}

/* The three pillar plots, in the bottom row. */
.fw-open-cell.is-plot {
  justify-content: flex-start;
  /* The bottom row of tiles is shorter than the top two, and at the old padding the fifth
     bar ran 8px past the card. Nothing clipped is a hard rule, so the tile gives the
     padding back rather than the chart losing a system. */
  padding-bottom: 8px;
  overflow: hidden;
}
.fw-open-cell.is-plot .fw-open-lab { margin-bottom: 0; }
.fw-open-cell.is-plot:has(.fw-open-head) .fw-open-hrows { gap: 1.0px; }
.fw-open-cell.is-plot:has(.fw-open-head) .fw-open-htrack { height: 7px; }
.fw-open-cell.is-plot:has(.fw-open-head) .fw-open-hrow { row-gap: 1.0px; }
.fw-open-cols {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 4px; align-items: end; flex: 1 1 auto; min-height: 0;
}
.fw-open-cb { display: grid; grid-template-rows: auto auto 1fr; min-width: 0; height: 100%; }
.fw-open-cv {
  font-size: clamp(14px, 0.78vw, 14px); font-weight: 800; color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: center; margin-bottom: 3.0px;
}
.fw-open-bar {
  /* Sleeker: a column, not a block. The bar is the system's own colour, so the plot is
     read by hue as well as by height and the name row underneath stops being load-bearing. */
  align-self: end; width: 58%; max-width: 42px; min-height: 3px;
  margin: 0 auto;
  border-radius: 5px 5px 2px 2px;
  background: var(--sys, var(--tint));
}
.fw-open-names { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px;
  /* Deep enough for a rotated 14px name. At the old height the labels were sheared off at
     the tile's bottom edge, so four of five systems read as "...altime-2.1". */
  height: clamp(58px, 7.2vh, 74px); margin-top: 8px; overflow: visible; }
.fw-open-nc { position: relative; min-width: 0; }
.fw-open-nm {
  position: absolute; top: 4px; right: 4px;
  display: flex; align-items: center; gap: 3.0px;
  font-size: clamp(14px, 0.66vw, 14px); font-weight: 700; line-height: 1;
  color: var(--soft); white-space: nowrap;
  transform: rotate(-45deg); transform-origin: right top;
}
.fw-open-nm svg, .fw-open-nm .fw-vmark { width: 14px; height: 14px; flex: none; display: block; }
/* The mark sits ON the column, above the value, where it is read with the bar rather than
   in the rotated name row where it was 9px and effectively invisible. */
.fw-open-mk { display: flex; justify-content: center; margin-bottom: 3.0px; }
.fw-open-mk .fw-vmark, .fw-open-mk svg { width: 16px; height: 16px; display: block; }


/* The section bar. Named, in the header, following the scroll. */
.fw-nav {
  display: flex;
  align-items: center;
  gap: clamp(2px, 0.5vw, 10px);
  margin-left: auto;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.fw-nav-item {
  font: inherit;
  /* No rule under the words. Half the bar is buttons and half is anchors now that the
     nav spans four pages, and the anchors were arriving with the browser's default
     underline - so the same bar read as two different kinds of control. */
  text-decoration: none;
  font-size: var(--t-ctrl);
  font-weight: 600;
  color: var(--ink-dim);
  background: none;
  border: 0;
  border-radius: 100px;
  padding: 8px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: color .18s, background-color .18s;
}
.fw-nav-item:hover { color: var(--ink); }
.fw-nav-item.is-on { color: var(--ink); background: rgba(14, 16, 41, .07); }
.fw-nav-item:focus-visible { outline: 2px solid var(--magenta); outline-offset: 1px; }

/* ===========================================================================
   The opening summary
   ---------------------------------------------------------------------------
   One screen: what the work is, and the three numbers it claims. Set as soft
   rectangular tiles because three plots and a block of prose on one screen need
   edges to be read as four things rather than one crowded one.

   No captions and no axis titles anywhere in here on purpose. The tile heading
   is the metric, the bars carry their own values, and the prose beside them
   already says what they mean.
   ========================================================================= */

/* The mark itself in the topbar. A magenta dot stood here, which is a decoration nobody
   chose and which sat next to the words it was supposed to be the mark for. */
.wordmark-logo {
  width: 22px; height: 22px; flex: none;
  object-fit: contain; object-position: center;
  margin-right: 2px;
  /* Centred on the lettering, not hung from its baseline.
     The row aligns on the text baseline so "Duplex World" and "BENCHMARK" sit on one
     line, which is right for two runs of type and wrong for a picture: baseline puts a
     22px image's bottom edge on the baseline, which left its centre 6.5px above the
     centre of the words. `vertical-align` was trying to correct that and does nothing at
     all here, because a flex container ignores it. */
  align-self: center;
  /* Centring on the flex LINE leaves it 1.5px below the words' own centre, because the
     line is taller than the type: the descender space belongs to the text, not to the
     picture. Corrected with a transform rather than a margin: a margin moves the line
     too, so the image and the text chase each other and never meet. */
  transform: translateY(-1.5px);
}

/* THE KEYNOTE MATRIX. Three by three, the mark in the middle cell and eight tiles around
   it - the abstract, the three pillar figures, the six worlds, the corpus, the eleven
   conversation types and the reliability decay. One screen that says what the work is and
   shows the evidence for the one sentence that matters.

   The mark is the FILM's own first frame, not a second copy of the artwork, so nothing
   changes size or position when the flight starts moving at the next stop. */
.fw-sum {
  margin-top: 8px;
  display: grid;
  /* Three columns in the right half. Two put the seven tiles on four rows and the block
     ran 43px past the fold; three fits them on three - abstract across the top, the three
     text tiles, then the three plots - which is both shorter and a cleaner read. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  align-items: stretch;
}
.fw-sum > * { min-width: 0; }
/* The centre cell is a window onto the film behind, not a tile: the film is painted by the
   stage underneath and this is the hole it shows through. */
.fw-sum-hole {
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px;
  min-height: 0;
}
.fw-sum-mark { width: auto; max-width: 100%; max-height: 190px; object-fit: contain; display: block; }
.fw-sum-hole span {
  font-size: 14px; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--ink-dim);
}

/* Six worlds: the globe and the name. The paper explains each at length and this tile is
   not the place to do it again. */
.fw-tile-worlds { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.0px 8px; }
.fw-tw { display: flex; align-items: center; gap: 8px; min-width: 0; }
.fw-tw-img { width: 26px; height: 26px; object-fit: contain; flex: none; }
.fw-tw span { font-size: 14px; font-weight: 600; color: var(--ink); }

/* Spread per conversation type. */
.fw-spread { display: flex; flex-direction: column; gap: 3.0px; }
.fw-sp-row { display: grid; grid-template-columns: 13ch 1fr; align-items: center; gap: 8px; }
.fw-sp-n { font-size: 14px; color: var(--ink-dim); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.fw-sp-t { position: relative; height: 12px; }
.fw-sp-t::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: rgba(14, 16, 41, .09);
}
.fw-sp-rng {
  position: absolute; top: 50%; height: 3px; transform: translateY(-50%);
  border-radius: 2px; background: rgba(14, 16, 41, .16);
}
.fw-sp-d {
  --c0: var(--magd); --c1: var(--vio); --c2: #0B7A56; --c3: #b87a2a; --c4: #6b7280;
  position: absolute; top: 50%; width: 6px; height: 6px; border-radius: 50%;
  transform: translate(-50%, -50%); background: var(--c0);
}
.fw-sp-d[style*="--i: 1"], .fw-sp-d[style*="--i:1"] { background: var(--c1); }
.fw-sp-d[style*="--i: 2"], .fw-sp-d[style*="--i:2"] { background: var(--c2); }
.fw-sp-d[style*="--i: 3"], .fw-sp-d[style*="--i:3"] { background: var(--c3); }
.fw-sp-d[style*="--i: 4"], .fw-sp-d[style*="--i:4"] { background: var(--c4); }
.fw-sum-abstract { grid-column: 1 / -1; }

.fw-sum-abstract {
  font-size: clamp(14px, 0.74vw, 14px);
  line-height: 1.48;
  color: var(--ink-soft);
  text-wrap: pretty;
  /* Two columns. At this size one column across the right half runs to about 130 characters
     a line, roughly twice a readable measure, and the abstract is the longest single block
     on the page. */
  column-count: 2;
  column-gap: 24px;
  column-rule: 1px solid var(--line);
  padding: 12px 16px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--line);
}

/* The three runs of the abstract that a plot is evidence for. Translucent, because the
   point is to tie two things together, not to shout: a solid highlight on body text at
   this size reads as an error state. */
.fw-lnk {
  border-radius: 4px;
  /* The padding is pulled back out by the margin. Left in, it opened a visible gap on both
     sides of each linked run and the sentence read as "( Pass@1: 0.490 ,  turn-taking...". */
  padding: 1.0px 3.0px;
  margin: 0 -3px;
  cursor: default;
  transition: background-color .16s, color .16s;
}
.fw-lnk.is-lit {
  background: color-mix(in srgb, var(--lnk, var(--magenta)) 20%, transparent);
  color: var(--ink);
}
.fw-lnk:focus-visible { outline: 2px solid var(--lnk, var(--magenta)); outline-offset: 1px; }
.fw-lnk[data-link="pass"] { --lnk: var(--magd); }
.fw-lnk[data-link="tt"]   { --lnk: var(--vio); }
.fw-lnk[data-link="mos"]  { --lnk: var(--teal); }

.fw-sum-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.fw-tile-plot {
  margin: 0;
  min-width: 0;
  padding: 12px 12px 8px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--line);
  transition: background-color .16s, border-color .16s;
}
.fw-tile-plot.is-lit {
  border-color: color-mix(in srgb, var(--tint) 45%, transparent);
  background: color-mix(in srgb, var(--tint) 7%, rgba(255, 255, 255, .75));
}
.fw-tile-plot:focus-visible { outline: 2px solid var(--tint); outline-offset: 2px; }
.fw-tile-h {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.085em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 8px;
}
.fw-tile-plot.is-lit .fw-tile-h { color: var(--tint); }
.fw-tile-plot.is-note { padding-bottom: 12px; }
.fw-tile-big { font-size: 14px; line-height: 1.35; color: var(--ink); margin-bottom: 8px; }
.fw-tile-i { display: flex; gap: 8px; font-size: 14px; line-height: 1.35; margin-top: 1.0px; }
.fw-tile-i b { color: var(--ink); font-weight: 650; flex: none; }
.fw-tile-i span { color: var(--ink-dim); min-width: 0; }
.fw-tile-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.fw-chip {
  font-size: 14px; line-height: 1; padding: 4px 8px; border-radius: 100px;
  background: rgba(14, 16, 41, .055); color: var(--ink-soft);
}

/* Five columns, ordered by their own value. Each plot has a different leader, and that is
   the finding - sorting them is what makes it visible without a caption saying so. */
.fw-cols {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  height: clamp(56px, 7.6vh, 80px);
}
.fw-col-b {
  display: grid;
  /* value, then the track, then a fixed strip for the rotated name. The track has to be a
     grid ROW with a definite size for the bar's percentage height to resolve against
     anything: as a plain flex child it had no definite height and every bar came out 0. */
  grid-template-rows: auto 1fr;
  height: 100%;
  min-width: 0;
}
.fw-col-v {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  text-align: center;
  margin-bottom: 3.0px;
}
.fw-col-bar {
  align-self: end;
  width: 100%;
  height: calc(var(--h) * var(--grow, 1));
  min-height: 2px;
  border-radius: 4px 4px 2px 2px;
  background: color-mix(in srgb, var(--tint) 40%, #C9CBD9);
  transition: background-color .16s;
}
/* The plot is drawn in one weight until it is the one being talked about. Colouring all
   three at once would make the page a fruit salad and would say nothing. */
.fw-tile-plot.is-lit .fw-col-bar { background: var(--tint); }

/* The names hang below the plot on a 45 degree rake, anchored to each column's RIGHT edge
   so each one runs down-left into the space under its neighbour instead of across it.
   Flat they cannot be set: five names across a third of a column truncates
   "GPT-Realtime-2.1" and "GPT-Realtime-2.1-mini" to the same label. */
.fw-col-n {
  position: absolute;
  top: 4px;
  right: 2px;
  display: flex;
  align-items: center;
  gap: 3.0px;
  font-size: 14px;
  line-height: 1;
  color: var(--ink-dim);
  white-space: nowrap;
  transform: rotate(-45deg);
  transform-origin: right top;
}
.fw-tile-plot.is-lit .fw-col-n { color: var(--ink-soft); }
.fw-col-mk { display: flex; }
.fw-col-mk svg { width: 9px; height: 9px; flex: none; display: block; }
/* The rake needs its own strip; the tile reserves it rather than letting the labels
   overflow onto whatever is beneath. */
.fw-names {
  position: relative;
  height: clamp(36px, 4.8vh, 48px);
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  margin-top: 4px;
}
.fw-name-cell { position: relative; min-width: 0; }

/* The worked example: five runs, and the three numbers they produce. */
.fw-work { margin: 16px 0 0; }
.fw-work-h {
  font-size: 14px; font-weight: 700; letter-spacing: 0.085em; text-transform: uppercase;
  color: var(--ink-dim); margin-bottom: 10px;
}
.fw-work-rows { display: flex; flex-direction: column; gap: 7px; }
/* Four columns now: the run, the walk, the verdict in a word, and the figures. */
.fw-work-row {
  display: grid; grid-template-columns: 5.5ch 1fr 9ch 12ch;
  align-items: center; gap: 10px;
}
.fw-work-n { font-size: 14px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.fw-work-t {
  position: relative; height: 22px; border-radius: 7px;
  background: rgba(14, 16, 41, .045);
  box-shadow: inset 0 0 0 1px rgba(14, 16, 41, .06);
}
.fw-work-b {
  position: absolute; inset: 0 auto 0 0; border-radius: 7px;
  background: rgba(14, 16, 41, .22);
}
/* A pass walks nearly the optimal route, a fail wanders. The ramp runs from the colour of
   the route to the colour of the detour, so the bar carries the verdict along its whole
   length rather than only where it stops. */
.fw-work-row.is-pass .fw-work-b {
  background: linear-gradient(90deg, #2FA37A 0%, #48BE7C 100%);
}
.fw-work-row.is-fail .fw-work-b {
  background: linear-gradient(90deg, #F2A104 0%, #DE4B2C 100%);
}
/* A run that never arrives has no length to draw. An empty track read as "zero blocks",
   which is the opposite of what happened, so it is drawn as an absence instead. */
.fw-work-row.is-none .fw-work-t {
  background: repeating-linear-gradient(135deg,
    rgba(14, 16, 41, .05) 0 6px, rgba(14, 16, 41, .10) 6px 12px);
  box-shadow: inset 0 0 0 1px rgba(14, 16, 41, .12);
}
.fw-work-c {
  font-size: var(--t-micro); font-weight: 680; text-align: center;
  padding: 3px 0 4px; border-radius: 999px; letter-spacing: .02em;
}
.fw-work-row.is-pass .fw-work-c { background: rgba(47, 163, 122, .16); color: #1C6B4F; }
.fw-work-row.is-fail .fw-work-c { background: rgba(222, 75, 44, .14); color: #A33318; }
.fw-work-row.is-none .fw-work-c { background: rgba(14, 16, 41, .07); color: var(--ink-dim); }
/* The optimum and the efficiency limit, drawn on every run so the two thresholds are the
   same two lines every time rather than a sentence under the figure. */
.fw-work-opt, .fw-work-lim { position: absolute; top: -3px; bottom: -3px; width: 0; }
.fw-work-opt { border-left: 2px solid var(--ink); }
.fw-work-lim { border-left: 2px dashed rgba(14, 16, 41, .5); }
.fw-work-key { margin-top: 8px; font-size: 14px; color: var(--ink-dim); }
.fw-work-out {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px; margin-top: 12px;
}
.fw-work-cell {
  padding: 9px 12px 10px; border-radius: 12px;
  background: color-mix(in srgb, var(--pillar-agentic) 7%, #fff);
  border: 1px solid color-mix(in srgb, var(--pillar-agentic) 26%, var(--line));
}
/* All three of these are agentic-capability metrics computed from the SAME five runs, so
   they take that pillar's one colour. Tinting them green, blue and amber made them look
   like one metric from each of the three pillars, which is a claim about the figure that
   is simply untrue. They separate by depth instead, left to right, because that is the
   direction the requirement tightens: arrived, then passed once, then passed every time. */
.fw-work-cell:nth-child(2) { background: color-mix(in srgb, var(--pillar-agentic) 12%, #fff); }
.fw-work-cell:nth-child(3) { background: color-mix(in srgb, var(--pillar-agentic) 18%, #fff); }
.fw-work-cell b {
  display: block; font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-dim); font-weight: 700;
}
.fw-work-cell strong {
  display: block; font-size: 20px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.2;
}
.fw-work-cell span { font-size: 14px; color: var(--ink-dim); }

/* ===========================================================================
   Recorded runs
   ---------------------------------------------------------------------------
   Five tiles: two Pathfinding walks that play, and three enterprise calls that
   loop the turns in which the thing the conversation type is named for actually
   happens. Silent, as the rest of the page is.
   ========================================================================= */

.fw-runs {
  display: grid;
  /* Three then two, not five across. Five tiles in a half-width column is 135px each: the
     maps come out as a cropped strip and a turn of dialogue becomes a one-word-per-line
     ribbon. On six tracks, the first three span two each and the last two span three. */
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: 8px;
  margin-top: 16px;
  height: clamp(300px, 54vh, 540px);
}
.fw-run:nth-child(-n+3) { grid-column: span 2; }
.fw-run:nth-child(n+4)  { grid-column: span 3; }
.fw-run {
  margin: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255, 255, 255, .58);
}
.fw-run.is-ok  { border-top: 3px solid var(--teal); }
.fw-run.is-bad { border-top: 3px solid var(--red); }
.fw-run[data-kind="map"] { cursor: pointer; transition: border-color .14s, transform .14s; }
.fw-run[data-kind="map"]:hover { border-color: rgba(11, 122, 86, .45); transform: translateY(-1px); }
.fw-run:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; }
.fw-run-head {
  flex: 0 0 auto;
  padding: 8px 8px 8px;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 1.0px;
}
.fw-run-type { font-size: 14px; font-weight: 650; color: var(--ink); }
.fw-run-where {
  font-size: 14px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-dim);
}
.fw-run-box { flex: 1 1 auto; min-height: 0; display: flex; position: relative; }
.fw-run-box .wm-root { flex: 1 1 auto; min-width: 0; min-height: 0; }

/* The turns. Two speakers, no avatars and no timestamps: the shape of the exchange is the
   content and everything else is furniture. */
.fw-turns {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  justify-content: flex-end;
}
.fw-turn {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 14px;
  line-height: 1.38;
  padding: 4px 8px;
  border-radius: 9px;
  max-width: 94%;
  animation: fw-turn-in .28s ease-out both;
  /* Some turns run three hundred words. Six lines is the most a tile this size can hold
     without pushing its neighbours out of the loop, and the point of the excerpt is the
     move, not the whole speech. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fw-turn b {
  display: block;
  font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase;
  font-weight: 700; opacity: 1; margin-bottom: 2.0px;
}
.fw-turn.is-user  { background: rgba(14, 16, 41, .07); color: var(--ink-soft);
  border-bottom-left-radius: 3px; align-self: flex-start; }
.fw-turn.is-agent { background: color-mix(in srgb, var(--magenta) 13%, #fff); color: var(--ink);
  border-bottom-right-radius: 3px; align-self: flex-end; }
@keyframes fw-turn-in { from { opacity: 0; transform: translateY(6px); } }

/* ===========================================================================
   Grouped bars, and cards
   ========================================================================= */

.fw-grp { margin: 16px 0 0; }
.fw-grp-label {
  font-size: 14px; font-weight: 700; letter-spacing: 0.085em; text-transform: uppercase;
  color: var(--ink-dim); margin-bottom: 8px;
}
.fw-grp-plot {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(6px, 1vw, 16px);
  align-items: end;
}
.fw-grp-cluster { min-width: 0; }
.fw-grp-bars {
  display: flex;
  align-items: flex-end;
  gap: 2.0px;
  height: 96px;
  padding-bottom: 1.0px;
  border-bottom: 1px solid var(--line);
}
/* One hue per system, held across every cluster and every plot on the page. The whole
   point of a grouped chart is that you can follow one system across the groups. */
.fw-grp-bar, .fw-grp-sw {
  --c0: var(--magd); --c1: var(--vio); --c2: #0B7A56; --c3: #b87a2a; --c4: #6b7280;
  background: var(--c0);
}
.fw-grp-bar { flex: 1 1 0; min-width: 0; height: calc(var(--h) * var(--grow, 1));
  min-height: 1px; border-radius: 2px 2px 0 0; }
.fw-grp-bar[style*="--i:1"], .fw-grp-sw[style*="--i:1"] { background: var(--c1); }
.fw-grp-bar[style*="--i:2"], .fw-grp-sw[style*="--i:2"] { background: var(--c2); }
.fw-grp-bar[style*="--i:3"], .fw-grp-sw[style*="--i:3"] { background: var(--c3); }
.fw-grp-bar[style*="--i:4"], .fw-grp-sw[style*="--i:4"] { background: var(--c4); }
.fw-grp-name {
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.15;
  color: var(--ink-dim);
  text-align: center;
  overflow-wrap: anywhere;
}
.fw-grp-key {
  display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 12px;
  font-size: 14px; color: var(--ink-dim);
}
.fw-grp-keyit { display: inline-flex; align-items: center; gap: 4px; }
.fw-grp-sw { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }

.fw-cards { display: grid; gap: 8px; margin-top: 16px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fw-cards.is-c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fw-card {
  min-width: 0;
  padding: 12px 12px 12px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--line);
  border-top: 3px solid var(--tint, var(--line));
}
.fw-card-h {
  margin: 0 0 4px;
  font-size: 14px; font-weight: 700; letter-spacing: 0.055em; text-transform: uppercase;
  color: var(--ink);
}
.fw-card-l { margin: 0 0 8px; font-size: 14px; line-height: 1.45; color: var(--ink-dim); }
.fw-card-i { display: flex; gap: 8px; font-size: 14px; line-height: 1.45; margin-top: 3.0px; }
.fw-card-i b { color: var(--ink); font-weight: 650; flex: none; min-width: 5.5ch; }
.fw-card-i span { color: var(--ink-soft); }
/* Two speakers, set the way a phone sets them. No avatars, no timestamps: the shape of the
   exchange is the content, and anything else is furniture. */
.fw-chat {
  font-size: 14px; line-height: 1.4; padding: 4px 8px; border-radius: 10px;
  margin-top: 4px; max-width: 92%;
}
.fw-chat.is-user { background: rgba(14, 16, 41, .07); color: var(--ink-soft);
  border-bottom-left-radius: 3px; }
.fw-chat.is-agent { background: color-mix(in srgb, var(--tint, var(--magenta)) 15%, #fff);
  color: var(--ink); margin-left: auto; border-bottom-right-radius: 3px; }

/* ===========================================================================
   Bar chart
   ---------------------------------------------------------------------------
   Five rows of two divs. Every width is a number from Table 3 rather than a
   scale somebody chose, and the interval is DRAWN as well as printed: 0.533
   plus or minus 0.133 and 0.490 plus or minus 0.039 are different claims, and
   a plain bar chart shows them as the same kind of thing.
   ========================================================================= */

.fw-bars { margin: 16px 0 0; }
.fw-bars-label {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.075em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 8px;
}
.fw-bars-list { display: flex; flex-direction: column; gap: 8px; }

.fw-bar-row {
  position: relative;
  display: grid;
  /* 19ch, not 15: "Gemini-3.1-Flash-Live" and "GPT-Realtime-2.1-mini" both truncated at 15,
     and a chart whose longest label is an ellipsis is a chart you have to hover to read. */
  grid-template-columns: minmax(0, 19ch) 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 3.0px 4px;
  border-radius: 8px;
  cursor: default;
}
.fw-bar-row:hover, .fw-bar-row:focus-visible { background: rgba(255, 255, 255, .55); }
.fw-bar-row:focus-visible { outline: 2px solid var(--magenta); outline-offset: 1px; }

.fw-bar-name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.fw-bar-name > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.fw-bar-track {
  position: relative;
  height: 20px;
  border-radius: 5px;
  background: rgba(14, 16, 41, .07);
  overflow: visible;
}
.fw-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--w);
  border-radius: 5px;
  background: linear-gradient(90deg, var(--sys, var(--accent)),
              color-mix(in oklab, var(--sys, var(--accent)) 74%, #000 6%));
}
/* A real zero. Nova 2 Sonic scores 0.000 in Pathfinding, and an empty track beside the
   number reads as a chart that failed to draw rather than as a result. A 3px stub at the
   origin says the row was measured and the answer was none. */
.fw-bar-row.is-zero .fw-bar-fill { width: 3px; border-radius: 2px; background: var(--ink-dim); }
/* The bars grow WITH THE SCROLL, from --grow, which is the panel's own visibility written
   every frame by paintCopy. Two earlier versions were wrong in the same way: pinned to zero
   until the panel got `is-live` (written only inside d < 0.5) and then released on a 0.7s
   wall-clock transition. That left a fully legible chart on screen with five zero-length
   bars and their whiskers floating over nothing for up to 0.62 viewport heights, and then
   animated it on a clock the reader does not control - at reading speed the bars were still
   filling 1,050px after they started, with the panel already fading out. Driven by --grow
   they are simply part of the same motion as everything else. */
.fw-bar-fill { width: calc(var(--w) * var(--grow, 1)); }

/* The 95% bootstrap interval, drawn over the bar. A capped whisker rather than a shaded
   band: the band reads as more bar, which is the opposite of what it means. */
/* The interval crosses its own bar, so it has to be legible on BOTH grounds. Drawn in ink
   it measured 1.85:1 against the magenta fill and 1.61:1 for the connector, so the lower
   half of every whisker vanished into the bar and the upper half did not - which reads as
   an interval that starts at the bar's end, the exact opposite of what it means. White with
   a dark drop shadow holds on the fill and on the track alike. */
.fw-bar-ci {
  position: absolute;
  top: 50%;
  height: 10px;
  transform: translateY(-50%);
  border-left: 2px solid rgba(255, 255, 255, .96);
  border-right: 2px solid rgba(255, 255, 255, .96);
  filter: drop-shadow(0 0 1px rgba(14, 16, 41, .75));
  pointer-events: none;
}
.fw-bar-ci::before {
  content: "";
  position: absolute;
  top: 50%; left: 0; right: 0;
  height: 2px;
  transform: translateY(-50%);
  background: rgba(255, 255, 255, .9);
}

.fw-bar-val {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-variant-numeric: tabular-nums;
  min-width: 8.5ch;
  justify-content: flex-end;
}
.fw-bar-val strong { font-size: 14px; font-weight: 700; color: var(--ink); }
/* 0.86em, not a fixed 11px. Fixed, it did not follow the table's own size steps, so at the
   widths where the value dropped to 10.5px the UNCERTAINTY was set larger than the estimate
   it qualifies. */
.fw-pm { font-size: 0.86em; color: var(--ink-dim); font-style: normal; font-variant-numeric: tabular-nums; }

.fw-bar-tip {
  position: absolute;
  bottom: calc(100% + 5px);
  left: 16ch;
  z-index: 9;
  padding: 8px 8px;
  border-radius: 8px;
  background: var(--ink);
  color: #fff;
  font-size: 14px;
  line-height: 1.4;
  max-width: 34ch;
  opacity: 0;
  pointer-events: none;
  transition: opacity .14s;
}
.fw-bar-row:hover .fw-bar-tip, .fw-bar-row:focus-visible .fw-bar-tip { opacity: 1; }

/* The axis is laid out on the SAME grid as the rows rather than being pushed across with a
   margin. The margin was `calc(19ch + 12px)` at 10.5px while the name column is 19ch at
   12.5px: `ch` resolves against whatever font the element is in, so 19ch meant 103px here
   and 174px there, and the axis sat 64px left of the bar origin it was meant to label. */
.fw-bars-axis {
  display: grid;
  grid-template-columns: minmax(0, 19ch) 1fr auto;
  gap: 12px;
  margin: 4px 0 0;
  padding: 3.0px 4px 0;
  font-size: 14px;                 /* so the 19ch above matches the row's 19ch */
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
.fw-bars-axis .fw-axis-ticks {
  grid-column: 2;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
}
/* The end labels centre on their tick rather than butting the ends of the track. */
.fw-bars-axis .fw-axis-ticks > span:first-child { transform: translateX(-50%); }
.fw-bars-axis .fw-axis-ticks > span:last-child { transform: translateX(50%); }
.fw-bars-note {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-dim);
  max-width: 74ch;
}

/* ===========================================================================
   The pillar table
   ---------------------------------------------------------------------------
   Table 3 as the paper prints it: three pillars over eleven metrics, every cell
   with its own 95% half-width. The half-widths are why this is worth the width
   it costs - without them the Pathfinding block reads as five separated systems
   when three of its intervals overlap.
   ========================================================================= */

.fw-pillars-wrap {
  margin-top: 16px;
  /* Its own scroller. Eleven columns will not fit a phone at any type size worth reading,
     and a table that widens the BODY makes the whole page scroll sideways.

     `max-width` and the `min-width: 0` below are what make that true. Without them the
     panel is a flex item sized to its content, so the table pushed the PANEL wider than
     the column and `.fw-copy`'s overflow-x: hidden quietly cut it off: measured at
     1560x900 the suite table ended at FAI and three columns of Naturalness were gone,
     with no scrollbar and nothing to say they existed. */
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  /* Its OWN container, so the size queries below measure the width the table actually has
     rather than the width of the copy column. They are not the same number: at a world stop
     the table shares its column with a bar chart and gets about 60% of it, so keyed to the
     column the type never stepped down and three pillars ran off the edge. */
  container-type: inline-size;
  container-name: pillars;
  /* When it does have to scroll, say so. A table that is silently clipped reads as a table
     with eight columns. */
  /* `transparent`, not a hard-coded rgba of the base ground: --bg is sampled from the film
     and runs from rgb(229,230,229) to rgb(251,250,249) across the flight, so fading to a
     fixed 232,233,231 painted a grey smudge down both edges of the table at the light
     stops. */
  background:
    linear-gradient(to right, var(--bg) 30%, transparent) left / 26px 100% no-repeat,
    linear-gradient(to left, var(--bg) 30%, transparent) right / 26px 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, rgba(14, 16, 41, .13), transparent) left / 12px 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, rgba(14, 16, 41, .13), transparent) right / 12px 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}
.fw-pillars {
  border-collapse: collapse;
  width: 100%;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
}
/* The panel must be allowed to be narrower than its widest child, or it is not a column,
   it is whatever the table wants. */
.fw-panel { min-width: 0; max-width: 100%; }
/* The name sits BESIDE its vendor mark, not under it. `.fw-m-sysin` was only given a flex
   row by the old metrics rule, so in the pillar table every system name wrapped to a second
   line under its glyph and each row was twice the height it needed. */
.fw-m-sysin { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.fw-pillars caption {
  caption-side: bottom;
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-dim);
  text-align: left;
  /* 62ch, not 92. It was the smallest type on the page carrying the longest line - measured
     at 89 to 112 characters - and it is the text that defines all eleven metrics. */
  max-width: 62ch;
  font-size: 14px;
}
.fw-p-group {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.085em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 0 8px 4px;
  text-align: center;
  /* The rule under the group name is what makes the span visible; without it three pillars
     read as one undifferentiated row of eleven. */
  border-bottom: 1px solid var(--line);
}
.fw-p-group + .fw-p-group { border-left: 1px solid var(--line); }
.fw-p-corner { border: 0; }
.fw-p-metrics th {
  font-size: 14px;
  font-weight: 650;
  color: var(--ink-soft);
  padding: 4px 8px;
  text-align: right;
  white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.fw-p-metrics th.fw-p-sys { text-align: left; }
.fw-pillars th.fw-p-sys {
  text-align: left;
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
  padding: 4px 12px 4px 0;
  white-space: nowrap;
}
.fw-pillars td {
  padding: 4px 8px;
  text-align: right;
  color: var(--ink-soft);
  white-space: nowrap;
}
/* Banded, not ruled. The rule was rgba(14, 16, 41, .055) - 1.12:1, which is to say invisible -
   so eleven columns at a 90px pitch had no horizontal guide at all and the table read as a
   grey field. Banding is also what this audience reads all day. */
.fw-pillars tbody tr:nth-child(odd) { background: rgba(14, 16, 41, .032); }
.fw-pillars td b { font-weight: 500; color: var(--ink-soft); }
/* Weight AND tint. A 250-unit weight step is legible at 11.5px and nearly invisible at the
   9px the narrowest columns fall back to; the tint survives every size. */
.fw-pillars td.is-best { background: rgba(11, 122, 86, .085); }
.fw-pillars td.is-best b { font-weight: 750; color: var(--ink); }
.fw-pillars tbody tr:last-child td { border-bottom: 0; }
/* No opacity. At .72 these measured 3.15:1 against the table ground - under AA - and they
   are the reason the table is worth the width it costs: without them the Pathfinding block
   reads as five separated systems when three of its intervals overlap. Full-strength
   --ink-dim is 5.44:1, and the size step alone is enough to keep them subordinate. */
.fw-pillars td .fw-pm { margin-left: 3.0px; }

/* Eleven columns of numbers plus a system name is about 980px at the base size. The column
   they land in is not one width - it is 34vw of the window at a world stop and rather less
   elsewhere - so the type steps down to meet it. Keyed to the CONTAINER, not the viewport,
   because it is the column's width that decides, and the same viewport gives the column two
   different widths at two different stops. */
@container pillars (max-width: 980px) {
  .fw-pillars { font-size: 14px; }
  .fw-pillars td, .fw-p-metrics th { padding: 4px 4px; }
  .fw-pillars td .fw-pm { margin-left: 2.0px; }
}
@container pillars (max-width: 860px) {
  .fw-pillars { font-size: 14px; }
  .fw-pillars th.fw-p-sys { font-size: 14px; padding-right: 8px; }
  .fw-pillars td, .fw-p-metrics th { padding: 3.0px 3.0px; }
  .fw-pillars td .fw-pm { font-size: 14px; }
  .fw-p-metrics th { font-size: 14px; }
}
@container pillars (max-width: 740px) {
  .fw-pillars { font-size: 14px; }
  .fw-pillars th.fw-p-sys { font-size: 14px; padding-right: 4px; }
  .fw-pillars td, .fw-p-metrics th { padding: 3.0px 2.0px; }
  /* Past here the half-widths are what has to go: they are the widest thing in every cell
     and the least useful at a glance. The caption still says what they were, and the suite
     stop shows them at full size. */
  .fw-pillars td .fw-pm { display: none; }
}
@container pillars (max-width: 560px) {
  /* Past here it cannot be made to fit and stay readable, so it scrolls instead - which is
     honest, because the affordance on .fw-pillars-wrap shows there is more. */
  .fw-pillars { font-size: 14px; width: auto; min-width: 540px; }
  .fw-pillars td .fw-pm { display: inline; }
}

/* The reveal: the pillar names, then the metric names, then the systems one block at a
   time. What it is measuring, before what scored what. */
.fw-pillars.is-staged .fw-p-metrics th:not(.fw-p-sys),
.fw-pillars.is-staged tbody tr { opacity: 0; transition: opacity .45s; }
[data-step="1"] .fw-pillars.is-staged .fw-p-metrics th,
[data-step="2"] .fw-pillars.is-staged .fw-p-metrics th,
[data-step="3"] .fw-pillars.is-staged .fw-p-metrics th { opacity: 1; }
[data-step="2"] .fw-pillars.is-staged tbody tr,
[data-step="3"] .fw-pillars.is-staged tbody tr { opacity: 1; }

/* ===========================================================================
   The walks
   ---------------------------------------------------------------------------
   Live renderers, not pictures. The grid tracks collapse to 0fr rather than the
   tiles being removed, which is what lets the browser interpolate between one
   walk and six instead of cutting - the same technique the demo page's
   presentation mode uses, and the reason a tile keeps walking while the layout
   around it changes size.
   ========================================================================= */

.fw-maps {
  display: grid;
  gap: 8px;
  margin-top: 8px;
  /* --map-h is the ROW height, and it must be an explicit length rather than 1fr.
     The renderer measures its host and lays itself out to fit; inside an auto-height
     column a 1fr row has no height to resolve against, so the host grows, the renderer
     re-measures, and the two feed each other. Measured before this was pinned: a panel
     2,720,150px tall. An explicit height breaks the loop in one direction, which is all
     it takes. */
  /* TWO heights, not one. The reveal gives up space on the horizontal axis first and on
     the vertical axis only at the last step, so a single walk on screen alone uses the
     whole frame the six will eventually share rather than sitting in a strip a sixth of
     its height with five empty cells beside it. --map-tall is one row filling the band;
     --map-h is what a row becomes once there are two of them. */
  --map-tall: clamp(230px, 44vh, 470px);
  --map-h: calc((var(--map-tall) - 9px) / 2);
  grid-template-columns: 1fr 0fr 0fr;
  grid-template-rows: var(--map-tall) 0fr;
  transition: grid-template-columns .6s cubic-bezier(.3,.7,.3,1),
              grid-template-rows .6s cubic-bezier(.3,.7,.3,1);
}
[data-step="1"] .fw-maps { grid-template-columns: 1fr 1fr 0fr; }
[data-step="2"] .fw-maps { grid-template-columns: 1fr 1fr 1fr; }
[data-step="3"] .fw-maps {
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: var(--map-h) var(--map-h);
}

.fw-map {
  margin: 0;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--glass);
  cursor: pointer;
  opacity: 0;
  transition: opacity .5s, border-color .14s, transform .14s;
}
/* The row's meaning is carried by the tile, not only by a 9.5px pill in its corner: solved
   and unsolved were otherwise two visually identical containers. */
.fw-map.is-ok  { border-top: 3px solid var(--teal); }
.fw-map.is-bad { border-top: 3px solid var(--red); }
.fw-map:hover { border-color: rgba(11, 122, 86, .45); transform: translateY(-1px); }
.fw-map:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; }
/* Which tiles are up at each step. The top row builds left to right, then the whole failure
   row arrives at once - the point of the last step is the comparison, not a fourth walk.
   `.is-ok` is load-bearing in every one of these: each job has two cells with the same
   data-at, so without it step 0 showed the job done well AND done badly, which is the
   comparison the last step exists to make.
   Steps 0 to 2 are also unclickable: a 0fr column is zero pixels wide, but a tile inside one
   is still in the hit-testing tree and a click near the grid's left edge opened it. */
[data-step="0"] .fw-map.is-ok[data-at="0"],
[data-step="1"] .fw-map.is-ok[data-at="0"], [data-step="1"] .fw-map.is-ok[data-at="1"],
[data-step="2"] .fw-map.is-ok[data-at="0"], [data-step="2"] .fw-map.is-ok[data-at="1"],
[data-step="2"] .fw-map.is-ok[data-at="2"],
[data-step="3"] .fw-map { opacity: 1; }
.fw-map { pointer-events: none; visibility: hidden; }
[data-step="0"] .fw-map.is-ok[data-at="0"],
[data-step="1"] .fw-map.is-ok[data-at="0"], [data-step="1"] .fw-map.is-ok[data-at="1"],
[data-step="2"] .fw-map.is-ok[data-at="0"], [data-step="2"] .fw-map.is-ok[data-at="1"],
[data-step="2"] .fw-map.is-ok[data-at="2"],
[data-step="3"] .fw-map { pointer-events: auto; visibility: visible; }

/* A maps grid marked `all` is not part of the staged reveal: every tile is up from the
   first frame, at the final two-row size, whatever `data-step` happens to say. The reveal
   builds the three PASSES one at a time on the stop before this one; replaying that build
   before the three failures arrive would be telling the reader something they just read. */
.fw-maps.is-all .fw-map { opacity: 1; pointer-events: auto; visibility: visible; }
.fw-maps.is-all {
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  grid-template-rows: repeat(2, var(--map-h));
}

.fw-map-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  flex: 0 0 auto;
  /* Fixed, so the map below is the residual and all six maps share edges. Free, the
     "All-day assistance" head wrapped to two lines below 1366 and those two tiles started
     15px lower than their neighbours - in a grid whose whole purpose is pairwise
     comparison. */
  min-height: 30px;
}
.fw-map-cond { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fw-map-cond { font-weight: 650; color: var(--ink); }
.fw-map-verdict {
  margin-left: auto;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  padding: 2.0px 8px;
  border-radius: 100px;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.fw-map.is-ok  .fw-map-verdict { color: var(--teald); }
.fw-map.is-bad .fw-map-verdict { color: var(--red); }

/* The renderer fills whatever is left. It measures its host, so the host must have a real
   height: min-height 0 in a grid item plus flex:1 is what gives it one. */
.fw-map-box { flex: 1 1 auto; min-height: 0; display: flex; position: relative; }
.fw-map-box .wm-root { flex: 1 1 auto; min-width: 0; min-height: 0; }
.fw-map-wait {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.fw-map-foot {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 0 0 auto;
  padding: 4px 8px 8px;
  border-top: 1px solid var(--line);
  font-size: 14px;
  line-height: 1.38;
  color: var(--ink-dim);
  min-height: calc(2 * 1.38em + 11px);      /* the pair to .fw-map-head's floor */
  /* Hard-capped at two lines. The row height is fixed, so every pixel the note takes comes
     straight out of the map: unclamped, a three-line note left a 144px tile with 37px of
     actual walk in it, which is a caption with a sliver attached rather than a map. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The full walk, over the page. Same renderer without `compact`, which is what brings back
   the step scrubber: stepping the route one block at a time is the renderer's own transport,
   not a second implementation that could disagree with the tile. */
.fw-walk {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vh, 40px);
  background: rgba(18, 11, 25, .55);
  backdrop-filter: blur(3px);
}
.fw-walk-card {
  display: flex;
  flex-direction: column;
  width: min(1180px, 100%);
  height: min(820px, 100%);
  border-radius: 14px;
  overflow: hidden;
  background: #12171f;      /* the map's own ground, so the card is not a light frame on dark */
  box-shadow: 0 24px 70px rgba(10, 4, 16, .5);
}
.fw-walk-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid #212a36;
  color: #e6edf5;
  flex: 0 0 auto;
}
.fw-walk-title { font-size: 14px; font-weight: 650; }
.fw-walk-sub { font-size: 14px; color: #8b97a8; min-width: 0; flex: 1 1 auto; }
.fw-walk-x {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  border-radius: 8px;
  border: 1px solid #212a36;
  background: transparent;
  color: #e6edf5;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.fw-walk-x:hover { border-color: #4285F4; }
.fw-walk-x:focus-visible { outline: 2px solid #4285F4; outline-offset: 1px; }
.fw-walk-body { flex: 1 1 auto; min-height: 0; display: flex; padding: 8px; }
.fw-walk-body .wm-root { flex: 1 1 auto; min-width: 0; min-height: 0; }
/* This page never fetches a recording - the walks are silent by design and setVoice is
   never called - so the renderer's Voice toggle is a control with nothing behind it. A
   button that does nothing is worse than no button. */
.fw-walk-body [data-el="sVoice"] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .fw-video { transition: none; }
  .fw-hero { filter: none !important; }
  /* Everything else with a transition or a transform. The rail mark scales on every stop
     change and the buttons lift on every hover, and neither was covered here before. */
  .fw-dot, .fw-dot-mark, .fw-dot-label, .fw-hint,
  .fw-btn, .fw-linkbtn, .linkbtn, .fw-auto, .fw-case { transition: none; }
  .fw-dot.is-on .fw-dot-mark { transform: none; }
  .fw-btn:hover { transform: none; }
  /* The blocks added with the paper layout. A bar that grows and a grid that expands are
     both motion, and a reader who asked for none should get the finished state at once. */
  /* `:not(.fw-p-sys)` has to be repeated or the staged header rule out-specifies this one
     and keeps its 0.45s fade. */
  /* These are TRANSITIONS to switch off, not elements to remove. As written this rule
     put display:none on .fw-copy - every word on the page - plus all six walks and every
     results row, so a reader who asked for less motion was served a film and nothing
     else. A reduced-motion visitor gets the finished state, not an empty page. */
  .fw-bar-tip, .fw-maps, .fw-map, .fw-pillars.is-staged .fw-p-metrics th,
  .fw-pillars.is-staged .fw-p-metrics th:not(.fw-p-sys),
  .fw-pillars.is-staged tbody tr, .fw-copy, .fw-globes {
    transition: none !important;
    animation: none !important;
    opacity: 1 !important;
  }
  /* The one thing here that really is hidden: a rail the page has closed. */
  .fw-rail[hidden] { display: none !important; }
  /* The staged reveals are motion, so a reader who asked for none gets the FINISHED
     state: all six walks and every table row present at once, rather than a grid that
     never advances past step 0 and shows nothing at all. */
  .fw-map { opacity: 1 !important; visibility: visible !important; pointer-events: auto !important; }
  .fw-maps { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; 
  /* Columns were pinned and rows were not, so the reveal's step-0 value
     `var(--map-tall) 0fr` survived: three walks at full height and three at
     four pixels. The three that collapsed are the FAILURES, which is the half
     of the comparison the section exists to show. */
  grid-template-rows: auto auto !important;
}
  .fw-pillars.is-staged tbody tr, .fw-pillars.is-staged .fw-p-metrics th { opacity: 1 !important; }
  /* Transforms are motion too, and these two survived the sweep. */
  .fw-globe.is-on, .fw-shell-split .fw-video, .fw-shell-split .fw-art { transform: none !important; }
.fw-rail { transition: none; }
  /* The bars are no longer a transition - they are a width computed from the panel's own
     visibility - so there is nothing here to switch off. Pinning them to full width instead
     means a reader who asked for no motion still gets the finished chart. */
  .fw-bar-fill { width: var(--w); }
  .fw-bar-row.is-zero .fw-bar-fill { width: 3px; }
  .fw-map:hover, .fw-run[data-kind="map"]:hover { transform: none; }
  .fw-turn { animation: none; }
  .fw-walk { backdrop-filter: none; }
}


/* The byline, under the title on the opening. Public build only: the anonymous build never
   renders it, because nothing injects the global it reads. Set tight and quiet - it is
   front matter, not a heading, and the title above it is already doing the shouting. */
.fw-open-by {
  /* A column of three rows: names, then who they are and how to reach them, then what the
     marks mean. As one wrapping row these packed themselves wherever they fitted - the
     notes ended up a column away from the names they annotate, beside the affiliations
     rather than under them. */
  margin: 6px 0 0;
  display: flex; flex-direction: column;
  gap: 1px;
  line-height: 1.32;
}
.fw-open-authors { width: 100%; }
.fw-open-authors {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0 4px;
  font-size: clamp(14px, 1.05vw, 17px);
  color: var(--ink);
}
.fw-open-author { white-space: nowrap; }
.fw-open-author b { font-weight: 750; }
.fw-open-author sup, .fw-open-aff sup {
  /* An em here resolved to 10.5px, under the 12px floor. */
  font-size: 14px; font-weight: 700; color: var(--soft);
  vertical-align: super; line-height: 0; margin-left: 1.0px;
}
.fw-open-sep { color: var(--soft); margin-left: -4px; }
/* Row 2 and row 3. Each is one kind of thing, at one size, on one baseline - which is
   what stops the notes drifting up beside the affiliations. */
.fw-open-affrow,
.fw-open-notes {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0 16px; min-width: 0;
}
.fw-open-affrow { font-size: 14px; }
.fw-open-notes { font-size: 14px; color: var(--muted); }
.fw-open-aff {
  /* Not a flex line: a <sup> inside one becomes a flex item and loses its raise, so the
     marker dropped to the baseline and read as part of the affiliation name rather than
     as a reference to it. (Kept generic on purpose: this file is shipped byte-identical
     into the anonymous build, so an example spelled out here is a de-anonymising leak in
     the one file both variants share.) */
  display: inline; font-size: inherit; font-weight: 600; color: var(--text);
}
.fw-open-affrow > .fw-open-aff { white-space: nowrap; }
.fw-open-aff sup { margin-right: 3.0px; margin-left: 0; }
.fw-open-afflink { color: var(--vio); text-decoration: none; border-bottom: 1px solid var(--violn); }
.fw-open-afflink:hover { color: var(--viod); border-bottom-color: var(--viod); }
.fw-open-mail {
  /* A monospace face has a smaller x-height than the text beside it, so at the same
     nominal size it reads a notch small and sits low. A touch larger puts the two on one
     optical line. */
  font-size: 1.04em; color: var(--sub);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.fw-open-note { font-size: inherit; color: inherit; }

/* The byline standing in a flight stop rather than in the opening block.
   -----------------------------------------------------------------------------------
   In the opening it sits under a title in a dense three-by-three grid and has to stay
   tight. On the landing stop it is the ONLY thing under the title, on a half-width column
   with the film beside it, so the constraint is reversed: at the opening's sizes it read
   as a caption stranded in white space. Set a step up, with the three rows actually
   separated, and let the names breathe on their own line. */
.fw-body-by { margin-top: 22px; gap: 10px; }
.fw-body-by .fw-open-authors {
  font-size: clamp(17px, 1.45vw, 21px);
  line-height: 1.42;
  gap: 2px 8px;
}
.fw-body-by .fw-open-author sup,
.fw-body-by .fw-open-aff sup { font-size: 12.5px; }
.fw-body-by .fw-open-affrow { font-size: 15.5px; gap: 4px 20px; }
.fw-body-by .fw-open-notes  { font-size: 14.5px; gap: 2px 18px; margin-top: 2px; }

/* THE PAPER BUTTONS.
   -----------------------------------------------------------------------------------
   The shape a project page for a paper uses: a pill per destination, the label spelled
   out, an outline rather than a fill so they sit under the byline without competing with
   the title above them. */
.fw-open-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.fw-paperbtn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px;
  font-size: 15px; font-weight: 640; line-height: 1;
  color: var(--paper); text-decoration: none;
  background: var(--pillar-agentic);
  border: 1px solid var(--pillar-agentic);
  border-radius: 999px;
  transition: background .16s ease, box-shadow .16s ease, transform .16s ease;
}
.fw-paperbtn:hover {
  background: var(--magd); border-color: var(--magd);
  box-shadow: 0 3px 14px rgba(23, 23, 31, .16);
}
.fw-paperbtn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* The second and later destinations are the same journey, so they are the quieter
   variant: same pill, outline only. */
.fw-paperbtn.is-pdf {
  color: var(--pillar-agentic); background: transparent;
}
.fw-paperbtn.is-pdf:hover {
  color: var(--paper); background: var(--pillar-agentic);
}
.fw-paperbtn-i { width: 15px; height: 15px; flex: none; }

/* A figure carried by a flight STOP rather than by an opening cell.
   -----------------------------------------------------------------------------------
   The opening packs nine cells into a grid, so the ring and the globes are sized to fit a
   ninth of a screen. A stop gives the same figure a whole copy column with the film beside
   it, so at the cell sizes they read as thumbnails of themselves. Scaled up here rather
   than in the shared rules, which the opening still needs at its own size. */
.fw-stop-fig { margin-top: 20px; }
.fw-stop-fig .fw-ring-wrap { gap: 22px; margin-top: 0; }
.fw-stop-fig .fw-ring { max-height: 190px; }
.fw-stop-fig .fw-ring-khead { font-size: 14px; }
.fw-stop-fig .fw-ring-kname { font-size: 14.5px; }
.fw-stop-fig .fw-open-worlds {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px 18px;
}
.fw-stop-fig .fw-open-wimg { width: clamp(36px, 3.4vw, 56px); }
.fw-stop-fig .fw-open-w span { font-size: 15px; }
@media (max-width: 900px) {
  .fw-stop-fig .fw-open-worlds { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fw-stop-fig .fw-ring-wrap { flex-wrap: wrap; }
}

/* The three-by-three grid carried by a STOP rather than by the opening block.
   -----------------------------------------------------------------------------------
   In the opening it owns a whole viewport under a title, so it is sized to fill one. On a
   stop it sits under an eyebrow and a title inside a band panel that also has to travel,
   so it takes its height from its content instead of stretching to the screen. */
.fw-stop-cells { margin-top: 18px; }
.fw-stop-cells .fw-open-grid { height: auto; min-height: 0; }
/* THE GRID NEEDS THE BAND, NOT THE COPY MEASURE.
   -----------------------------------------------------------------------------------
   Measured at 1600px wide: as a page opening the grid is 1523px across and 527px tall; as
   a stop it came out 650px across and 1068px tall. The copy column is a reading measure -
   right for a paragraph, wrong for a three-by-three of charts - and a band stop still
   inherits it, so three 502px columns became three 211px ones and the grid paid for the
   lost width in height. That doubled height is also what put its tail past the fold.
   Lifted only for the panel that actually carries this grid, so every other band stop
   keeps the measure its prose wants. */
.fw-shell-split[data-block="band"] .fw-copy:has(.fw-stop-cells) { max-width: none; }

/* WIDE BAND STOPS TAKE THE BAND.
   -----------------------------------------------------------------------------------
   A band stop stands the stage down precisely because its content does not fit beside a
   film, and then the panel capped it at 1200px anyway. Measured across the site at 1600px
   wide, every one of these sat at 1200 with 352px of empty band beside it, and the gap
   grows with the screen: 672px unused at 1920.
   The cap is right for PROSE - a paragraph at 1500px is unreadable - so it stays the
   default and is lifted only for the blocks that are figures rather than text: the map
   tiles, the call tiles and their timelines, the eleven-column results tables, the card
   grids and the at-a-glance grid. The three Pathfinding maps were the worst of it, at
   1152px for three tiles side by side. */
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-stop-cells),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-maps),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-tapes),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-runs),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-cards),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-navcards),
/* The three figure stops the first pass of this rule missed, all of them on the results
   page and all of them measured at 1200px in a 1552px band: the small multiples (twelve
   panels on one shared scale, which is only worth printing if the scale is wide enough to
   separate them), the composition matrix, and the duration violins whose x axis runs to a
   twenty minute cap. Plus the metric suite on its own page. */
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-sm),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-mx),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-vio),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-work),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-metrics),
.fw-shell-split[data-block="band"] .fw-panel:has(.fw-pillars) {
  max-width: min(1560px, calc(100vw - 48px));
}

/* Shown only where the stage cannot carry it. See the phone block in alt-split.css. */
.fw-phone-only { display: none; }


/* Opening plots: horizontal, direct labelled, interval always drawn.
   ---------------------------------------------------------------------------------
   Replaces the vertical columns whose names were rotated 45 degrees and cut off at the
   front. Everything here is on the 8px grid. */
.fw-open-hrows {
  display: flex; flex-direction: column; justify-content: space-evenly;
  gap: clamp(2px, 0.6vh, 8px);
  margin-top: 4px;
  flex: 1 1 auto; min-height: 0;
  overflow: hidden;
}
.fw-open-hrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px;
  grid-template-areas: "name val" "track track";
  align-items: center;
  column-gap: 8px;
  row-gap: 2.0px;
}
.fw-open-hname {
  grid-area: name;
  display: flex; align-items: center; gap: 8px;
  line-height: 1.15;
  font-size: 14px; font-weight: 700; color: var(--ink);
  white-space: nowrap;              /* never truncated: the row grows instead */
}
.fw-open-hname .fw-vmark { width: 14px; height: 14px; }
.fw-open-hval {
  grid-area: val;
  line-height: 1.15;
  text-align: right;
  font-size: 14px; font-weight: 800; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.fw-open-hpm { font-weight: 600; color: var(--muted); margin-left: 3.0px; font-size: 14px; }
.fw-open-htrack {
  grid-area: track;
  position: relative;
  height: 8px;
  border-radius: 3px;
  background: var(--grid);
}
.fw-open-hbar {
  position: absolute; inset: 0 auto 0 0;
  min-width: 3px;                   /* a 0.011 must still be a bar, not a hairline */
  border-radius: 3px;
  background: var(--sys, var(--ink));
}
/* The 95% interval, drawn over the bar rather than beside it. */
.fw-open-hci {
  position: absolute; top: 50%; height: 8px;
  transform: translateY(-50%);
  border-left: 2px solid var(--ink);
  border-right: 2px solid var(--ink);
  opacity: 0.55;
}
.fw-open-hci::before {
  content: ""; position: absolute; top: 50%; left: 0; right: 0;
  border-top: 2px solid var(--ink);
}
.fw-open-pnote {
  margin-top: 8px;
  font-size: 14px; color: var(--sub); line-height: 1.4;
}


/* The composition matrix. Worlds down, conversation types across, count in the cell.
   ---------------------------------------------------------------------------------
   One fill, one numeral, no colour ramp: every non-empty cell is 3 by design, so a
   sequential scale would encode a constant. System colours are deliberately absent -
   they mean "which model" everywhere else on this page and reusing them for worlds
   would break the one-colour-one-meaning rule. Everything is on the 8px grid. */
.fw-mx { margin: 0; width: 100%; }
.fw-mx-grid {
  --cell: clamp(30px, 3.1vw, 40px);
  --wcol: clamp(120px, 12vw, 168px);
  display: flex; flex-direction: column; gap: 4px;
  overflow-x: auto;
}
.fw-mx-spans, .fw-mx-head, .fw-mx-row, .fw-mx-foot {
  display: grid;
  grid-template-columns: var(--wcol) repeat(var(--n), minmax(var(--cell), 1fr)) 72px;
  gap: 4px; align-items: center;
}
.fw-mx-spans { grid-template-columns: var(--wcol) repeat(var(--n), minmax(var(--cell), 1fr)) 72px; }
.fw-mx-span {
  grid-column: calc(var(--from) + 1) / calc(var(--to) + 1);
  font-size: 14px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--grid);
  padding-bottom: 4px; margin-bottom: 4px;
}
.fw-mx-span.is-nav { color: var(--sub); }
/* Column headings run horizontally. Nothing on this page is rotated. */
.fw-mx-th {
  font-size: 14px; font-weight: 700; line-height: 1.15; color: var(--sub);
  hyphens: auto; overflow-wrap: anywhere;
  align-self: end; padding-bottom: 4px;
}
.fw-mx-corner { }
.fw-mx-world {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  font-size: 14px; font-weight: 700; color: var(--ink);
}
.fw-mx-globe { width: 24px; height: auto; flex: none; }
.fw-mx-cell {
  aspect-ratio: 1; min-height: var(--cell);
  display: grid; place-items: center;
  border-radius: 8px;
  background: color-mix(in oklab, var(--ink) 8%, transparent);
  font-size: 14px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Deliberately empty, not missing. */
.fw-mx-cell.is-empty {
  background: transparent;
  border: 1px solid var(--grid);
}
/* The one rule that separates the nine shared shapes from the two navigation adds. */
.fw-mx-th.is-cut, .fw-mx-cell.is-cut, .fw-mx-colt.is-cut { margin-left: 12px; box-shadow: -12px 0 0 -11px var(--sub); }
/* Pathfinding is the world that behaves differently, and the layout says so first. */
.fw-mx-row.is-apart { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--grid); }
.fw-mx-marg, .fw-mx-colt { display: flex; align-items: center; gap: 8px; }
.fw-mx-margbar {
  height: 8px; width: var(--w); border-radius: 2px;
  background: var(--muted); opacity: .25;
}
.fw-mx-marg span, .fw-mx-colt span {
  font-size: 14px; font-weight: 700; color: var(--sub);
  font-variant-numeric: tabular-nums;
}
.fw-mx-foot { align-items: start; padding-top: 4px; border-top: 1px solid var(--grid); }
.fw-mx-colt { flex-direction: column; gap: 2.0px; }
.fw-mx-coltbar {
  width: 8px; height: calc(var(--h) * 0.24); min-height: 3px;
  border-radius: 2px; background: var(--muted); opacity: .25;
}
.fw-mx-total { font-size: 14px; font-weight: 800; color: var(--ink); }
.fw-mx-cap { margin-top: 12px; font-size: 17px; line-height: 1.55; color: var(--sub); max-width: 72ch; }
.fw-mx-note { margin-top: 8px; font-size: 14px; color: var(--muted); }


/* Duration violins. One colour, one shared axis, the cap drawn rather than implied.
   ---------------------------------------------------------------------------------
   Six hues here would invite the reader to look for a meaning that is not there: the
   comparison is between SHAPES. Pathfinding is set apart by the same rule and gap the
   composition matrix uses, not by a highlight colour. */
.fw-vio { margin: 0; width: 100%; }
.fw-vio-rows { display: flex; flex-direction: column; gap: 8px; }
.fw-vio-row {
  display: grid;
  grid-template-columns: clamp(96px, 10vw, 132px) minmax(0, 1fr) clamp(150px, 15vw, 196px);
  align-items: center; gap: 16px;
  height: 72px;
}
.fw-vio-row.is-apart { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--grid); }
.fw-vio-name { font-size: 14px; font-weight: 700; color: var(--ink); }
.fw-vio-plot { position: relative; height: 64px; }
.fw-vio-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.fw-vio-body { fill: var(--ink); fill-opacity: .12; stroke: var(--ink); stroke-width: 1.5; }
/* The box the numbers get quoted from. Never a violin without one. */
.fw-vio-whisk {
  position: absolute; top: 50%; height: 1.5px; transform: translateY(-50%);
  background: var(--ink); opacity: .55;
}
.fw-vio-box {
  position: absolute; top: 50%; height: 6px; transform: translateY(-50%);
  background: var(--ink); border-radius: 1px;
}
.fw-vio-med {
  position: absolute; top: 50%; width: 2px; height: 10px;
  transform: translate(-1px, -50%); background: #fff;
}
/* Real data at a hard boundary, so it is a solid stub and not part of the smooth density. */
.fw-vio-stub {
  position: absolute; left: 0; bottom: 50%; width: 6px;
  height: calc(var(--h) * 0.32); min-height: 4px;
  transform: translate(-3px, 0);
  background: var(--ink); opacity: .6; border-radius: 1px;
}
.fw-vio-gut {
  font-size: 14px; color: var(--sub); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.fw-vio-axis {
  position: relative; height: 24px; margin-top: 20px;
  margin-left: clamp(112px, calc(10vw + 16px), 148px);
  margin-right: clamp(166px, calc(15vw + 16px), 212px);
  border-bottom: 1px solid var(--grid);
}
.fw-vio-tick {
  position: absolute; bottom: 2px; transform: translateX(-50%);
  font-size: 14px; color: var(--muted);
}
.fw-vio-capline {
  position: absolute; top: 0; bottom: -428px; width: 0;
  border-left: 1.5px dashed var(--sub); opacity: .7;
}
.fw-vio-capline b {
  position: absolute; top: -18px; left: 6px;
  max-width: 64px;
  font-size: 14px; font-weight: 700; color: var(--sub); white-space: nowrap;
}
.fw-vio-annot {
  margin: 4px 0 0 clamp(112px, calc(10vw + 16px), 148px);
  font-size: 14px; font-weight: 700; color: var(--ink);
}
.fw-vio-cap { margin-top: 16px; font-size: 17px; line-height: 1.55; color: var(--sub); max-width: 76ch; }

/* Below 700px a violin cannot be read, so the row degrades to the numbers it carries:
   the median as a dot and the interquartile range as a bar. */
@media (max-width: 700px) {
  .fw-vio-row { grid-template-columns: 92px minmax(0, 1fr); height: 44px; row-gap: 2.0px; }
  .fw-vio-plot { height: 24px; }
  .fw-vio-svg, .fw-vio-stub { display: none; }
  .fw-vio-med { height: 14px; width: 3px; background: var(--ink); border-radius: 2px; }
  .fw-vio-gut { grid-column: 1 / -1; font-size: 14px; }
  .fw-vio-axis { margin-left: 92px; margin-right: 0; }
  .fw-vio-capline { bottom: -300px; }
}


/* The nine conversation types, as a card set. The paper's own mark heads each card. */
.fw-card-ic {
  width: 32px; height: 32px; margin-bottom: 8px;
  display: flex; align-items: center; justify-content: center;
}
.fw-card-icimg { width: 100%; height: 100%; object-fit: contain; }
.fw-cards-lede {
  margin: 0 0 12px; font-size: 17px; font-weight: 700; color: var(--ink);
}
.fw-cards-foot {
  margin: 12px 0 0; font-size: 14px; color: var(--sub);
  padding-top: 8px; border-top: 1px solid var(--grid);
}


/* The three pillars. One column each, each with its own coloured header bar. */
.fw-pil {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px;
  align-items: start;
}
.fw-pil-col { min-width: 0; }
.fw-pil-head {
  border-top: 4px solid var(--tint);
  padding: 8px 0 8px;
  margin-bottom: 8px;
}
.fw-pil-h {
  margin: 0; font-size: 17px; font-weight: 800; color: var(--tint-t, var(--tint));
  letter-spacing: -0.01em;
}
.fw-pil-l { margin: 2.0px 0 0; font-size: 14px; color: var(--sub); }
.fw-pil-m {
  background: var(--surface);
  border: 1px solid var(--grid);
  border-radius: 10px;
  padding: 12px;
  margin-bottom: 8px;
}
.fw-pil-sym {
  font-size: 17px; font-weight: 800; color: var(--tint-t, var(--tint));
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.fw-pil-full { font-size: 14px; font-weight: 700; color: var(--ink); margin-top: 2.0px; }
.fw-pil-says { margin: 8px 0 0; font-size: 14px; line-height: 1.45; color: var(--sub); }
.fw-pil-range { margin-top: 8px; font-size: 14px; color: var(--muted); }

/* The claim the whole suite exists to protect. */
.fw-callout {
  margin-top: 16px;
  background: var(--canvas);
  border-left: 4px solid var(--ink);
  border-radius: 0 10px 10px 0;
  padding: 16px;
}
.fw-callout-h { display: block; font-size: 20px; font-weight: 800; color: var(--ink); }
.fw-callout-b { margin: 8px 0 0; font-size: 17px; line-height: 1.55; color: var(--sub); }

@media (max-width: 900px) {
  .fw-pil { grid-template-columns: 1fr; }
}


/* The recording player. One button, one bar, one clock - the default <audio> control is
   300px of browser chrome with a volume slider and a download menu this page cannot use. */
.fw-pl {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
}
.fw-pl-btn {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--grid); border-radius: 50%;
  background: var(--surface); color: var(--ink);
  cursor: pointer; padding: 0;
}
.fw-pl.is-on .fw-pl-btn {
  background: var(--sys, var(--ink)); border-color: var(--sys, var(--ink));
  /* White on the mint system colour is 2.18:1 and on orange 2.3:1, so the pairing is
     decided per system in sysInk() rather than assumed to be white. */
  color: var(--on-sys, #fff);
}
.fw-pl-btn svg { width: 15px; height: 15px; fill: currentColor; }
.fw-pl-btn:focus-visible { outline: 2px solid var(--sys, var(--ink)); outline-offset: 2px; }
.fw-pl-bar {
  flex: 1 1 auto; min-width: 0; height: 6px;
  -webkit-appearance: none; appearance: none;
  border-radius: 3px; cursor: pointer;
  background: linear-gradient(to right,
    var(--sys, var(--ink)) 0 var(--pc, 0%), var(--grid) var(--pc, 0%) 100%);
}
.fw-pl-bar::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--sys, var(--ink)); border: 2px solid var(--surface);
}
.fw-pl-bar::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; border: 2px solid var(--surface);
  background: var(--sys, var(--ink));
}
.fw-pl-bar:focus-visible { outline: 2px solid var(--sys, var(--ink)); outline-offset: 3px; }
.fw-pl-time {
  font-size: 14px; color: var(--sub); font-variant-numeric: tabular-nums;
  white-space: nowrap; min-width: 76px; text-align: right;
}


/* Small multiples. Nine panels on one shared scale, then Pathfinding below a rule. */
.fw-sm { margin: 0; width: 100%; }
.fw-sm-lede { margin: 0 0 16px; font-size: 20px; line-height: 1.45; color: var(--sub); }
.fw-sm-gl {
  margin: 0 0 8px; font-size: 14px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.fw-sm-grp.is-apart { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--grid); }
.fw-sm-wrap { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.fw-sm-pan {
  background: var(--surface); border: 1px solid var(--grid); border-radius: 10px;
  padding: 12px; min-width: 0;
}
.fw-sm-name { font-size: 14px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
.fw-sm-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 46px;
  align-items: center; column-gap: 8px; margin-bottom: 4px;
}
.fw-sm-row:has(.fw-sm-sys) { grid-template-columns: 142px minmax(0, 1fr) 46px; }
.fw-sm-sys { font-size: 14px; font-weight: 700; color: var(--sub); white-space: nowrap; }
.fw-sm-track { height: 8px; border-radius: 3px; background: var(--grid); position: relative; }
.fw-sm-bar {
  position: absolute; inset: 0 auto 0 0; min-width: 3px;
  border-radius: 3px; background: var(--sys, var(--ink));
}
.fw-sm-val {
  font-size: 14px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: right;
}
.fw-sm-note { margin-top: 16px; font-size: 14px; color: var(--muted); max-width: 76ch; }
@media (max-width: 900px) { .fw-sm-wrap { grid-template-columns: 1fr; } }


/* The three navigation shapes, stated beside the walks that demonstrate them. */
.fw-navlede { margin: 0 0 16px; font-size: 20px; line-height: 1.45; color: var(--sub); max-width: 72ch; }
.fw-navcards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-top: 16px; }
.fw-navcard {
  background: var(--surface); border: 1px solid var(--grid);
  border-radius: 10px; padding: 16px; min-width: 0;
}
.fw-navcard-h { margin: 0; font-size: 17px; font-weight: 800; color: var(--ink); }
.fw-navcard-l { margin: 8px 0 0; font-size: 14px; line-height: 1.45; color: var(--sub); }
@media (max-width: 900px) { .fw-navcards { grid-template-columns: 1fr; } }


/* The three headline results, on the opening's bottom row. */
.fw-open-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 2.0px; flex-wrap: wrap; }
.fw-open-headv {
  /* Compact on purpose: on the opening's short bottom row every 8px of headline costs a
     system row in the chart underneath, and a chart that shows four of five systems is
     worse than a smaller numeral. */
  font-size: clamp(20px, 1.5vw, 20px); font-weight: 800; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.fw-open-headw {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 14px; font-weight: 700; color: var(--sys, var(--sub)); white-space: nowrap;
}
.fw-open-headw .fw-vmark { width: 14px; height: 14px; }


/* Visible only when it has focus, which is the whole point of it. */
.fw-skip {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  transform: translateY(-200%);
  background: var(--ink); color: #fff;
  padding: 8px 16px; border-radius: 8px;
  font-size: 14px; font-weight: 700; text-decoration: none;
}
.fw-skip:focus { transform: translateY(0); outline: 2px solid var(--pillar-agentic); outline-offset: 2px; }


/* THE NINE SHAPES
   ---------------------------------------------------------------------------------
   Nine equal cards, which is what makes a 3x3 read as a taxonomy rather than a collage.
   No per-card accent: the colour was assigned by position in the list and meant nothing,
   and on this page a colour means a system. The paper's own mark carries the identity. */
.fw-shell-split[data-block="band"] .fw-cards {
  display: grid !important;
  grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  grid-auto-rows: 1fr;
  gap: 16px !important;
  align-items: stretch;
}
.fw-shell-split[data-block="band"] .fw-card {
  background: var(--surface);
  border: 1px solid var(--grid);
  border-top: 1px solid var(--grid);
  border-radius: 12px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* Bold or capitals, never both. */
.fw-shell-split[data-block="band"] .fw-card-h {
  text-transform: none;
  letter-spacing: -0.005em;
  font-size: 17px;
  font-weight: 800;
  color: var(--ink);
  margin: 0 0 4px;
}
.fw-shell-split[data-block="band"] .fw-card-ic {
  width: 28px; height: 28px; margin-bottom: 8px;
}
.fw-shell-split[data-block="band"] .fw-card-l {
  margin: 0 0 12px;
  font-size: 14px; line-height: 1.45; color: var(--sub);
}
/* The exchange is the evidence: it shows the shape rather than describing it, so it sits
   at the foot of the card on a ground that separates it from the definition above. */
.fw-shell-split[data-block="band"] .fw-chat {
  font-size: 14px; line-height: 1.4;
  border-radius: 10px; padding: 8px 8px; margin-top: 4px;
  max-width: 92%;
}
.fw-shell-split[data-block="band"] .fw-chat.is-user {
  background: var(--canvas); color: var(--ink); align-self: flex-start;
  border: 1px solid var(--grid);
}
.fw-shell-split[data-block="band"] .fw-chat.is-agent {
  background: color-mix(in oklab, var(--pillar-agentic) 9%, transparent);
  color: var(--ink); align-self: flex-end;
  border: 1px solid color-mix(in oklab, var(--pillar-agentic) 22%, transparent);
}


/* Conversations by world, as a ring. One hue, no legend, counts stated beside the arc. */
.fw-ring-wrap {
  display: flex; align-items: center; gap: 12px;
  flex: 1 1 auto; min-height: 0; margin-top: 4px;
}
.fw-ring { position: relative; flex: 0 0 auto; aspect-ratio: 1; height: 100%; max-height: 118px; }
.fw-ring-svg { width: 100%; height: 100%; display: block; }
/* The five enterprise worlds share one ink; Pathfinding, which differs in size and in
   kind, is the lighter arc. Colour here would collide with the system palette. */
.fw-ring-seg { stroke: var(--wc, var(--ink)); opacity: 1; }
.fw-ring-mid {
  position: absolute; inset: 0; display: grid; place-content: center; text-align: center;
  pointer-events: none;
}
.fw-ring-mid b {
  display: block; font-size: 20px; font-weight: 800; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.fw-ring-mid span { font-size: 14px; color: var(--sub); letter-spacing: -0.01em; }
.fw-ring-key {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  /* Capped, so a count sits beside its world instead of drifting to the far edge of the
     tile. A number that far from its label is a second lookup, not a label. */
  flex: 0 1 auto; max-width: 236px; width: 100%;
}
.fw-ring-krow {
  /* A swatch and a name. The counts that used to sit on the right are all 675 except one,
     so as a column they said "these are equal" six times over and buried the one that is
     not; the ring already shows that, and the tooltip and the accessible name still carry
     the exact figures for anyone who wants them. */
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  /* 1.28, not the inherited body leading. Six rows at 14px with normal leading is 12px
     taller than the tile, and the sixth row - Pathfinding, the world the ring exists to
     single out - was cut in half at the card's bottom edge. */
  font-size: 14px; line-height: 1.22; color: var(--sub); white-space: nowrap;
}
.fw-ring-sw {
  flex: 0 0 auto; width: 11px; height: 11px; border-radius: 3px;
  background: var(--wc, var(--ink));
}
.fw-ring-kname { overflow: hidden; text-overflow: ellipsis; }

.fw-ring-khead {
  font-size: 14px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 2.0px;
}

/* The wordmark is the way back to the story from the results and the recordings. */
a.wordmark { text-decoration: none; color: inherit; }
a.wordmark:hover .sub { color: var(--ink); }
a.wordmark:focus-visible { outline: 2px solid var(--pillar-agentic); outline-offset: 3px; border-radius: 6px; }


/* Sort controls and the pillar filter. Presentation only: nothing here recomputes a
   number, it reorders rows and hides columns. */
.fw-p-metric { padding: 0; }
.fw-p-sortbtn {
  width: 100%; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; padding: 4px 4px;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
}
.fw-p-sortbtn::after {
  content: "";
  width: 0; height: 0; opacity: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 4px solid currentColor;
}
.fw-p-metric[data-sort="desc"] .fw-p-sortbtn::after { opacity: 1; }
.fw-p-metric[data-sort="asc"] .fw-p-sortbtn::after {
  opacity: 1; border-top: 0; border-bottom: 4px solid currentColor;
}
.fw-p-sortbtn:hover { color: var(--ink); text-decoration: underline; }
.fw-p-sortbtn:focus-visible { outline: 2px solid var(--pillar-agentic); outline-offset: -2px; }

.fw-p-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.fw-p-filter-l {
  font-size: 14px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.fw-p-chip {
  font-size: 14px; font-weight: 700; cursor: pointer;
  padding: 4px 12px; border-radius: 999px;
  border: 1px solid var(--grid); background: var(--surface); color: var(--sub);
}
.fw-p-chip:hover { color: var(--ink); border-color: var(--muted); }
.fw-p-chip.is-on { background: var(--ink); border-color: var(--ink); color: #fff; }
.fw-p-chip:focus-visible { outline: 2px solid var(--pillar-agentic); outline-offset: 2px; }


/* The call's own speech activity strip: two lanes, the excerpt's window, and a playhead
   that moves while the recording plays. The Pathfinding tiles get this from the walk
   renderer; the enterprise calls had nothing, so the thing this benchmark measures was
   invisible on five of the seven tiles. */
.fw-tl {
  position: relative;
  height: 28px; flex: none;
  margin: 0 8px 4px;
  border-radius: 4px;
  background: var(--canvas);
  border: 1px solid var(--grid);
  overflow: hidden;
}
.fw-tl-lane { position: absolute; left: 0; right: 0; height: 8px; }
.fw-tl-lane.is-agent { top: 4px; }
.fw-tl-lane.is-user { top: 16px; }
.fw-tl-lane i {
  position: absolute; top: 0; height: 100%;
  border-radius: 2px;
}
.fw-tl-lane.is-agent i { background: var(--pillar-agentic); opacity: .85; }
.fw-tl-lane.is-user i { background: var(--pillar-dynamics); opacity: .85; }
/* Where the excerpt below sits inside the whole call. */
.fw-tl-win {
  position: absolute; top: 0; bottom: 0;
  border-left: 1px solid var(--ink); border-right: 1px solid var(--ink);
  background: rgba(23, 23, 31, .06);
}
.fw-tl-head {
  position: absolute; top: 0; bottom: 0; width: 2px; left: 0;
  background: var(--ink);
  transform: translateX(-1px);
}
/* The turn being spoken. Faded in and out rather than switched: a transcript follows the
   audio turn by turn, so an untweened background snapped once per turn and read as a
   flicker running down the page. The out-transition is slower than the in, because a turn
   that has just finished should recede rather than vanish under the one that follows. */
.fw-turn {
  transition: background .34s ease, outline-color .34s ease;
}
.fw-turn.is-now {
  background: color-mix(in oklab, var(--pillar-agentic) 12%, transparent);
  border-radius: 8px;
  outline: 1px solid color-mix(in oklab, var(--pillar-agentic) 30%, transparent);
  transition: background .16s ease, outline-color .16s ease;
}


/* Enlarge. A wide figure in a 1200px band is legible; on a laptop it is not comfortable.
   The clone opens over the page so the live charts behind keep their state. */
.fw-zoom-btn {
  position: absolute; top: 0; right: 0;
  font-size: 14px; font-weight: 700; cursor: pointer;
  padding: 4px 12px; border-radius: 999px;
  border: 1px solid var(--grid); background: var(--surface); color: var(--sub);
}
.fw-zoom-btn:hover { color: var(--ink); border-color: var(--muted); }
.fw-zoom-btn:focus-visible { outline: 2px solid var(--pillar-agentic); outline-offset: 2px; }
.fw-mx, .fw-sm, .fw-vio { position: relative; }

.fw-zoom {
  position: fixed; inset: 0; z-index: 200;
  display: flex; flex-direction: column;
  background: rgba(23, 23, 31, .55);
  padding: 24px;
}
.fw-zoom-head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--surface);
  border-radius: 12px 12px 0 0;
  padding: 12px 16px;
  font-size: 17px; color: var(--ink);
}
.fw-zoom-x {
  border: 1px solid var(--grid); background: var(--surface); color: var(--ink);
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 14px;
}
.fw-zoom-x:focus-visible { outline: 2px solid var(--pillar-agentic); outline-offset: 2px; }
.fw-zoom-body {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  background: var(--surface);
  border-radius: 0 0 12px 12px;
  padding: 24px;
}
/* The clone has room, so it stops compressing: full column widths and taller rows. */
.fw-zoom-body .is-zoomed .fw-zoom-btn { display: none; }
.fw-zoom-body .fw-mx-grid { --cell: 44px; --wcol: 180px; }
.fw-zoom-body .fw-vio-row { height: 96px; }
.fw-zoom-body .fw-sm-wrap { gap: 24px; }
@media (prefers-reduced-motion: reduce) { .fw-zoom { transition: none; } }


/* The ninth cell: which system holds each pillar. */
.fw-leads { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.fw-lead {
  display: grid; grid-template-columns: 1fr auto;
  align-items: baseline; column-gap: 8px; row-gap: 0;
  padding-left: 8px;
  border-left: 4px solid var(--sys, var(--ink));
}
.fw-lead-pillar { font-size: 14px; color: var(--muted); grid-column: 1 / -1; }
.fw-lead-metric { font-size: 14px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.fw-lead-who {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 14px; font-weight: 700; color: var(--sys, var(--sub)); white-space: nowrap;
}
.fw-lead-who .fw-vmark { width: 14px; height: 14px; }


/* The corpus tile: four figures and the shape they divide into.
   ---------------------------------------------------------------------------------
   The three totals run as a strip across the top - numeral over label, three abreast -
   with the ring beneath. Stacked as rows they read as a list that happened to sit above
   a chart; as a strip the tile is one statement. Numerals are the heroes, labels are
   small and quiet, and every one of the four is set the same way. */
.fw-open-stats.is-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px 12px;
  padding-bottom: 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--grid);
}
.fw-open-stats.is-strip .fw-open-i {
  display: flex; flex-direction: column; gap: 0; min-width: 0;
}
.fw-open-stats.is-strip .fw-open-i b {
  font-size: clamp(20px, 1.5vw, 24px); font-weight: 800; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.fw-open-stats.is-strip .fw-open-i span {
  font-size: 14px; color: var(--muted); line-height: 1.2;
}
/* The ring's own total is the fourth figure, so it is set at the same size as the three
   above it rather than shouting over them. */
.fw-open-cell:has(.fw-open-stats.is-strip) .fw-ring-mid b { font-size: 20px; }
.fw-open-cell:has(.fw-open-stats.is-strip) .fw-ring-wrap { margin-top: 0; }


/* A world's results, at full width.
   ---------------------------------------------------------------------------------
   Header, chart, filter, table - in that order, each the width of the page. The half
   width layout put the table beside a film, which cut it at DNSMOS and left three
   columns no reader could reach. */
.fw-plate {
  /* Set by the flight: 0 until this panel is most of the way in, so two world names are
     never legible at the same time. See the note beside --namevis in flight.js. */
  opacity: var(--namevis, 1);
  display: flex; align-items: center; gap: 16px;
  padding-bottom: 12px; margin-bottom: 16px;
  border-bottom: 1px solid var(--grid);
}
.fw-plate-globe { width: clamp(56px, 6vw, 88px); height: auto; flex: none; }
.fw-plate-tx { min-width: 0; }
.fw-plate-name {
  margin: 0; font-size: 32px; font-weight: 800; color: var(--ink);
  letter-spacing: -0.02em; line-height: 1.05;
}
.fw-plate-line { margin: 4px 0 0; font-size: 17px; color: var(--sub); max-width: 72ch; }

/* On a band the table has the page, so it stops needing its own scroller in the common
   case. It keeps one for narrow windows, with a hint that it can be scrolled. */
.fw-shell-split[data-block="band"] .fw-pillars-wrap {
  overflow-x: auto;
  scrollbar-width: thin;
}
.fw-shell-split[data-block="band"] .fw-pillars { min-width: 100%; }
/* The chart above it gets the same width, and the longest system name stops being cut. */
.fw-shell-split[data-block="band"] .fw-bar-name {
  max-width: none; width: auto; white-space: nowrap; overflow: visible;
}
.fw-shell-split[data-block="band"] .fw-bar-row {
  grid-template-columns: 190px minmax(0, 1fr) 104px;
}

/* ------------------------------------------------------------------ call tapes
   A recorded enterprise call: the marker rail, two speech lanes, the effects band, the
   window the shipped audio covers, the transport and the transcript. The colours are the
   paper's three pillar hues plus two reds for the two ways a handover goes wrong; nothing
   here introduces a sixth family. Every marker also carries a title and a legend entry,
   because a reader who cannot separate the hues must still be able to read the tape. */
:root {
  --mk-smooth:     #0B7A56;
  --mk-contested:  #B36800;   /* both spoke at once */
  --mk-failed:     #C0392B;   /* the handover did not complete */
  --mk-interrupt:  #4285F4;   /* one cut into the other */
  --mk-back:       #6d6f87;   /* mm-hm */
  --mk-self:       var(--viod); /* the agent corrected itself */
  --tp-fx:         #C9CBDA;
}
/* THE SIX WORLDS: a warm ramp, yellow through orange and red to purple.
   ---------------------------------------------------------------------------------
   Hue alone was not enough. The first pass put yellow and orange ten degrees apart at the
   same lightness, which on a ring with a hairline gap reads as one slice. So the ramp
   steps DOWN in lightness as it walks round the wheel: where two neighbours are close in
   hue they are far apart in luminance (yellow to orange is 1.64:1), and where they are
   close in luminance their hues are 30 to 49 degrees apart. Both ends hold on white.

   Pathfinding sits at the dark end, which is also the honest place for it: 450
   conversations rather than 675, the only world run in two channels. */
:root {
  --world-banking:     #F7D445;
  --world-insurance:   #FA8C0F;
  --world-travel:      #D33422;
  --world-healthcare:  #B62B6C;
  --world-logistics:   #8F389F;
  --world-pathfinding: #4F277C;
}
/* The system hues are FILLS. As text on the page ground mint is 2.18:1 and orange 2.46:1,
   so any label that took the raw fill was effectively invisible. These are the same five
   hues darkened to the point where they pass 4.5:1 on white, computed rather than picked:
   mint 4.57, blue 4.54, orange 4.51; grok and gpt already pass and are unchanged. */
:root {
  --sys-grok-t:   var(--sys-grok);
  --sys-gpt-t:    var(--sys-gpt);
  --sys-mini-t:   #2C8464;
  --sys-gemini-t: #3974D4;
  --sys-nova-t:   #AE6500;
}

/* TWO COLUMNS, one passing call and one failing call of the same shape on the same row.
   `auto-fit` gave three across on a wide band, which broke the pairing the whole figure
   rests on: the reader could not tell which failure belonged to which success, because the
   second column of one row was the pass of the next pair. The tapes are authored in
   ok/bad order, so two fixed columns put the two halves of a pair side by side at every
   width wide enough to hold them. */
.fw-tapes {
  display: grid; gap: 18px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 14px;
}
.fw-tp {
  margin: 0; padding: 13px 14px 12px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 12px;
  border-top: 3px solid var(--mk-smooth);
  display: flex; flex-direction: column; gap: 9px;
}
.fw-tp.is-bad { border-top-color: var(--mk-failed); }
.fw-tp-cap { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.fw-tp-type { font-size: 15px; font-weight: 640; color: var(--ink); }
.fw-tp-verdict {
  font-size: 14px; font-weight: 640; letter-spacing: .02em;
  padding: 2px 8px; border-radius: 999px;
  background: var(--magbg); color: var(--magd);
}
.fw-tp.is-bad .fw-tp-verdict { background: var(--redbg); color: var(--red); }
.fw-tp-model {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; font-weight: 600; color: var(--sys-t, var(--ink2));
}
.fw-tp-model img, .fw-tp-model svg { width: 15px; height: 15px; border-radius: 3px; }
.fw-tp-line { margin: 0; font-size: 14px; line-height: 1.45; color: var(--soft); }
.fw-tp-box { font-size: 14px; color: var(--grey); }

/* ---- the tape itself */
.fw-tp-tape {
  position: relative; height: 54px; margin-top: 2px;
  background: var(--bg-base); border-radius: 6px; overflow: hidden;
}
.fw-tp-rail { position: absolute; left: 58px; right: 4px; top: 0; height: 11px; }
.fw-tp-rail i {
  position: absolute; top: 1px; width: 2px; height: 9px; margin-left: -1px;
  background: var(--mk); border-radius: 1px;
}
.fw-tp-rail i.fw-tp-tool {
  top: 2px; width: 7px; height: 7px; margin-left: -3.5px; border-radius: 50%;
  background: none; border: 1.6px solid var(--ink2);
}
.fw-tp-rail i.fw-tp-tool.is-err { border-color: var(--red); background: var(--red); }
.fw-tp-lane { position: absolute; left: 0; right: 0; height: 13px; line-height: 13px; }
.fw-tp-lane.is-agent { top: 13px; }
.fw-tp-lane.is-user  { top: 28px; }
/* The lane name gets its own gutter. Printed over the track it sat on top of the first
   forty seconds of speech, which on a five minute call is the whole opening exchange. */
.fw-tp-lane-n {
  position: absolute; left: 6px; top: 0; width: 46px; z-index: 2;
  font-size: 14px; line-height: inherit; font-weight: 600; letter-spacing: .02em;
  color: var(--grey); pointer-events: none; text-align: right;
  white-space: nowrap; overflow: hidden;
}
.fw-tp-track { position: absolute; top: 0; bottom: 0; left: 58px; right: 4px; }
.fw-tp-track i { position: absolute; top: 2px; height: 9px; border-radius: 2px; }
.fw-tp-lane.is-agent .fw-tp-track i { background: var(--pillar-agentic); }
.fw-tp-lane.is-user  .fw-tp-track i { background: var(--pillar-dynamics); }
.fw-tp-fx { position: absolute; left: 58px; right: 4px; top: 44px; height: 6px; }
.fw-tp-fx i { position: absolute; top: 1px; height: 4px; background: var(--tp-fx); border-radius: 1px; }
.fw-tp-fx i.is-burst_noise { background: color-mix(in srgb, var(--viod) 45%, #fff); }
.fw-tp-fx i.is-out_of_turn_speech { background: color-mix(in srgb, var(--ambd) 45%, #fff); }
/* The window the audio covers. A bracket, not a fill: the tape underneath has to stay
   readable through it, and a filled block reads as "this part is different" rather than
   as "this is the part you can hear". */
.fw-tp-win {
  position: absolute; top: 11px; bottom: 8px; pointer-events: none;
  border-left: 2px solid var(--ink2); border-right: 2px solid var(--ink2);
  background: color-mix(in srgb, var(--ink2) 7%, transparent);
}
.fw-tp-head {
  position: absolute; top: 11px; bottom: 6px; width: 2px; margin-left: -1px;
  background: var(--ink); opacity: 0; transition: opacity .18s ease; pointer-events: none;
}
.fw-tp.is-playing .fw-tp-head { opacity: 1; }
.fw-tp-scale {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 14px; color: var(--grey); font-variant-numeric: tabular-nums;
}
.fw-tp-scale-w { color: var(--ink2); font-weight: 600; }

/* ---- transport */
.fw-tp-pl { display: flex; align-items: center; gap: 9px; }
.fw-tp-btn {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  display: grid; place-items: center; cursor: pointer;
}
.fw-tp-btn svg { width: 17px; height: 17px; fill: currentColor; }
.fw-tp-btn:hover { border-color: var(--ink2); }
.fw-tp-bar { flex: 1 1 auto; min-width: 0; accent-color: var(--ink2); height: 20px; }
.fw-tp-time {
  flex: none; font-size: 14px; color: var(--grey);
  font-variant-numeric: tabular-nums; min-width: 92px; text-align: right;
}

/* ---- transcript */
.fw-tp-turns {
  max-height: 190px; overflow-y: auto; display: flex; flex-direction: column; gap: 3px;
  padding-right: 4px;
}
.fw-tp-turn {
  display: grid; grid-template-columns: 52px minmax(0, 1fr) 40px; gap: 8px;
  align-items: baseline; text-align: left;
  background: none; border: 0; border-radius: 6px; padding: 4px 6px; cursor: pointer;
  font: inherit; font-size: 14px; line-height: 1.42; color: var(--text);
}
.fw-tp-turn:hover { background: var(--bg-base); }
/* Same tween as the full transcript above, and for the same reason: this one runs inside a
   190px scroller, so an instant swap moved the eye twice at once - the highlight jumping
   and the list scrolling under it. */
.fw-tp-turn { transition: background .34s ease; }
.fw-tp-turn.is-now {
  background: color-mix(in srgb, var(--ink2) 8%, transparent);
  transition: background .16s ease;
}
.fw-tp-who { font-size: 14px; font-weight: 640; color: var(--pillar-agentic); }
.fw-tp-turn.is-user .fw-tp-who { color: var(--pillar-dynamics-t); }
.fw-tp-at {
  font-size: 14px; color: var(--grey); font-variant-numeric: tabular-nums; text-align: right;
}
.fw-tp-legend {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 10px;
  font-size: 14px; color: var(--soft);
}
.fw-tp-legend span { display: inline-flex; align-items: center; gap: 6px; }
.fw-tp-legend i { width: 10px; height: 10px; border-radius: 2px; background: var(--mk); }
.fw-tp-legend i.is-tool { border-radius: 50%; background: none; border: 1.6px solid var(--ink2); }

@media (prefers-reduced-motion: reduce) {
  .fw-tp-head { transition: none; }
}
@media (max-width: 700px) {
  .fw-tapes { grid-template-columns: 1fr; }
  .fw-tp-turns { max-height: 150px; }
  .fw-tp-model { margin-left: 0; }
}
/* The window bracket and the playhead live in the same coordinate space as the tracks,
   so all five layers agree on where a given second sits. */
.fw-tp-over { position: absolute; left: 58px; right: 4px; top: 0; bottom: 0; pointer-events: none; }
.fw-tp-over .fw-tp-win, .fw-tp-over .fw-tp-head { position: absolute; }

/* ------------------------------------------------------------------ print
   Without this the page prints as one screen, repeated. The stage is position:fixed and
   only the live panel carries opacity, so results.html printed to 44 identical pages of
   the Pathfinding chart and twelve of its thirteen sections never appeared at all.

   Printing is the one context where the scroll flight cannot be the medium: there is no
   scroll, so every panel has to stand still, in order, at full strength. */
@media print {
  html, body { height: auto !important; overflow: visible !important; background: #fff !important; }
  .fw-skip, .topbar, .fw-rail, .fw-hint, .fw-auto, .fw-nav, .fw-globes { display: none !important; }
  /* The film is a scrub surface; on paper it is one arbitrary frame. */
  .fw-video, .fw-art, .fw-sky, .fw-veil, .fw-scrim, .fw-zoom-btn { display: none !important; }
  .fw-stage {
    position: static !important; height: auto !important; width: auto !important;
  }
  .fw-copy {
    position: static !important; inset: auto !important; width: auto !important;
    max-width: none !important; padding: 0 !important; display: block !important;
  }
  .fw-spacer { display: none !important; }
  .fw-panel {
    position: static !important; opacity: 1 !important; visibility: visible !important;
    transform: none !important; filter: none !important; will-change: auto !important;
    width: auto !important; max-width: none !important; margin: 0 0 22mm !important;
    padding: 0 !important; break-inside: avoid; page-break-inside: avoid;
  }
  .fw-panel[hidden] { display: block !important; }
  /* Evidence that scrolls on screen has to unroll on paper, or it prints one column wide. */
  .fw-pillars-wrap, .fw-mx-scroll, .fw-tp-turns {
    overflow: visible !important; max-height: none !important;
  }
  .fw-tp, .fw-card, .fw-navcard, .fw-sm-pan, figure, table {
    break-inside: avoid; page-break-inside: avoid;
  }
  /* A play button on paper is a black circle that says nothing. */
  .fw-tp-pl, .fw-pl, .fw-tp-head, .fw-p-filter, .fw-p-sortbtn { display: none !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 14px; color: #555; }
}

/* ------------------------------------------------------------------ step controls
   Two buttons that move the reader exactly one stop. They sit against the right edge,
   vertically centred, because that is where a thumb and a cursor both already are and it
   is the one column of the page that never carries content at any block or viewport.

   Quiet until wanted: low-contrast at rest, full strength on hover or focus. A scroll page
   should still read as a scroll page. */
.fw-step {
  position: fixed; z-index: 40;
  right: clamp(10px, 1.6vw, 22px); top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  opacity: .58; transition: opacity .2s ease;
}
.fw-step:hover, .fw-step:focus-within { opacity: 1; }
.fw-step-btn {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  -webkit-backdrop-filter: saturate(1.4) blur(8px);
  backdrop-filter: saturate(1.4) blur(8px);
  color: var(--ink); padding: 0;
}
.fw-step-btn svg { width: 22px; height: 22px; display: block; }
.fw-step-btn.is-up svg { transform: rotate(180deg); }
.fw-step-btn:hover:not(:disabled) { border-color: var(--ink2); background: var(--paper); }
.fw-step-btn:disabled { opacity: .3; cursor: default; }
.fw-step-now {
  font-size: 14px; font-variant-numeric: tabular-nums; color: var(--grey);
  letter-spacing: .01em;
}
/* On a phone the right edge is where the thumb is, so it stays - but it drops to the lower
   third, out of the way of the text the reader is actually on. */
@media (max-width: 700px) {
  .fw-step { top: auto; bottom: 18px; transform: none; flex-direction: row; gap: 8px; }
  .fw-step-btn { width: 46px; height: 46px; }
}
@media (prefers-reduced-motion: reduce) {
  .fw-step { transition: none; }
}
@media print { .fw-step { display: none !important; } }

/* ---- the tile as a control, and the call it opens */
.fw-tp { cursor: pointer; transition: border-color .16s ease, box-shadow .16s ease; }
.fw-tp:hover { box-shadow: 0 3px 14px rgba(23, 23, 31, .09); }
.fw-tp:hover .fw-tp-cue, .fw-tp:focus-visible .fw-tp-cue { color: var(--ink2); }
.fw-tp:focus-visible { outline: 2px solid var(--ink2); outline-offset: 3px; }
.fw-tp-cue {
  display: flex; align-items: center; gap: 6px; margin-top: 2px;
  font-size: 14px; font-weight: 600; color: var(--grey);
}
.fw-tp-cue-a { transition: transform .16s ease; }
.fw-tp:hover .fw-tp-cue-a { transform: translateX(3px); }

.fw-call-card { max-width: min(1080px, 94vw); }
.fw-call-head { display: block; }
.fw-call-headrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fw-call-headrow .fw-tp-verdict.is-bad { background: var(--redbg); color: var(--red); }
.fw-call-body { display: flex; flex-direction: column; gap: 14px; }
/* The open call is where the transcript is READ, so it gets real height rather than the
   tile's 190px window. */
.fw-call-body .fw-tp-turns { max-height: min(46vh, 520px); }
.fw-call-body .fw-tp-tape { height: 74px; }
.fw-call-body .fw-tp-lane { height: 18px; line-height: 18px; }
.fw-call-body .fw-tp-lane.is-agent { top: 15px; }
.fw-call-body .fw-tp-lane.is-user { top: 36px; }
.fw-call-body .fw-tp-track i { top: 3px; height: 12px; }
.fw-call-body .fw-tp-fx { top: 62px; }
/* A turn the shipped excerpt does not cover is still part of the conversation, so it is
   dimmed rather than hidden - and it does not offer a seek it cannot honour. */
.fw-tp-turn.is-outside { opacity: .62; cursor: default; }
.fw-tp-turn.is-outside:hover { background: none; }
.fw-call-stats {
  display: flex; flex-wrap: wrap; gap: 6px 26px;
  padding-top: 12px; border-top: 1px solid var(--line);
}
.fw-call-stat { display: flex; align-items: baseline; gap: 7px; font-size: 14px; }
.fw-call-stat-k { color: var(--grey); }
.fw-call-stat b { color: var(--ink); font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) {
  .fw-tp, .fw-tp-cue-a { transition: none; }
}

/* The call dialog is NOT the walk dialog's dark card.
   ---------------------------------------------------------------------------------
   The walk overlay is dark because the map renderer draws its own dark ground and a light
   frame around it reads as a mistake. A conversation is type: the tape, the transcript and
   the readout are all designed against the page's light surface, and inherited into the
   dark card the transcript was grey-on-near-black and the readout's values disappeared
   entirely. Same dialog, same focus trap, its own ground. */
.fw-call .fw-call-card {
  background: var(--paper);
  color: var(--ink);
  height: auto; max-height: min(880px, 92vh);
  box-shadow: 0 24px 70px rgba(23, 23, 31, .30);
}
.fw-call .fw-call-head {
  position: relative;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  padding: 14px 56px 12px 18px;      /* room on the right for the close control */
}
.fw-call .fw-walk-title { font-size: 18px; font-weight: 650; color: var(--ink); }
.fw-call .fw-walk-sub { font-size: 14px; color: var(--soft); margin-top: 3px; }
.fw-call .fw-call-headrow .fw-tp-model { margin-left: auto; }
.fw-call .fw-walk-x {
  position: absolute; top: 12px; right: 12px;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font-size: 20px; line-height: 1; cursor: pointer;
}
.fw-call .fw-walk-x:hover { border-color: var(--ink2); }
.fw-call .fw-call-body { padding: 16px 18px 18px; overflow-y: auto; }

/* ---------------------------------------------------------------- the onward link
   One per page, at the end, naming the next page by what it contains rather than by
   "next". A reader who has finished the samples wants "How this was scored", not an
   arrow. Sized as a real target on a phone (56px tall) rather than a text link. */
.fw-next {
  display: inline-grid;
  grid-template-columns: auto auto;
  grid-template-areas: "k a" "l a";
  align-items: center;
  gap: 0 18px;
  margin-top: 28px;
  padding: 14px 22px 15px;
  min-height: 56px;
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
  transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.fw-next:hover {
  border-color: var(--accent);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .10);
  transform: translateY(-1px);
}
.fw-next:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.fw-next-k {
  grid-area: k;
  font-size: var(--t-micro);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.fw-next-l { grid-area: l; font-size: 19px; font-weight: 620; line-height: 1.25; }
.fw-next-a { grid-area: a; display: flex; color: var(--accent); }
.fw-next-a svg { width: 26px; height: 26px; }
.fw-next-n { grid-column: 1 / -1; font-size: var(--t-micro); color: var(--ink-3); margin-top: 4px; }
@media (prefers-reduced-motion: reduce) { .fw-next { transition: none; } .fw-next:hover { transform: none; } }
@media (max-width: 700px) { .fw-next { width: 100%; } }

/* ------------------------------------------------------------------- the harness figure
   Three columns: who is speaking, the shared channel, and what is being tested. The middle
   column is the argument - both lanes are lit at the same moment, which is the thing a
   turn-by-turn diagram cannot show. */
.fw-hn { margin: 18px 0 0; }
.fw-hn-cols {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .82fr) minmax(0, 1fr);
  gap: 18px; align-items: start;
}
.fw-hn-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.fw-hn-head-k {
  font-size: var(--t-micro); font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-dim);
}
.fw-hn-head-t { font-size: var(--t-micro); color: var(--ink-3); }
.fw-hn-box {
  padding: 10px 13px 11px; border-radius: 12px; margin-bottom: 8px;
  background: #fff; border: 1px solid var(--line);
}
.fw-hn-side.is-caller .fw-hn-box { border-left: 3px solid var(--pillar-dynamics); }
.fw-hn-side.is-agent .fw-hn-box { border-left: 3px solid var(--pillar-agentic); }
.fw-hn-role { font-size: 16px; font-weight: 640; line-height: 1.25; }
.fw-hn-side.is-caller .fw-hn-role { color: var(--pillar-dynamics); }
.fw-hn-side.is-agent .fw-hn-role { color: var(--pillar-agentic); }
.fw-hn-model {
  font-size: var(--t-micro); color: var(--ink-soft); margin-top: 2px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  display: flex; align-items: center; gap: 7px;
}
/* The vendor mark beside a model name, in both columns of the harness. Sized to the cap
   height of the monospace it sits against rather than to the line box, so the logo and the
   name share a baseline instead of the logo riding high. */
.fw-hn-mark { display: inline-flex; flex: none; width: 15px; height: 15px; }
.fw-hn-mark img, .fw-hn-mark svg { width: 100%; height: 100%; display: block; }
.fw-hn-mark svg { color: var(--muted); }
.fw-hn-says { font-size: var(--t-micro); color: var(--ink-soft); margin: 6px 0 0; line-height: 1.42; }
.fw-hn-sys {
  font-size: var(--t-micro); padding: 5px 9px; border-radius: 8px; margin-top: 6px;
  background: rgba(14, 16, 41, .04); color: var(--ink-soft);
  display: flex; align-items: center; gap: 8px;
}
.fw-hn-sys.is-lead {
  background: color-mix(in srgb, var(--pillar-agentic) 12%, #fff);
  color: var(--ink); font-weight: 620;
}
.fw-hn-mid { padding-top: 26px; }
.fw-hn-mid-h { font-size: var(--t-micro); color: var(--ink-3); text-align: center; margin-bottom: 8px; }
.fw-hn-lane { display: grid; grid-template-columns: 5.5em 1fr; align-items: center; gap: 8px; margin-bottom: 8px; }
.fw-hn-lane-n { font-size: var(--t-micro); color: var(--ink-dim); }
.fw-hn-lane-t { height: 14px; border-radius: 5px; background: rgba(14, 16, 41, .06); position: relative; }
/* Deliberately overlapping: the caller's bar starts before the agent's ends. That overlap
   is what the benchmark measures, so the figure has to show it rather than imply it. */
.fw-hn-lane.is-c .fw-hn-lane-t::after,
.fw-hn-lane.is-a .fw-hn-lane-t::after {
  content: ''; position: absolute; top: 0; bottom: 0; border-radius: 5px;
}
.fw-hn-lane.is-c .fw-hn-lane-t::after { left: 46%; right: 4%; background: var(--pillar-dynamics); }
.fw-hn-lane.is-a .fw-hn-lane-t::after { left: 8%; right: 38%; background: var(--pillar-agentic); }
.fw-hn-tick { font-size: var(--t-micro); color: var(--ink-3); text-align: center; margin-top: 2px; }
.fw-hn-mid-n { font-size: var(--t-micro); color: var(--ink-soft); margin: 10px 0 0; line-height: 1.42; text-align: center; }
.fw-hn-more {
  display: inline-block; margin-top: 12px; font-size: var(--t-micro);
  color: var(--pillar-agentic); text-decoration: none; border-bottom: 1px solid currentColor;
}
.fw-hn-more:hover { opacity: .78; }
@media (max-width: 900px) { .fw-hn-cols { grid-template-columns: 1fr; } .fw-hn-mid { padding-top: 4px; } }

/* A run tile that opens is a control, and has to look like one. Same treatment the walk
   tiles carry: a pointer, a lift on hover, and a focus ring that is visible on the paper
   ground rather than only on white. */
.fw-run[role="button"] { cursor: pointer; }
.fw-run[role="button"]:hover {
  border-color: var(--accent, var(--rule));
  box-shadow: 0 6px 20px rgba(0, 0, 0, .09);
}
.fw-run[role="button"]:focus-visible { outline: 3px solid var(--accent, var(--ink)); outline-offset: 3px; }

/* ------------------------------------------------- one real exchange, on the setup page
   The harness diagram claims the caller and the agent hold the channel at once. This is
   the evidence for it: the opening seconds of a scored call, both lanes on one clock, with
   every moment they are both speaking marked. Every value is read from the payload at run
   time - see the note beside the renderer in flight.js - so it cannot drift from the file
   the samples page plays. */
.fw-ex { margin: 14px 0 0; padding: 0; }
.fw-ex-lede {
  font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 9px;
}
.fw-ex-body {
  border: 1px solid var(--line); border-radius: 12px; background: var(--paper);
  padding: 13px 14px 12px;
}
.fw-ex-lanes { position: relative; display: flex; flex-direction: column; gap: 5px; }
.fw-ex-lane { display: grid; grid-template-columns: 52px 1fr; gap: 9px; align-items: center; }
.fw-ex-lane-k {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); text-align: right;
}
.fw-ex-track {
  position: relative; height: 16px; border-radius: 4px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}
.fw-ex-bar { position: absolute; top: 3px; bottom: 3px; border-radius: 3px; }
.fw-ex-bar.is-agent { background: var(--pillar-agentic); }
.fw-ex-bar.is-user  { background: var(--pillar-dynamics); }
/* The overlaps sit ACROSS both lanes rather than on either one, because that is what they
   are: a property of the pair, not of a speaker. Drawn behind the bars so a bar is never
   obscured by the marker that describes it. */
.fw-ex-overs { position: absolute; left: 61px; right: 0; top: 0; bottom: 0; pointer-events: none; }
.fw-ex-over {
  position: absolute; top: -2px; bottom: -2px;
  background: color-mix(in srgb, var(--mag) 26%, transparent);
  border-left: 1px solid var(--mag); border-right: 1px solid var(--mag);
}
.fw-ex-axis {
  display: flex; justify-content: space-between; margin: 6px 0 0 61px;
  font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.fw-ex-talk { margin-top: 11px; display: flex; flex-direction: column; gap: 7px; }
.fw-ex-line {
  margin: 0; font-size: 13.5px; line-height: 1.45; color: var(--ink-2);
  padding-left: 10px; border-left: 2px solid var(--line);
}
.fw-ex-line.is-agent { border-left-color: var(--pillar-agentic); }
.fw-ex-line.is-user  { border-left-color: var(--pillar-dynamics); }
.fw-ex-who {
  display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 1px;
}
.fw-ex-foot { margin: 11px 0 0; font-size: 12.5px; color: var(--ink-2); }
.fw-ex-foot + .fw-hn-more { margin-top: 8px; }
