/* ============================================================
   Tiling attention — scrollytelling prototype
   Cream paper, ink type, three spot inks.
   Lenis + GSAP/ScrollTrigger via CDN, Helvetica served locally.
   ============================================================ */

/* Helvetica Now Display is tried first via local(): it is a paid
   Monotype licence, so it only renders if it is installed on the
   machine. Otherwise TeX Gyre Heros (URW Nimbus Sans, a direct clone
   of classic Helvetica, GUST Font License) loads from fonts/.
   The family is listed FIRST in the stack on purpose: Windows aliases
   the name "Helvetica" to Arial at the OS level, so putting Helvetica
   ahead of it silently resolves to Arial and the real face never loads. */
@font-face {
  font-family: "Heros";
  src: local("Helvetica Now Display"), local("Helvetica Now Display Regular"),
       local("HelveticaNowDisplay-Regular"),
       url("fonts/heros-regular.otf") format("opentype");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Heros";
  src: local("Helvetica Now Display Bold"), local("HelveticaNowDisplay-Bold"),
       url("fonts/heros-bold.otf") format("opentype");
  font-weight: 700;
  font-display: swap;
}

/* Palette "lagoon": cold inks only. Petrol, periwinkle and pale aqua.
   The token names are roles, kept from the first palette. Each ink has
   one job, and it is the same job in the diagram and on the page:
     --blue     petrol, the thing being worked on (query block, structure)
     --red      periwinkle, now (the live tile, the active step)
     --saffron  pale aqua, done (accumulated output, results) */
:root {
  --paper:  #f4f0e6;
  --ink:    #13120f;
  --ink-60: #6b675e;
  --ink-30: #b2ada2;
  --line:   #dcd6c9;
  --panel:  #ebe6da;

  --blue:    #134e6f;
  --red:     #5b5bd6;
  --saffron: #a9dcd9;
  --blue-tint: color-mix(in srgb, var(--blue) 13%, var(--paper));

  /* blue as a surface (the pull band, a hovered tile) and the text that
     sits on the accents. Separate from --blue because in the dark theme
     blue *text* has to lighten to stay readable, while the band keeps
     its deep petrol. */
  --blue-deep:  var(--blue);
  /* the "now" ink as small text takes a deeper cut of the fill, so it
     holds 4.5:1 on paper however light the fill is tuned */
  --red-text:   #4a4ac4;
  --on-accent:  #f7f3ea;
  --on-saffron: #13120f;
  color-scheme: light;

  /* the live site's variable names, which site.js's widgets read with
     getPropertyValue(), pointed at this palette */
  --grey:      var(--ink-60);
  --faint:     var(--ink-60);
  --hairline:  var(--line);
  --code-bg:   var(--panel);
  --selection: var(--blue-tint);

  --serif:   "Source Serif 4", Georgia, serif;
  --display: "Heros", Helvetica, "Helvetica Neue", Arial, sans-serif;
  --mono:    "JetBrains Mono", ui-monospace, Menlo, monospace;

  --measure: 34rem;
  --pad: clamp(1.25rem, 5vw, 5rem);
}

/* Dark: the same warm paper-and-ink idea inverted, not a cold slate.
   The accents step up in lightness to hold contrast on the dark ground;
   --blue-deep stays deep petrol, so the pull band still reads as the
   same object in both themes. Follows the OS unless the toggle has
   made an explicit choice, which then wins either way. Only tokens
   change here - every rule above and below reads them. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper:  #15130f;
    --ink:    #ebe5d8;
    --ink-60: #a29b8e;
    --ink-30: #5c574e;
    --line:   #2d2922;
    --panel:  #1e1b16;
    --blue:      #6fb8d9;
    --blue-deep: #134e6f;
    --red:       #9a9cff;
    --red-text:  #9a9cff;
    --saffron:   #a9dcd9;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --paper:  #15130f;
  --ink:    #ebe5d8;
  --ink-60: #a29b8e;
  --ink-30: #5c574e;
  --line:   #2d2922;
  --panel:  #1e1b16;
  --blue:      #6fb8d9;
  --blue-deep: #134e6f;
  --red:       #9a9cff;
  --red-text:  #9a9cff;
  --saffron:   #a9dcd9;
}





* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* Lenis wants this so it can take over the scroll */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

::selection { background: var(--blue-deep); color: var(--on-accent); }

.label {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-60);
}

/* ---- masthead ---------------------------------------------- */

