/* MotionBlind project page: no dependencies, no runtime imports.
   GENERATED: style.css is css/*.css concatenated in filename order by
   scripts/build_io_page.py. Edit the modules, not style.css.
   Palette is the validated categorical set used in every figure, so a series that is
   blue in a chart is blue in the prose that describes it. */
:root {
  /* The page is dark unconditionally: one palette, no prefers-color-scheme branch.
     These hues are selected FOR the dark surface, not lightened from a light set.
     --green is #00C488 rather than a lightened #009E73 because the obvious step
     (#3FBF95) sits at OKLab dE 14.8 from --blue in normal vision and 7.5 under
     deuteranopia, and those two share a chart more often than any other pair
     (uniform vs HORNet, I_Acc vs Acc). */
  --blue: #4DA3D9;
  --red: #CC5A6F;     /* re-stepped: the old #F0645F sat at OKLab dE 4.6 from --amber
                         under deuteranopia, and the two share the model-comparison
                         chart. Rotating toward crimson buys 14.0 without colliding
                         with --pink or --orange. */
  --green: #00C488;
  --amber: #F0B44B;
  --pink:  #E29CC0;   /* reversed arm; Okabe-Ito, matches the figure */
  --orange:#EE8A4A;   /* MotionBlind series; validated vs --green for CVD */
  --ink: #f2f4f7;
  --body: #c7ccd4;
  --muted: #9aa2ad;
  --line: #2a2e35;
  --surface: #14161a;
  --panel: #1b1e24;
  --maxw: 900px;

  /* Tells the browser its own chrome is dark too: scrollbars, focus rings, spinners
     and any default form styling. Without it those stay light and the page reads as
     a dark document sitting inside a light frame. */
  color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--body);
  font: 16px/1.68 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 22px; }

/* ---------------------------------------------------------------- hero */
header.hero {
  border-bottom: 1px solid var(--line);
  padding: 64px 0 40px;
  text-align: center;
}
h1 {
  font-size: clamp(30px, 5vw, 46px);
  line-height: 1.14;
  letter-spacing: -0.02em;
  margin: 0 0 10px;
  color: var(--ink);
}
h1 .sub { display: block; font-size: 0.46em; font-weight: 500; color: var(--muted);
          letter-spacing: 0; margin-top: 12px; }
.venue { color: var(--muted); font-size: 14.5px; margin: 6px 0 22px; }

.authors { margin: 0 0 6px; font-size: 17px; color: var(--ink); }
.equal   { margin: 0 0 4px; font-size: 13px; color: var(--muted); }
.authors sup { font-size: 0.72em; }
.affil   { margin: 0 0 24px; font-size: 14.5px; color: var(--muted); }

.links { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.links a {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 17px; border-radius: 999px;
  background: var(--ink); color: var(--surface);
  text-decoration: none; font-size: 14.5px; font-weight: 550;
  transition: transform .12s ease, opacity .12s ease;
}
.links a:hover { transform: translateY(-1px); opacity: .88; }
.links a.ghost { background: transparent; color: var(--ink); border: 1px solid var(--line); }

/* ---------------------------------------------------------------- sections */
section { padding: 46px 0; border-bottom: 1px solid var(--line); }
section:last-of-type { border-bottom: 0; }
h2 {
  font-size: 25px; letter-spacing: -0.01em; color: var(--ink);
  margin: 0 0 18px; padding-left: 13px; border-left: 4px solid var(--blue);
}
h3 { font-size: 17.5px; color: var(--ink); margin: 30px 0 10px; }
p  { margin: 0 0 15px; }
a  { color: var(--blue); }

.lede { font-size: 18px; color: var(--body); }

code, .mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.885em;
  background: var(--panel); padding: 2px 6px; border-radius: 5px;
}

/* ---------------------------------------------------------------- callouts */
.note {
  background: var(--panel); border-left: 4px solid var(--amber);
  padding: 15px 18px; border-radius: 0 8px 8px 0; margin: 20px 0;
  font-size: 15.2px;
}
.note strong { color: var(--ink); }

.todo {
  background: repeating-linear-gradient(45deg, var(--panel), var(--panel) 9px,
              transparent 9px, transparent 18px);
  border: 1px dashed var(--muted); border-radius: 8px;
  padding: 13px 16px; margin: 16px 0; font-size: 14.5px; color: var(--muted);
}
.todo b { color: var(--ink); }