.masthead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  padding: 1.5rem var(--pad);
  border-bottom: 1px solid var(--line);
}

/* Optical weight compensation: display sizes take Regular, because
   Helvetica at 6rem already reads heavy. Anything under ~2rem takes Bold,
   because Regular goes limp against the serif body at small sizes. */
.masthead .mark {
  color: inherit;
  text-decoration: none;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
}

/* the mark: two squares overlapping in the working and done inks, the
   overlap in the "now" ink, the same three states the figure uses */
.masthead .mark .mark-logo { width: 1.15em; height: 1.15em; margin-right: 0.55rem; vertical-align: -0.22em; }
.mark-logo .m-work { fill: var(--blue); }
.mark-logo .m-done { fill: var(--saffron); }
.mark-logo .m-now  { fill: var(--red); }

.masthead nav { display: flex; gap: 1.5rem; }
.masthead nav a { color: var(--ink-60); text-decoration: none; }
.masthead nav a:hover { color: var(--blue); }
/* the theme switch reads like the nav links beside it; its label is the
   mode it would switch to, the same convention as the live site */
.masthead nav .mode-toggle,
.masthead nav .mute-toggle {
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--ink-60);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.masthead nav .mode-toggle:hover,
.masthead nav .mute-toggle:hover { color: var(--blue); }

.masthead nav a[aria-current="page"] { color: var(--ink); text-decoration: underline; text-decoration-color: var(--red); text-decoration-thickness: 2px; text-underline-offset: 0.45em; }

/* ---- hero -------------------------------------------------- */

.hero { padding: clamp(4rem, 12vw, 9rem) var(--pad) clamp(3rem, 7vw, 5rem); }

.hero .kicker { color: var(--red-text); }

.hero h1 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2.6rem, 8.5vw, 6.5rem);
  line-height: 0.96;
  letter-spacing: -0.035em;
  margin: 1.5rem 0 0;
  max-width: 15ch;
}

.hero .standfirst {
  margin: 2rem 0 0;
  max-width: var(--measure);
  font-size: 1.22rem;
  line-height: 1.55;
  color: var(--ink-60);
}

.hero .byline {
  margin-top: 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  max-width: var(--measure);
  display: flex;
  justify-content: space-between;
}

.hero .byline a { text-decoration: none; }
.hero .byline a:hover { color: var(--blue); }
.byline-links { display: inline-flex; gap: 1.5rem; }
/* the button takes its type from .label, like the date and the Source
   link beside it; only the button chrome is reset here */
.hero .byline .share {
  color: var(--ink-60);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.hero .byline .share:hover { color: var(--blue); }

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

.rail {
  position: fixed;
  right: clamp(0.75rem, 2.2vw, 1.75rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  align-items: flex-end;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}

.rail.is-visible { opacity: 1; pointer-events: auto; }

.rail button {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: var(--ink-30);
  transition: color 0.25s ease;
}

.rail button .tick {
  display: block;
  width: 16px;
  height: 1px;
  background: var(--ink-30);
  transition: width 0.28s cubic-bezier(0.22, 0.61, 0.36, 1), background 0.25s ease;
}

.rail button .n { opacity: 0; transition: opacity 0.25s ease; }
.rail button:hover .n { opacity: 1; }
.rail button:hover .tick { background: var(--ink-60); }

.rail button.is-active { color: var(--red-text); }
.rail button.is-active .tick { width: 34px; background: var(--red); height: 2px; }
.rail button.is-active .n { opacity: 1; }

/* ---- scrollytelling core ------------------------------------ */

.scrolly { padding: 0 var(--pad); border-top: 1px solid var(--line); }

.scrolly { margin-top: clamp(3rem, 6vw, 5rem); }

.scrolly-head { padding: clamp(3rem, 6vw, 5rem) 0 0; }

.scrolly-head h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.7rem, 3.4vw, 2.6rem);
  letter-spacing: -0.03em;
  line-height: 1.05;
  margin: 0.75rem 0 0;
  max-width: 18ch;
}

.scrolly-head p { max-width: var(--measure); margin: 1.4rem 0 0; }

.scrolly-inner {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
}

/* left: the steps */

.steps { padding: 12vh 0 22vh; }

.step {
  min-height: 82vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 32rem;
  opacity: 0.32;
  transition: opacity 0.45s ease;
}

.step.is-active { opacity: 1; }
.step.is-active .step-n { color: var(--red-text); }

.step .step-n {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-60);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.1rem;
}

.step .step-n::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.step h3 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.35rem, 2.6vw, 1.95rem);
  letter-spacing: -0.022em;
  line-height: 1.12;
  margin: 0 0 1rem;
}

.step p { margin: 0 0 1rem; }
.step p:last-child { margin-bottom: 0; }

.step code,
.prose code,
.scrolly-head code {
  font-family: var(--mono);
  font-size: 0.84em;
  background: var(--panel);
  padding: 0.08em 0.35em;
  border: 1px solid var(--line);
}

/* right: the sticky panel */

/* The panel must fit the viewport or its caption and readout fall off the
   bottom edge, which defeats a sticky figure entirely. So the shell is a
   flex column, the chrome keeps its natural height, and the SVG is the
   only thing allowed to flex - it scales down inside its own viewBox. */
.sticky-col {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  padding: 1.5rem 0;
}

.panel {
  width: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--ink);
  background: var(--paper);
}

.panel-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 0.95rem;
  border-bottom: 1px solid var(--ink);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.panel-bar .state { color: var(--ink-60); }

/* legend: the only place the three inks are named, so the figure reads
   without the text next to it */
.legend {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  padding: 0.6rem 0.95rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-60);
}
.legend span { display: inline-flex; align-items: center; gap: 0.45rem; }
.legend i { width: 0.75rem; height: 0.75rem; display: block; }
.legend .k-q    { background: var(--blue-tint); border: 1px solid var(--blue); }
.legend .k-live { background: var(--red); }
.legend .k-done { background: var(--saffron); }
.legend .k-plan { border: 1px solid var(--ink-30);
  background: repeating-linear-gradient(45deg, var(--paper) 0 2.5px, color-mix(in srgb, var(--ink) 34%, var(--paper)) 2.5px 4px); }
/* only the last step draws anything hatched, and on a phone the legend
   wraps to a second line, so it earns its space only there */
.legend .k-need { background: none; border: 1px dashed var(--red); }
.legend .k-need-item,
.legend .k-plan-item { display: none; }
.panel[data-step="2"] .k-need-item { display: inline-flex; }
.panel[data-step="4"] .k-plan-item { display: inline-flex; }

.panel-bar { flex: 0 0 auto; }

.panel figure {
  margin: 0;
  padding: 1.1rem 1.1rem 0.5rem;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.panel svg { display: block; flex: 1 1 auto; min-height: 0; width: 100%; height: 100%; }

.panel figcaption {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  margin: 0.9rem 0 0;
  padding: 0.8rem 0 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--ink-60);
  min-height: 4.2em;
}

.panel figcaption b { color: var(--ink); font-weight: 400; }

/* the readout under the diagram */
.readout {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--ink);
}

.readout div {
  padding: 0.65rem 0.95rem 0.8rem;
  border-right: 1px solid var(--line);
}

.readout div:last-child { border-right: 0; }

.readout dt {
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin-bottom: 0.25rem;
}

.readout dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
}

/* diagram tile states. Raw matrix stays in ink; the three inks mark
   working (--blue), live (--red) and done (--saffron). */
.tile rect { transition: fill 0.32s ease, stroke 0.32s ease, stroke-opacity 0.32s ease; }
.tile text { transition: fill-opacity 0.32s ease; }

.tile .cell     { fill: var(--paper); stroke: var(--ink); stroke-width: 1; stroke-opacity: 0.28; }
.tile .cellText { fill: var(--ink); fill-opacity: 0; font-family: "JetBrains Mono", monospace; font-size: 9px; }