/* ---------------------------------------------------------------- stat tiles */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
         gap: 13px; margin: 26px 0; }
.stat { background: var(--panel); border-radius: 10px; padding: 17px 16px;
        border-top: 3px solid var(--blue); }
.stat .n { font-size: 27px; font-weight: 680; color: var(--ink); line-height: 1.1;
           letter-spacing: -0.02em; }
.stat .l { font-size: 13px; color: var(--muted); margin-top: 5px; }
.stat.red   { border-top-color: var(--red); }
.stat.green { border-top-color: var(--green); }
.stat.amber { border-top-color: var(--amber); }

/* ---------------------------------------------------------------- tables */
table { width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 14.6px; }
th, td { padding: 9px 11px; text-align: left; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 600; font-size: 12.6px;
     text-transform: uppercase; letter-spacing: .045em; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody tr:hover { background: var(--panel); }
.chance { color: var(--muted); font-style: italic; }

/* ---------------------------------------------------------------- figures */
figure { margin: 26px 0; }
figure img { width: 100%; height: auto; display: block; border: 1px solid var(--line);
             border-radius: 9px; background: var(--panel); }
figcaption { font-size: 13.8px; color: var(--muted); margin-top: 10px; line-height: 1.55; }
figcaption b { color: var(--ink); }

.swatch { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
          margin-right: 5px; vertical-align: baseline; }
.s-blue { background: var(--blue); } .s-red { background: var(--red); }
.s-green { background: var(--green); } .s-amber { background: var(--amber); }
.s-pink { background: var(--pink); }
.dotk { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
        background: var(--surface); border: 1.4px solid var(--ink); margin-right: 5px;
        vertical-align: -1px; }
.mosk { display: inline-block; width: 10px; height: 10px; margin-right: 5px;
        vertical-align: -1px; border-radius: 2px;
        background: repeating-conic-gradient(var(--pink) 0% 25%, transparent 0% 50%) 0 0/6px 6px; }
.s-orange { background: var(--orange); }

/* ---------------------------------------------------------------- bibtex */
pre.bib {
  background: var(--panel); border: 1px solid var(--line); border-radius: 9px;
  padding: 16px 18px; overflow-x: auto; font-size: 13.2px; line-height: 1.55;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--body);
}


@media (max-width: 620px) {
  header.hero { padding: 44px 0 30px; }
  section { padding: 34px 0; }
}

/* ---- Interactive frame browser -------------------------------------------------
   One framed card per instance, stepped through with prev/next. Deliberately NOT a row
   of selector chips: ten chips is a menu, and a menu invites scanning labels instead of
   looking at frames. Sequential paging keeps attention on the one comparison that
   matters -- clip A against clip B.                                                   */
.browser { margin: 18px 0 12px; }

.card {
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,.30), 0 8px 24px rgba(0,0,0,.38);
}
/* This band used to be the one dark element on a light page. Now that the page is dark
   it has to separate the other way, by sitting one step LIGHTER than the card, so the
   question still reads as a header rather than as more body. */
.cardhead { background: var(--panel); padding: 18px 20px 16px;
            border-bottom: 1px solid var(--line); }
.vidline { color: var(--ink); font-size: 16px; margin-bottom: 12px; }
.vidline b { font-family: var(--mono, ui-monospace, monospace); font-weight: 700; }
.vidline .sub { color: var(--muted); font-size: 13.5px; margin-left: 8px; }

.qpill {
  display: inline-block; margin: 0 8px 6px 0; padding: 8px 15px;
  border-radius: 999px; font-size: 13.5px; font-weight: 600; line-height: 1.35;
  background: var(--amber); color: #241a05;
}
/* The opposed question is the same claim inverted; a hollow pill says "same family,
   different item" without introducing a third colour. */
.qpill.alt {
  background: transparent; color: var(--amber);
  box-shadow: inset 0 0 0 1.5px var(--amber);
}

.cardbody { padding: 18px 20px 20px; background: var(--surface); }

/* Circular pager, centred under the card. */
.nav {
  display: flex; align-items: center; justify-content: center; gap: 18px;
  margin: 16px 0 0;
}
.rnd {
  width: 40px; height: 40px; border-radius: 50%; cursor: pointer;
  font: inherit; font-size: 20px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  transition: background .12s, color .12s, border-color .12s;
}
.rnd:hover { background: var(--panel); border-color: var(--muted); color: var(--ink); }
.rnd:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.counter {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 14px; color: var(--muted); min-width: 56px; text-align: center;
}

.clips { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.clip { margin: 0; }
.clip > figcaption {
  font-size: 13px; color: var(--muted); margin: 0 0 10px; text-align: left;
}
.tot { color: var(--muted); }

/* A sampler strip: label, then the eight frames it chose. The frames are real <img>
   tiles rather than one baked sheet, so they reflow, scale on retina, and can be tied
   to the timeline below. */
.strip { margin: 0 0 10px; }
.slab {
  display: inline-block; font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; font-weight: 700; padding: 2px 7px;
  border-radius: 4px; margin-bottom: 5px;
}
.strip.s-uniform .slab { background: color-mix(in srgb, var(--blue) 16%, transparent);  color: var(--blue); }
.strip.s-random  .slab { background: color-mix(in srgb, var(--red) 16%, transparent);   color: var(--red); }
.strip.s-hornet  .slab { background: color-mix(in srgb, var(--green) 16%, transparent); color: var(--green); }

.tiles { display: grid; grid-template-columns: repeat(8, 1fr); gap: 3px; }
.tile { margin: 0; position: relative; }
.tile img {
  width: 100%; height: auto; display: block; border-radius: 3px;
  border: 1px solid transparent; background: var(--line);
  transition: border-color .1s, transform .1s;
}
.tile figcaption {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 9px; color: var(--muted); text-align: center; margin-top: 2px;
}
/* .hot is set on BOTH the tile and its tick, from either direction. */
.tile.hot img { border-color: var(--ink); transform: scale(1.06); z-index: 2; position: relative; }

/* Timeline: one shared rail carrying every sampler's picks as blocks at
   frame_index / total. Blocks rather than hairlines because a 2px tick reads as noise
   at this width -- the gaps between them are the finding, and they need mass to be
   legible. This is the part that makes under-sampling visible. */
.tl { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }

.tlkey {
  display: flex; align-items: center; gap: 14px; margin-bottom: 7px;
  font-size: 11.5px; color: var(--muted);
}
.k { display: inline-flex; align-items: center; text-transform: capitalize; }
.k::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px;
}
.k-uniform::before { background: var(--blue); }
.k-random::before  { background: var(--red); }
.k-hornet::before  { background: var(--green); }
.ktail { margin-left: -4px; }

.rail {
  position: relative; height: 11px; border-radius: 999px;
  background: var(--line);
}
/* 8px, not 13: on a 134-frame clip consecutive picks sit ~3% apart, and a wide block
   made neighbours merge into one smear. Narrow enough that adjacent frames stay
   countable, wide enough to hit with a cursor. */
.blk {
  position: absolute; top: 0; width: 8px; height: 11px; border-radius: 2px;
  transform: translateX(-50%); cursor: default;
  transition: transform .1s, box-shadow .1s;
}
/* Named so the script can hand a ring colour to a shared frame via --ring. */
:root { --k-uniform: var(--blue); --k-random: var(--red); --k-hornet: var(--green); }
.b-uniform { background: var(--blue); }
.b-random  { background: var(--red); }
.b-hornet  { background: var(--green); }

/* A frame chosen by more than one sampler: solid fill for the highest-priority sampler,
   a dotted ring in the next one's colour. Dotted rather than solid so a shared frame is
   distinguishable from a merely adjacent pair even at this size. */
.blk.shared {
  outline: 1.5px dotted var(--ring, var(--muted));
  outline-offset: 1.5px;
}
/* .hot is set on BOTH the block and its tile, from either direction. */
.blk.hot {
  transform: translateX(-50%) scaleY(1.45); z-index: 3;
  /* Halo in the card's own colour so an enlarged block reads as lifted off the rail
     rather than merged into its neighbours. --panel because .clip sets that background. */
  box-shadow: 0 0 0 2px var(--surface);
}
.axis {
  display: flex; justify-content: space-between; margin-top: 4px;
  font-family: var(--mono, ui-monospace, monospace); font-size: 9.5px; color: var(--muted);
}
.gold { font-weight: 700; color: var(--ink); text-transform: uppercase; font-size: 12px; }