.tile.is-materialised .cell { fill: url(#hatch); stroke-opacity: 0.55; }
.tile.is-materialised .cellText { fill: var(--ink); fill-opacity: 0.45; }
/* needed at the same time, with no room for it: outlined in the live
   ink rather than faded, so step 2 shows four blocks wanted and one box
   of space, not one block and three near-invisible ones */
.tile.is-needed      .cell { fill: none; stroke: var(--red); stroke-opacity: 0.85; stroke-dasharray: 4 3; }
.tile.is-queued      .cell { fill: var(--blue-tint); stroke: var(--blue); stroke-opacity: 1; }
.tile.is-done        .cell { fill: var(--saffron); stroke: var(--ink); stroke-opacity: 0.6; }
.tile.is-active      .cell { fill: var(--red); stroke: var(--red); stroke-opacity: 1; }
.tile.is-active .cellText { fill: var(--on-accent); fill-opacity: 1; }
.tile.is-done   .cellText { fill: var(--on-saffron); fill-opacity: 0.6; }

/* The SVG was drawn with literal light-theme colours in its attributes.
   CSS outranks presentation attributes, so these carry it into dark. */
.panel svg g[fill="#6d6c68"] { fill: var(--ink-60); }
.panel svg rect[stroke="#0d0d0c"],
.panel svg line[stroke="#0d0d0c"],
.panel svg path[stroke="#0d0d0c"] { stroke: var(--ink); }
#trafficBar { fill: var(--ink); }
#trafficBar, #trafficOver { transition: width 0.32s ease; }
#trafficOver { fill: var(--red); }
#hatch rect { fill: var(--paper); }
#hatch line { stroke: var(--ink); }

/* SVG accents take their ink from the same variables */
#sramBox rect  { stroke: var(--red); }
#sramLabel     { fill: var(--red-text); }
#sramLabel text { paint-order: stroke; stroke: var(--paper); stroke-width: 3px; stroke-linejoin: round; }
#sramLabel line { stroke: var(--red); }
#qBracket path { stroke: var(--blue); }
#outCol rect:first-of-type { fill: var(--saffron); }
/* by the last step every row has finished, so every output block is done */
.panel[data-step="4"] #outCol rect { fill: url(#hatch); stroke-opacity: 1; transition: fill 0.32s ease; }

#sramBox, #sramLabel { opacity: 0; transition: opacity 0.35s ease; }
.panel[data-step="2"] #sramBox, .panel[data-step="2"] #sramLabel { opacity: 1; }

#outCol { opacity: 0; transition: opacity 0.35s ease; }
.panel[data-step="3"] #outCol, .panel[data-step="4"] #outCol { opacity: 1; }

#qBracket { opacity: 0; transition: opacity 0.35s ease; }
.panel[data-step="1"] #qBracket,
.panel[data-step="2"] #qBracket,
.panel[data-step="3"] #qBracket { opacity: 1; }

/* ---- article prose ------------------------------------------- */

.prose { padding: 0 var(--pad); }
.prose > * { max-width: var(--measure); }
.prose > p, .prose > ul { margin: 0 0 1.2rem; }
.prose ul { padding-left: 1.2rem; }
.prose li { margin-bottom: 0.5rem; }

/* Section headings break out to the wide measure, the left edge the code
   blocks and figures share, so they hang past the text and read as the
   start of a section rather than a large paragraph. The numbered label
   and hairline echo the Triton figure's step headings. The count runs
   across the article halves either side of the pull quote and on
   through a scroll figure's heading. */
body { counter-reset: section; }
.prose h2, .scrolly-head h2 { counter-increment: section; }
.prose h2::before,
.scrolly-head h2::before {
  content: counter(section, decimal-leading-zero);
  display: block;
  margin-bottom: 0.9rem;
  font: 0.66rem/1 var(--mono);
  letter-spacing: 0.18em;
  color: var(--red-text);
}

.prose h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 3.1rem);
  letter-spacing: -0.032em;
  line-height: 1.04;
  margin: 5rem 0 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
  max-width: 48rem;
  text-wrap: balance;
}

.prose a, .step a { color: inherit; text-decoration-color: var(--ink-30); text-underline-offset: 0.2em; }
.prose a:hover, .step a:hover { color: var(--blue); text-decoration-color: currentColor; }

/* code can run wider than the text measure, and scrolls on its own */
.prose > pre {
  max-width: 48rem;
  margin: 1.6rem 0;
  padding: 1rem 1.2rem;
  overflow-x: auto;
  background: var(--panel);
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
}
.prose pre code { background: none; border: 0; padding: 0; font-size: inherit; }
/* long identifiers (torch.nn.functional.scaled_dot_product_attention)
   would otherwise push a phone-width page sideways */
.prose :not(pre) > code, .step code { overflow-wrap: anywhere; }

.prose-after { padding-bottom: clamp(3rem, 7vw, 5rem); }