.legend { font-size: 13px; color: var(--muted); margin: 12px 0 0; }

/* Prev / next. Visible controls rather than key bindings: a global arrow-key handler
   also swallows arrow-key page scrolling, which is a bad trade for a widget most
   visitors will click through. */
.nav { display: flex; align-items: center; gap: 12px; margin: 0 0 14px; }
.navbtn {
  font: inherit; font-size: 13.5px; cursor: pointer;
  padding: 7px 13px; border-radius: 6px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink);
  transition: border-color .12s, color .12s;
}
.navbtn:hover { border-color: var(--blue); color: var(--blue); }
.navbtn:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.counter {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12.5px; color: var(--muted); margin-right: auto;
}

/* The swap. Content fades and slides a few px in the direction of travel, so which way
   you moved is legible without reading the counter. 8px is deliberately small: this is
   a hint, not a carousel. */
.stage { transition: opacity .15s ease, transform .15s ease; }
.stage.out-left  { opacity: 0; transform: translateX(-8px); }
.stage.out-right { opacity: 0; transform: translateX(8px); }
.stage.in-right  { opacity: 0; transform: translateX(8px); }
.stage.in-left   { opacity: 0; transform: translateX(-8px); }

@media (prefers-reduced-motion: reduce) {
  .stage, .stage.out-left, .stage.out-right, .stage.in-right, .stage.in-left {
    transition: none; opacity: 1; transform: none;
  }
}

/* One column below 760px: two frame strips side by side stop being legible long
   before the viewport gets narrow enough to wrap on its own. */
@media (max-width: 760px) {
  .clips { grid-template-columns: 1fr; }
}

/* Placeholder copy for content that does not exist yet. Muted and italic so it reads as
   a gap rather than as a value someone forgot to remove. */
.pending { color: var(--muted); font-style: italic; margin: 0; }

/* ---- Inline SVG charts ---------------------------------------------------------
   Drawn in the DOM rather than shipped as PNG: they scale on any display, the text is
   real text (searchable, selectable, readable by a screen reader), every mark can be
   hovered, and -- the reason it matters most here -- the fills are CSS variables, so
   ONE chart is correct in both light and dark mode instead of needing two exports. */
.chart { width: 100%; height: auto; display: block; overflow: visible; }

.chart .cg      { stroke: var(--line); stroke-width: 1; }
.chart .ct      { fill: var(--muted); font-size: 12px; text-anchor: middle; }
.chart .ctr     { text-anchor: end; }
.chart .cl      { text-anchor: start; }
.chart .cax     { fill: var(--muted); font-size: 12px; text-anchor: middle; }
.chart .cxl     { text-anchor: middle; }
.chart .cpt     { fill: var(--ink); font-size: 13.5px; font-weight: 600; text-anchor: middle; }
.chart .cval    { fill: var(--ink); font-size: 11.5px; text-anchor: middle; }
.chart .cna     { fill: var(--muted); font-size: 11px; font-style: italic; text-anchor: middle; }

/* The chance / baseline reference. Dashed and recessive: it is a rule to read against,
   not a series. */
.chart .cchance  { stroke: var(--ink); stroke-width: 1.4; stroke-dasharray: 5 4; opacity: .55; }
/* Halo: the baseline label sits inside the plot, where a series line can pass
   straight through it. Painting the stroke first puts a surface-coloured outline
   under the glyphs so they stay readable without an opaque box. */
.chart .cchancet {
  fill: var(--muted); font-size: 11px;
  stroke: var(--surface); stroke-width: 3.5px; paint-order: stroke fill;
}

/* --- the contrastive 2x2 teaser (paper Fig. 1) ------------------------------------ */
.teaser .tgrid { display: grid; grid-template-columns: minmax(150px, 1.15fr) 1fr 1fr;
                 gap: 8px; align-items: stretch; margin: 6px 0 4px; }
.teaser .thead { color: var(--muted); font-size: 13px; padding: 2px 4px 6px;
                 align-self: end; }
.teaser .thead.tq { text-align: center; }
.teaser .tclip { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px;
                 background: var(--panel, transparent); display: flex;
                 flex-direction: column; gap: 7px; justify-content: center; }
.teaser .tname { font-weight: 650; font-size: 14px; }
.teaser .tsub  { color: var(--muted); font-size: 12.5px; }
/* The arrow is the only thing that differs between the two clips, so it is the one
   element drawn rather than described. */
.teaser .tarrow { display: block; height: 4px; border-radius: 2px; position: relative; }
.teaser .tarrow.right { background: linear-gradient(90deg,
                        color-mix(in srgb, var(--blue) 25%, transparent), var(--blue)); }
.teaser .tarrow.left  { background: linear-gradient(90deg,
                        var(--orange), color-mix(in srgb, var(--orange) 25%, transparent)); }
.teaser .tcell { border: 1px solid var(--line); border-radius: 10px; padding: 16px 10px;
                 text-align: center; font-size: 15px; display: flex; gap: 8px;
                 align-items: center; justify-content: center; }
/* Yes/no is stated in words AND marked; the tint alone never carries it. */
.teaser .tcell.yes { border-color: color-mix(in srgb, var(--green) 45%, var(--line));
                     background: color-mix(in srgb, var(--green) 9%, transparent); }
.teaser .tcell.no  { border-color: color-mix(in srgb, var(--red) 40%, var(--line));
                     background: color-mix(in srgb, var(--red) 8%, transparent); }
.teaser .tcell.yes .tick { color: var(--green); }
.teaser .tcell.no  .tick { color: var(--red); }
@media (max-width: 620px) {
  .teaser .tgrid { grid-template-columns: 1fr 1fr; }
  .teaser .thead:first-child, .teaser .tclip { grid-column: 1 / -1; }
}

/* A caveat that belongs to one mark in a figure, carried in the caption rather than
   printed next to the bar (where a short bar puts it on top of the chance rule). */
.subnote { display: block; margin-top: 6px; font-size: 12.5px; color: var(--muted); }

/* Sub-label inside a rowspan'd header cell (Video-MME has only one metric, so its
   "Acc" sits under the name instead of in the second header row). */
.thsub { display: block; font-weight: 400; font-size: 11px; color: var(--muted);
         letter-spacing: .02em; margin-top: 2px; }

/* --- the word cloud (paper Fig. 2b) ----------------------------------------------- */
.chart.cloud .wc { text-anchor: middle; font-weight: 700; stroke: none;
                   transition: opacity .1s; cursor: default; }
.chart.cloud:hover .wc { opacity: .38; }
.chart.cloud .wc:hover, .chart.cloud .wc.hot { opacity: 1; }

/* --- the question-projection scatter (paper Fig. 2c) ------------------------------ */
.chart.scat .sframe { fill: none; stroke: var(--line); stroke-width: 1; }
/* Semi-transparent so overlap reads as density: ~900 points share this frame and opaque
   dots would show only whichever class happened to be drawn last. */
.chart.scat .spt    { opacity: .55; stroke: none; }
/* The hovered point has to read against 1259 neighbours, so it gets full opacity, a size
   bump and a surface-coloured ring rather than a colour change. */
.chart.scat .spt.hot { opacity: 1; r: 5; stroke: var(--surface); stroke-width: 1.6; }
.ctip .ctq { display: block; max-width: 340px; margin: 3px 0 5px; color: var(--ink);
             font-size: 12px; line-height: 1.35; }
.chart.scat .spt.lg { opacity: 1; }
.chart.scat .slg    { fill: var(--ink); font-size: 11.5px; }
.chart.scat .sauc   { fill: var(--muted); font-size: 11px; font-style: italic; }

/* --- the selection-strategy schematic --------------------------------------------- */
.chart.strat .fcell   { fill: var(--line); }
.chart.strat .fnum    { fill: var(--muted); font-size: 10px; }
.chart.strat .tapband { fill: var(--amber); opacity: .11; }
.chart.strat .tapt    { fill: var(--muted); font-size: 11px; font-style: italic;
                        text-anchor: middle; }
/* An unselected cell is drawn, not omitted: the reader needs to see the frames each
   policy passed over, which is the whole comparison. */