/* glossary tooltips, same markup as the live site */
.term { position: relative; border-bottom: 1px dotted var(--ink-60); cursor: help; }
.term:hover, .term:focus { border-bottom-color: var(--ink); outline: none; }
.term-preview {
  position: absolute;
  left: 50%;
  bottom: 100%;
  width: 19rem;
  max-width: min(19rem, 80vw);
  margin-bottom: 0.6rem;
  padding: 0.8rem 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  font: 0.78rem/1.55 var(--mono);
  color: var(--ink-60);
  text-align: left;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 4px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
  pointer-events: none;
  z-index: 20;
}
.term-preview code { font-size: 1em; }
.term:hover .term-preview, .term:focus .term-preview {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

/* BLOCK_SIZE demo, ported from the post */
.blocksize-demo { margin: 2.5rem 0 3rem; max-width: 48rem !important; }
.blocksize-head { display: flex; flex-wrap: wrap; gap: 0.85rem 1.25rem; align-items: baseline; justify-content: space-between; margin-bottom: 1.1rem; }
.blocksize-n { font: 0.7rem var(--mono); color: var(--ink-60); }
.blocksize-n output { min-width: 3.4em; display: inline-block; color: var(--ink); font-variant-numeric: tabular-nums; }
.blocksize-n input[type="range"] { width: 9rem; accent-color: var(--blue); cursor: pointer; vertical-align: middle; }
.blocksize-switch { display: flex; }
.blocksize-switch button {
  font: 0.7rem var(--mono);
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--ink-60);
  border: 1px solid var(--line);
  cursor: pointer;
}
.blocksize-switch button + button { border-left: 0; }
.blocksize-switch button:hover { color: var(--ink); border-color: var(--ink); }
.blocksize-switch button[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.blocksize-readout { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin: 0 0 0.85rem; font: 0.7rem var(--mono); color: var(--ink-60); }
.blocksize-readout b { font-weight: 400; color: var(--ink); font-variant-numeric: tabular-nums; }
.blocksize-readout b.is-broken { color: var(--red-text); }
.blocksize-grid { display: block; width: 100%; height: auto; aspect-ratio: 852 / 140; border: 1px solid var(--line); }
.blocksize-demo figcaption { margin-top: 1rem; font: 0.72rem/1.55 var(--mono); color: var(--ink-60); }

/* ---- pull quote --------------------------------------------- */

/* the one full-bleed block of colour on the page */
.pull {
  padding: clamp(6rem, 18vh, 12rem) var(--pad);
  margin-top: clamp(3rem, 8vw, 7rem);
  overflow: hidden;
  background: var(--blue-deep);
  color: var(--on-accent);
}

.pull ::selection { background: var(--saffron); color: var(--on-saffron); }
.pull .label { color: color-mix(in srgb, var(--on-accent) 72%, var(--blue-deep)); }

.pull blockquote {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2rem, 7.4vw, 5.6rem);
  line-height: 1.0;
  letter-spacing: -0.035em;
  max-width: 20ch;
}

/* emphasis is the pale "done" ink alone; adding Bold on top at this size
   reads as shouting */
.pull blockquote em { font-style: normal; color: var(--saffron); }

.pull .attrib { margin-top: 2rem; display: flex; gap: 1rem; }

/* ---- mosaic -------------------------------------------------- */

.mosaic-wrap { padding: clamp(4rem, 9vw, 7rem) var(--pad) clamp(5rem, 10vw, 9rem); border-top: 1px solid var(--line); }

.mosaic-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2.5rem; }

.mosaic-head a, .foot a { color: inherit; text-decoration: none; }
.mosaic-head a:hover, .foot a:hover { color: var(--blue); }

.mosaic-head h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 2.2rem);
  letter-spacing: -0.03em;
  margin: 0;
}

.mosaic {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}

.tile-post {
  background: var(--paper);
  padding: 1.5rem 1.4rem;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  min-height: 13rem;
  transition: background 0.25s ease;
}

.tile-post:hover { background: var(--blue-deep); color: var(--on-accent); }
.tile-post:hover p,
.tile-post:hover .label { color: color-mix(in srgb, var(--on-accent) 72%, var(--blue-deep)); }

/* 4 + 2 / 2 + 4, repeating, so the same grid holds four entries or eight */
.tile-post:nth-child(4n+1) { grid-column: span 4; min-height: 20rem; }
.tile-post:nth-child(4n+2) { grid-column: span 2; }
.tile-post:nth-child(4n+3) { grid-column: span 2; }
.tile-post:nth-child(4n+4) { grid-column: span 4; }

/* Every tile title is the same Regular, tightly tracked Helvetica. Size
   follows the tile: the wide tiles take the full display size, the
   narrow ones a step down so a long title doesn't run to four lines. */