.chart.strat .pcell      { fill: var(--line); opacity: .55; }
.chart.strat .pcell.on   { opacity: 1; }
.chart.strat .k-uniform.on { fill: var(--blue);  color: var(--blue); }
.chart.strat .k-random.on  { fill: var(--red);   color: var(--red); }
.chart.strat .k-hornet.on  { fill: var(--green); color: var(--green); }
.chart.strat .k-f2c.on     { fill: var(--pink);  color: var(--pink); }
.chart.strat .anchor  { fill: var(--surface); stroke: var(--ink); stroke-width: 1.4; }

/* --- scatter: in-plot class labels + the series filter ---------------------------- */
/* The label names its cloud from inside the plot, so identity does not depend on a round
   trip to the legend. It sits in searched-for empty space (see scatter_projection), but a
   stray dot can still land under it -- hence the surface halo, the same treatment the
   chance-line label gets above. */
.chart .slabel {
  font: 600 13px/1 inherit; letter-spacing: .01em;
  /* Ink, not the series colour: saturated text over a cloud of the same hue is the one
     thing harder to read than no label at all. The dot beside it carries identity. */
  fill: var(--ink);
  /* 2.5px, not 3.5: a heavier halo starts eating the counters of the letterforms and the
     word reads as embossed rather than as text laid over the plot. */
  stroke: var(--surface); stroke-width: 2.5px; paint-order: stroke fill;
}
.chart .slabeld { fill: currentColor; stroke: var(--surface); stroke-width: 2px; paint-order: stroke fill; }
/* Hidden by the filter. `display:none` rather than a low opacity: a filtered-out cloud
   must drop out of the hover tooltip too, not sit under the cursor invisibly. */
.chart .sgroup[data-off] { display: none; }

/* Filter row: one line directly above the figure it controls. Matches .rnd (the frame
   browser's pager) so the page has one control idiom, not two. */
.cfilter { display: flex; gap: 8px; align-items: center; margin: 0 0 10px; flex-wrap: wrap; }
.cfilter .flab { font-size: 12.5px; color: var(--muted); }
.cfilter button {
  font: inherit; font-size: 12.5px; line-height: 1; color: var(--muted); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; display: inline-flex; align-items: center; gap: 6px;
  transition: background .12s, color .12s, border-color .12s;
}
.cfilter button:hover { background: var(--panel); border-color: var(--muted); color: var(--ink); }
/* Pressed state rides on aria-pressed, so the paint and the accessibility tree cannot
   disagree about which series are showing. */
.cfilter button[aria-pressed="true"] { color: var(--ink); border-color: var(--muted); background: var(--panel); }
.cfilter button[aria-pressed="false"] .dot { opacity: .3; }
.cfilter button:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.cfilter .dot { width: 8px; height: 8px; border-radius: 50%; }
.cfilter .f-tb .dot { background: var(--green); }
.cfilter .f-mb .dot { background: var(--orange); }
/* Region annotation: an author's note about a lobe, not a third series. Muted and
   italic so it reads as commentary, with a hairline leader to the cluster it names. */
.chart .sannot {
  font: italic 500 11.5px/1 inherit; fill: var(--muted);
  stroke: var(--surface); stroke-width: 2.5px; paint-order: stroke fill;
}
.chart .sleader { stroke: var(--muted); stroke-width: 1; opacity: .45; }

/* --- paper tables: group headers, footnotes, explicit zeros/absences -------------- */
.grouphead td   { color: var(--muted); font-size: 13px; padding-top: 14px;
                  border-bottom: none; letter-spacing: .01em; }
.humanrow td    { border-top: 2px solid var(--line); }
.fnote          { color: var(--muted); font-size: 12.6px; line-height: 1.65;
                  margin-top: -6px; }
.fnote sup      { color: var(--ink); }
.supp           { color: var(--muted); font-size: 12.8px; font-style: italic;
                  margin: -4px 0 12px; }
/* A hard zero is the finding here, so it is marked rather than left to read as an
   ordinary small number; "not run" is styled differently again, because a zero and an
   absent cell mean opposite things. */
.num.zero       { color: var(--ink); font-weight: 620; }
.num.na         { color: var(--muted); font-style: italic; font-size: 12.5px;
                  font-weight: 400; }
.chart .cna     { fill: var(--muted); font-size: 10.5px; font-style: italic;
                  text-anchor: middle; }

/* --- horizontal forms: row labels, segment labels, word labels ------------------- */
.chart .crow    { fill: var(--ink); font-size: 11.5px; }
.chart .cbword  { fill: var(--ink); font-size: 11.5px; text-anchor: middle; }
.chart .cseglab { fill: var(--muted); font-size: 11px; text-anchor: middle; }
.chart .csegn   { fill: var(--ink); font-weight: 650; }
.chart .coff    { fill: var(--muted); font-size: 11.5px; text-anchor: start;
                  font-style: italic; }
.chart .caxis   { stroke: var(--muted); stroke-width: 1.2; opacity: .8; }

/* --- the make-up donut (paper Fig. 2a) -------------------------------------------- */
/* Direction's two subtypes are two steps of ONE hue, as in the paper: they are subtypes
   of the same characteristic, and giving them unrelated colours would imply otherwise.
   The steps are wide (not the obvious adjacent pair) because blue and light blue collapse
   under deuteranopia -- these clear it at OKLab dE 13.5 light / 9.4 dark. */
.chart.dnut .arc.a0 { fill: #F0B44B; }   /* speed */
.chart.dnut .arc.a1 { fill: #00C488; }   /* magnitude */
.chart.dnut .arc.a2 { fill: #1C6AAE; }   /* direction (translational) */
.chart.dnut .arc.a3 { fill: #A8D8F8; }   /* direction (rotational) */
.chart.dnut .thring.t0 { stroke: #F0B44B; }
.chart.dnut .thring.t1 { stroke: #00C488; }
.chart.dnut .thring.t2 { stroke: #1C6AAE; }
.chart.dnut .thring.t3 { stroke: #A8D8F8; }
.chart.dnut .arc     { stroke: var(--surface); stroke-width: 2; transition: opacity .1s; }
.chart.dnut:hover .arc { opacity: .7; }
.chart.dnut .arc:hover, .chart.dnut .arc.hot { opacity: 1; }
/* The count sits on the ring, so it takes the one fill that reads on all four steps:
   white with a dark halo under it. */
.chart.dnut .arcn    { fill: #fff; font-size: 13px; font-weight: 700; text-anchor: middle;
                       stroke: rgba(0,0,0,.45); stroke-width: 2.6px;
                       paint-order: stroke fill; }
.chart.dnut .arclab  { fill: var(--ink); font-size: 12.5px; }
.chart.dnut .lead    { stroke: var(--line); stroke-width: 1.2; }
.chart.dnut .thring  { fill: none; stroke-width: 2.4; }
.chart.dnut .cmid    { fill: var(--muted); font-size: 12px; text-anchor: middle; }
.chart.dnut .cmid.b  { fill: var(--ink); font-size: 13.5px; font-weight: 700; }
/* --- paper tables: group headers, footnotes, explicit zeros/absences -------------- */
.grouphead td   { color: var(--muted); font-size: 13px; padding-top: 14px;
                  border-bottom: none; letter-spacing: .01em; }
.humanrow td    { border-top: 2px solid var(--line); }
.fnote          { color: var(--muted); font-size: 12.6px; line-height: 1.65;
                  margin-top: -6px; }
.fnote sup      { color: var(--ink); }
.supp           { color: var(--muted); font-size: 12.8px; font-style: italic;
                  margin: -4px 0 12px; }
/* A hard zero is the finding here, so it is marked rather than left to read as an
   ordinary small number; "not run" is styled differently again, because a zero and an
   absent cell mean opposite things. */
.num.zero       { color: var(--ink); font-weight: 620; }
.num.na         { color: var(--muted); font-style: italic; font-size: 12.5px;
                  font-weight: 400; }
.chart .cna     { fill: var(--muted); font-size: 10.5px; font-style: italic;
                  text-anchor: middle; }

/* --- horizontal forms: row labels, segment labels, word labels ------------------- */
.chart .crow    { fill: var(--ink); font-size: 11.5px; }
.chart .cbword  { fill: var(--ink); font-size: 11.5px; text-anchor: middle; }
.chart .cseglab { fill: var(--muted); font-size: 11px; text-anchor: middle; }
.chart .csegn   { fill: var(--ink); font-weight: 650; }
.chart .coff    { fill: var(--muted); font-size: 11.5px; text-anchor: start;
                  font-style: italic; }
.chart .caxis   { stroke: var(--muted); stroke-width: 1.2; opacity: .8; }

/* The make-up strip is ONE quantity (instances) split into parts, so its segments step
   through a single hue rather than taking four categorical colours: the categories are
   already named on the strip, and four hues would imply four unrelated series. */
.chart .kseg.s0 { fill: #7FC0EA; }
.chart .kseg.s1 { fill: #5FA5D4; }
.chart .kseg.s2 { fill: #3F7FAE; }
.chart .kseg.s3 { fill: #2A5C82; }
/* Series colours are NAMED, not positional: an entity keeps its hue across figures
   (TimeBlind is green everywhere) rather than depending on which slot it landed in. */
.chart .k-blue   { fill: var(--blue);   stroke: var(--blue); }
.chart .k-green  { fill: var(--green);  stroke: var(--green); }
.chart .k-red    { fill: var(--red);    stroke: var(--red); }
.chart .k-amber  { fill: var(--amber);  stroke: var(--amber); }
.chart .k-pink   { fill: var(--pink);   stroke: var(--pink); }
.chart .k-orange { fill: var(--orange); stroke: var(--orange); }

.chart .cline { fill: none; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; }
.chart .cdot  { stroke: var(--surface); stroke-width: 1.6; }

.chart .cbar, .chart .cdot { transition: opacity .1s; cursor: default; }
/* Hovering a chart de-emphasises the marks you are not pointing at. At .45 this read as
   "the chart is greyed out / broken" rather than as focus, so it is a light touch. */
.chart:hover .cbar, .chart:hover .cdot { opacity: .72; }
.chart .cbar:hover, .chart .cdot:hover { opacity: 1; }

/* The chart legend reuses the swatch chips from the rest of the page. */
.clegend { display: flex; flex-wrap: wrap; gap: 18px; font-size: 12.5px; color: var(--muted); margin: 6px 0 0; }

/* Chart tooltip. One element for the whole page, moved to the cursor -- cheaper than a
   tooltip per chart and it cannot end up with two visible at once. */
.ctip {
  position: fixed; z-index: 50; pointer-events: none;
  opacity: 0; transition: opacity .1s;
  /* A panel step, NOT the old inverted (near-white) chip. Inverting was right when the
     page was light; on a dark page it glares, it washes out the series dots below, which
     are stepped for a dark backdrop, and it made .ctq invisible -- that rule paints the
     question in var(--ink), which WAS the tooltip's own background. */
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line);
  padding: 6px 10px; border-radius: 6px;
  font-size: 12.5px; line-height: 1.35; white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0,0,0,.55);
}
.ctip.on { opacity: 1; }
/* Multi-row layout: a header naming the x-position, then one row per series that shares
   it. Values are right-aligned in a fixed column so several rows read as a small table
   rather than ragged text. */
.ctip .cth { display: block; opacity: .7; font-size: 11px; margin-bottom: 5px; }
.ctip .ctr2 {
  display: flex; align-items: center; gap: 7px; line-height: 1.5;
}
.ctip .ctr2 b { margin-left: auto; padding-left: 14px; font-variant-numeric: tabular-nums; }
.ctip .cts { width: 8px; height: 8px; border-radius: 50%; flex: none; background: currentColor; }
.ctip .ctu { display: block; opacity: .6; font-size: 10.5px; margin-top: 5px; }

/* The swatch reuses the chart's own series colours, so tooltip and plot cannot drift. */
.ctip .k-blue { color: var(--blue); }   .ctip .k-green  { color: var(--green); }
.ctip .k-red  { color: var(--red); }    .ctip .k-amber  { color: var(--amber); }
.ctip .k-pink { color: var(--pink); }   .ctip .k-orange { color: var(--orange); }
.ctip .k-uniform { color: var(--blue); } .ctip .k-random { color: var(--red); }
.ctip .k-hornet  { color: var(--green); } .ctip .k-f2c { color: var(--pink); }

/* Every peer at the hovered x lights up together, so the tooltip's rows and the marks
   they describe are visibly the same set. */
.chart .cbar.hot, .chart .cdot.hot { opacity: 1; }
.chart .cdot.hot { r: 5; }