.tile-post h3 {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin: 0.9rem 0 0;
  font-size: clamp(1.4rem, 2.1vw, 1.8rem);
}

.tile-post:nth-child(4n+1) h3,
.tile-post:nth-child(4n+4) h3 {
  font-size: clamp(1.6rem, 3.2vw, 2.4rem);
  max-width: 16ch;
}

.tile-post p { margin: 0.7rem 0 0; font-size: 0.94rem; color: var(--ink-60); max-width: 44ch; }
.tile-post .meta { margin-top: auto; padding-top: 1.5rem; }

/* ---- index: the log hero ---------------------------------------- */
/* One entry shown large, and under it a strip with a cell per day from
   the first entry to today. The "done" ink is a day something was written,
   the "now" ink the entry on show: the same states as the tiles
   in the Triton figure. The entry block's height is reserved in
   design.js for the tallest title, so the strip never moves under the
   pointer while you're reading along it. */

.log-hero { padding-top: clamp(2rem, 5vw, 3.5rem); }

.lh-ident {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}

.lh-entry { margin-top: clamp(2.5rem, 6vw, 4.5rem); }
.lh-entry > * { transition: opacity 0.16s ease, transform 0.22s cubic-bezier(0.22, 0.61, 0.36, 1); }
.lh-entry.is-swapping > * { opacity: 0; transform: translateY(6px); }

.log-hero h1 { font-size: clamp(2.4rem, 6.2vw, 5.4rem); max-width: 17ch; }
.log-hero h1 a { color: inherit; text-decoration: none; }
.log-hero h1 a:hover { color: var(--blue); }

.lh-read { display: inline-block; margin-top: 1.4rem; color: var(--ink); text-decoration: none; }
.lh-read:hover { color: var(--blue); }

.lh-strip { margin-top: clamp(2.5rem, 5vw, 4rem); }

.lh-scroll {
  overflow-x: auto;
  padding-bottom: 14px;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-30) transparent;
}

.lh-months,
.lh-cells {
  display: grid;
  grid-template-columns: repeat(var(--days), minmax(10px, 1fr));
  gap: 2px;
}

.lh-month {
  font: 0.6rem var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-60);
  border-left: 1px solid var(--ink-30);
  padding: 0 0 0.5rem 0.4rem;
  white-space: nowrap;
}

/* Scoped to the strip: the Triton figure's score tiles are also .cell, and
   height is a real geometry property on an SVG rect, so an unscoped rule
   here halved every tile in that figure. */
.lh-cells .cell {
  display: block;
  position: relative;
  height: 30px;
  border: 1px solid var(--line);
}
.lh-cells .cell.is-entry { background: var(--saffron); border-color: var(--saffron); }
.lh-cells .cell.is-today { border-color: var(--ink); }
.lh-cells .cell.is-current { background: var(--red); border-color: var(--red); }
.lh-cells .cell.is-current::after {
  content: "";
  position: absolute;
  left: 50%;
  top: calc(100% + 4px);
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top: 0;
  border-bottom-color: var(--red);
}
.lh-cells .cell:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

.lh-ends { display: flex; justify-content: space-between; margin-top: 0.5rem; }

.lh-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem 3rem;
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.lh-foot p { margin: 0; max-width: var(--measure); color: var(--ink-60); font-size: 0.98rem; }
.lh-foot a { color: inherit; text-decoration: none; }
.lh-foot a:hover { color: var(--blue); }

@media (prefers-reduced-motion: reduce) {
  .lh-entry > * { transition: none; }
}

/* ---- projects --------------------------------------------------- */
/* One ruled row per project: the name is the link to the code, the
   write-up (where there is one) is marked in the "done" ink, the one for
   "finished" everywhere else on the site. */

.projects { padding: 0 var(--pad) clamp(4rem, 8vw, 7rem); }

.project-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--ink); }

.project {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 8rem;
  gap: 0.8rem 3rem;
  align-items: baseline;
  padding: 1.9rem 0 2rem;
  border-bottom: 1px solid var(--line);
}

.project-name {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.5rem, 2.6vw, 2.2rem);
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: inherit;
  text-decoration: none;
  overflow-wrap: anywhere;
  transition: color 0.2s ease;
}
.project-name span { color: var(--ink-30); transition: color 0.2s ease; }
.project-name:hover, .project-name:hover span { color: var(--blue); }

.project-blurb { margin: 0; max-width: 38rem; }

.project-entry {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 1rem;
  color: var(--ink);
  text-decoration: none;
}
.project-entry::before { content: ""; width: 0.6rem; height: 0.6rem; background: var(--saffron); }
.project-entry:hover { color: var(--blue); }

.project-stack { text-align: right; }

.project-more { margin: 2rem 0 0; color: var(--ink-60); }
.project-more a { color: var(--ink); text-underline-offset: 0.2em; text-decoration-color: var(--ink-30); }
.project-more a:hover { color: var(--blue); }

@media (max-width: 800px) {
  .project { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
  .project-stack { text-align: left; order: -1; }
}

/* ---- about ------------------------------------------------------ */

/* no blue band on this page (that belongs to the articles); the first
   paragraph just reads a size up, as the way in */
.prose .lead { font-size: 1.22rem; line-height: 1.55; }

/* ---- archive ---------------------------------------------------- */
/* The index's answer to "what happens at 100 posts": the mosaic stays a
   fixed shelf of the latest four, and everything lives here as one row
   per post, grouped by year, filterable by any topic that recurs. */

.archive { padding: 0 var(--pad) clamp(5rem, 10vw, 9rem); }

.archive-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem 2rem;
  padding-top: clamp(3rem, 6vw, 5rem);
  border-top: 1px solid var(--line);
}

.archive-head h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 2.2rem);
  letter-spacing: -0.03em;
  margin: 0;
}

.filters { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.filters button {
  font: 0.66rem var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.45rem 0.75rem;
  background: none;
  color: var(--ink-60);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.filters button span { color: var(--ink-30); margin-left: 0.35rem; }
.filters button:hover { color: var(--ink); border-color: var(--ink); }
.filters button[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.filters button[aria-pressed="true"] span { color: var(--ink-30); }

.year {
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  gap: 0 2rem;
  margin-top: 2.5rem;
}
.year[hidden] { display: none; }

.year-label {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  letter-spacing: -0.03em;
  margin: 0.35rem 0 0;
  color: var(--ink-30);
}

.rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--ink); }
.rows li { border-bottom: 1px solid var(--line); }
.rows li[hidden] { display: none; }

.rows a {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) auto;
  gap: 0 1.5rem;
  align-items: baseline;
  padding: 0.95rem 0;
  color: inherit;
  text-decoration: none;
  transition: color 0.2s ease, padding 0.25s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.rows .d { font: 0.72rem var(--mono); color: var(--ink-60); font-variant-numeric: tabular-nums; }
.rows .t {
  font-family: var(--display);
  font-size: clamp(1.1rem, 1.7vw, 1.35rem);
  letter-spacing: -0.02em;
  line-height: 1.2;
}
.rows .tags { text-align: right; }

.rows a:hover { color: var(--blue); padding-left: 0.6rem; }
.rows a:hover .d, .rows a:hover .tags { color: var(--blue); }

.archive-empty { font: 0.8rem var(--mono); color: var(--ink-60); margin-top: 2rem; }

@media (max-width: 700px) {
  .year { grid-template-columns: minmax(0, 1fr); }
  .year-label { margin: 0 0 0.75rem; }
  .rows a { grid-template-columns: 3.4rem minmax(0, 1fr); }
  .rows .tags { grid-column: 2; text-align: left; margin-top: 0.3rem; }
}

/* ---- generated post pages ------------------------------------- */
/* Everything below is only exercised by the other seven posts: plain
   figures, blockquotes, tables, MathML and sub-headings that the
   Triton post doesn't have. */

.prose > figure,
.prose > .colormap-carousel { max-width: 48rem; margin: 2.5rem 0; }

.prose figure img { display: block; max-width: 100%; height: auto; filter: grayscale(1); }
/* the live site's rule: images are grey unless the post marks them .color */
.prose figure.color img { filter: none; }
/* the charts are white-backed PNGs; in the dark theme take the glare off */
:root[data-theme="dark"] .prose figure img { filter: grayscale(1) brightness(0.86); }
:root[data-theme="dark"] .prose figure.color img { filter: brightness(0.9); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .prose figure img { filter: grayscale(1) brightness(0.86); }
  :root:not([data-theme="light"]) .prose figure.color img { filter: brightness(0.9); }
}

/* the end-of-article mark the live posts carry */
.prose.fin::after {
  content: "∎";
  display: block;
  margin-top: 2.5rem;
  font: 0.9rem var(--mono);
  color: var(--ink-60);
}

.prose figure > figcaption,
.prose .grok-demo figcaption,
.prose .chaos-demo figcaption,
.prose .ray-demo figcaption,
.prose .birthday-demo figcaption,
.prose .birthday-scale figcaption,
.prose .structure-demo figcaption {
  margin-top: 0.9rem;
  font: 0.72rem/1.55 var(--mono);
  color: var(--ink-60);
  text-align: left;
}

.prose h3 {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.2rem;
  letter-spacing: -0.02em;
  margin: 2.4rem 0 0.7rem;
}

.prose blockquote {
  margin: 1.8rem 0;
  padding-left: 1.2rem;
  border-left: 2px solid var(--blue);
  color: var(--ink-60);
}
.prose blockquote p { margin: 0 0 0.6rem; }
.prose blockquote p:last-child { margin-bottom: 0; }

.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }
.prose ol { padding-left: 1.4rem; margin: 0 0 1.2rem; }
.prose sup { line-height: 0; }
.prose pre .cm { color: var(--ink-60); }

.prose .math-scroll {
  margin: 1.75rem 0;
  padding: 1rem 1.25rem;
  background: var(--panel);
  border: 1px solid var(--line);
  overflow-x: auto;
}
.prose .math-scroll math { font-size: 1.15em; }

@media (max-width: 600px) {
  /* a hover card anchored to its word runs off the side of a phone
     (and, even hidden, widened the page); pin it to the bottom instead */
  .term-preview {
    position: fixed;
    left: 50%;
    bottom: 1rem;
    width: calc(100vw - 2rem);
    max-width: 26rem;
    margin: 0;
  }
  /* the numeric tables scroll inside themselves rather than the page */
  .prose > table { display: block; overflow-x: auto; }
}

.prose .sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* the sound control, only on posts whose widget can make a noise */
.masthead nav .sound-toggle {
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--ink-60);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.masthead nav .sound-toggle:hover { color: var(--blue); }

/* ---- footer -------------------------------------------------- */

.foot {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 2rem var(--pad) 3rem;
  border-top: 1px solid var(--line);
}

/* ---- responsive ---------------------------------------------- */

@media (max-width: 900px) {
  .scrolly-inner { grid-template-columns: minmax(0, 1fr); }
  /* On narrow screens the panel pins to the top and the text scrolls
     through the half of the screen below it. So the panel is capped at
     half the height: the caption goes (the step text beside it says the
     same thing), the chrome tightens, and the SVG shrinks to fit. The
     script moves its reading line into that lower half to match. */
  .sticky-col { order: -1; height: auto; top: 0; padding: 0.5rem 0; background: var(--paper); z-index: 5; }
  .panel { max-height: 50vh; }
  .panel figure { padding: 0.5rem 0.8rem 0.3rem; }
  .panel figcaption { display: none; }
  .panel-bar { padding: 0.55rem 0.8rem; }
  .legend { padding: 0.4rem 0.8rem; font-size: 0.55rem; letter-spacing: 0.06em; gap: 0.3rem 0.9rem; }
  .readout div { padding: 0.4rem 0.8rem 0.5rem; }
  .readout dt { font-size: 0.5rem; letter-spacing: 0.06em; white-space: nowrap; }
  .readout dd { font-size: 0.88rem; }
  .step { min-height: 60vh; font-size: 16px; }
  .rail { display: none; }
  .mosaic { grid-template-columns: 1fr; }
  .tile-post:nth-child(n) { grid-column: span 1; min-height: 11rem; }
}

@media (max-width: 420px) {
  .masthead { gap: 1rem; }
  .masthead nav { gap: 0.9rem; letter-spacing: 0.1em; }
  .readout dt { white-space: normal; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  .step { opacity: 1; }
}

/* text for screen readers and agents only, like the index's site summary */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Articles read down the middle of the page. Each direct child of an
   article centres in its own width: the text at its 34rem measure, and
   the wider code blocks, figures and widgets spread evenly either side
   of it rather than only to the right. The selector outranks the
   widgets' own `article.post .grok-demo { margin: x 0 }` rules, so none
   of them pulls a figure back to the left; only the side margins are
   set, so vertical spacing is untouched. Phones are narrower than the
   measure, so nothing changes there. */
body article.prose.post > *,
body article.prose.post.fin::after { margin-left: auto; margin-right: auto; }
.prose.post.fin::after { max-width: var(--measure); }
