/* Klaro
   Tokens mirror the product design system (klaro-frontend/docs/design/tokens.css).
   Colours are space-separated RGB channels so rgb(var(--x) / a) works everywhere. */

/* ---------- faces ---------- */
/* Both are served from this origin, and preloaded in the head of every page,
   because the site is a set of pages now rather than one scroll. A stylesheet
   from fonts.googleapis.com blocks the first paint of every document, and the
   document being blocked here is the one arriving under the transition panel:
   the panel cannot paint, so it hangs at full cover until the round trip comes
   back, and what should be one continuous movement reads as two halves with a
   stall between them. Same origin, already in cache after the first page, no
   round trip, no stall.

   One variable file per family, latin, covering the two weights the site uses.
   Both are SIL OFL 1.1. No unicode-range: the file is wanted on every page, and
   a glyph it does not carry falls back on its own. */

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('/fonts/fraunces-latin.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('/fonts/inter-latin.woff2') format('woff2');
}

:root {
  color-scheme: light;

  --kl-ink: 34 31 26;
  --kl-ink-soft: 61 57 47;
  --kl-gold: 227 178 60;
  /* the tone gold has to take to be read as text on paper; the bright gold
     above is for ink grounds and for graphic marks */
  --kl-gold-deep: 166 119 20;
  --kl-gold-soft: 241 216 138;
  --kl-card: 251 248 242;
  --kl-page: 239 234 224;
  --kl-surface: 231 224 213;
  --kl-border: 216 207 190;
  --kl-muted: 119 112 99;

  --kl-success: 77 118 56;
  --kl-success-soft: 234 243 229;
  --kl-success-line: 197 222 188;
  --kl-info: 62 107 140;
  --kl-info-soft: 232 239 244;
  --kl-info-line: 194 211 223;
  --kl-warning: 155 97 28;
  --kl-warning-soft: 254 243 224;
  --kl-warning-line: 240 208 144;

  /* The framing panels: the bar, the hero, the demo, the closing band, the
     footer, the transition veil. They used to be one dark tone against the
     paper bands; they are paper themselves now, one step brighter than the
     bands so the page still alternates. The token names are unchanged, so
     every rule that dressed a dark panel dresses a bright one instead. */
  --kl-night: 248 245 237;
  --kl-night-soft: 241 236 226;
  --kl-night-line: 219 211 195;
  --kl-night-fg: 34 31 26;

  /* the light half of an ink chip: the primary button, the skip link */
  --kl-onink: 244 240 232;

  /* a video frame is dark whatever the page around it is doing, and so is the
     transition curtain: it is a thing drawn over the page rather than part of
     it, and it reads as a curtain only by being darker than what it covers */
  --kl-video: 26 24 20;
  --kl-veil: 26 24 20;
  --kl-veil-fg: 244 240 232;

  /* The chrome: the bar at the top and the footer at the bottom. These stay
     dark, because they are the frame rather than the page, and the page needs
     an edge to start and stop against. Everything between them is paper. */
  --kl-bar: 26 24 20;
  --kl-bar-fg: 244 240 232;
  --kl-bar-line: 62 57 47;

  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 20px;

  --shell: 1160px;
  --gut: 24px;

  /* the sticky bar's own height, so anything that has to clear it (an anchor
     landing, a sheet hanging off the bottom of it) is measured from one place */
  --bar: 64px;
}

/* ---------- reset ---------- */

*,
*::before,
*::after {
  box-sizing: border-box;
  border: 0 solid rgb(var(--kl-border));
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* every anchor on this site lands under a sticky bar otherwise, and on a
     phone the bar is most of what you can see */
  scroll-padding-top: calc(var(--bar) + 12px);
}

/* The grey flash a phone paints over anything tappable, gone. Every control
   here answers a touch with its own :active state instead, which is the whole
   point of having one. */
a, button, summary, .para, [role='button'] { -webkit-tap-highlight-color: transparent; }

/* the demo panels are display:grid, which beats the UA [hidden] rule */
[hidden] { display: none !important; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: rgb(var(--kl-page));
  color: rgb(var(--kl-ink));
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.7;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; cursor: pointer; }
a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0;
  font-optical-sizing: auto;
}

p { margin: 0; }

:focus-visible {
  outline: 2px solid rgb(var(--kl-gold-deep));
  outline-offset: 2px;
  border-radius: 4px;
}

.night :focus-visible { outline-color: rgb(var(--kl-gold-deep)); }

::selection {
  background: rgb(var(--kl-gold-soft));
  color: rgb(var(--kl-ink));
}

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

.skip-link {
  position: absolute;
  left: 16px;
  top: -60px;
  z-index: 100;
  padding: 10px 16px;
  background: rgb(var(--kl-ink));
  color: rgb(var(--kl-onink));
  border-radius: var(--r-sm);
  transition: top 0.15s ease;
}
.skip-link:focus { top: 16px; }

/* ---------- layout ---------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  /* the notch and the rounded corners of a phone held sideways are added to
     the gutter rather than fought with, so nothing ever sits under them */
  padding-inline:
    calc(var(--gut) + env(safe-area-inset-left))
    calc(var(--gut) + env(safe-area-inset-right));
}

.shell-narrow { max-width: 760px; }

section { position: relative; }

.band {
  padding-block: clamp(72px, 10vw, 128px);
}

.band-tight { padding-block: clamp(56px, 7vw, 88px); }

.night {
  background: rgb(var(--kl-night));
  color: rgb(var(--kl-night-fg));
}

.night h1, .night h2, .night h3 { color: rgb(var(--kl-night-fg)); }

.rule {
  height: 1px;
  background: rgb(var(--kl-border));
  border: 0;
  margin: 0;
}

/* ---------- typography helpers ---------- */

.eyebrow {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--kl-muted));
  display: flex;
  align-items: center;
  gap: 9px;
}

.eyebrow::before {
  content: '';
  width: 22px;
  height: 1px;
  background: rgb(var(--kl-gold-deep));
}

.night .eyebrow { color: rgb(var(--kl-gold-deep)); }
.night .eyebrow::before { background: rgb(var(--kl-gold-deep)); }

.h-xl { font-size: clamp(40px, 6.2vw, 68px); line-height: 1.04; letter-spacing: -0.03em; }
.h-lg { font-size: clamp(30px, 4.2vw, 46px); letter-spacing: -0.025em; }
.h-md { font-size: clamp(22px, 2.6vw, 30px); letter-spacing: -0.02em; }
.h-sm { font-size: 19px; }

.lede {
  font-size: clamp(16px, 1.5vw, 19px);
  line-height: 1.65;
  color: rgb(var(--kl-ink-soft));
  max-width: 60ch;
}

.night .lede { color: rgb(var(--kl-night-fg) / 0.72); }

.muted { color: rgb(var(--kl-muted)); }
.small { font-size: 13px; line-height: 1.6; }
.gold { color: rgb(var(--kl-gold-deep)); }
.night .gold { color: rgb(var(--kl-gold-deep)); }

.section-head { display: grid; gap: 16px; max-width: 62ch; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 20px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 0.16s ease, color 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
  background: rgb(var(--kl-ink));
  color: rgb(var(--kl-onink));
}
.btn-primary:hover { background: rgb(var(--kl-ink-soft)); }

.night .btn-primary {
  background: rgb(var(--kl-ink));
  color: rgb(var(--kl-onink));
}
.night .btn-primary:hover { background: rgb(var(--kl-ink-soft)); }

.btn-ghost {
  border: 1px solid rgb(var(--kl-border));
  color: rgb(var(--kl-ink-soft));
  background: rgb(var(--kl-card) / 0.6);
}
.btn-ghost:hover { border-color: rgb(var(--kl-ink) / 0.35); background: rgb(var(--kl-card)); }

.night .btn-ghost {
  border-color: rgb(var(--kl-night-line));
  color: rgb(var(--kl-ink-soft));
  background: transparent;
}
.night .btn-ghost:hover { border-color: rgb(var(--kl-ink) / 0.35); background: rgb(var(--kl-night-fg) / 0.04); }

.btn-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

/* ---------- wordmark ---------- */

.wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
  text-decoration: none;
  user-select: none;
}

.wordmark svg { fill: rgb(var(--kl-gold-deep)); align-self: flex-start; }

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

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgb(var(--kl-bar));
  color: rgb(var(--kl-bar-fg));
  border-bottom: 1px solid rgb(var(--kl-bar-line));
  transition: background-color 0.25s ease, border-color 0.25s ease;
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 64px;
}

.nav .wordmark { font-size: 21px; color: rgb(var(--kl-bar-fg)); }

/* gold-deep is the tone that holds against paper; on the dark chrome the
   brighter gold is the one that reads */
.nav .wordmark svg,
.footer .wordmark svg { fill: rgb(var(--kl-gold)); }

/* the ink pill the rest of the site uses would disappear into the bar */
.nav .btn-primary { background: rgb(var(--kl-gold)); color: rgb(var(--kl-ink)); }
.nav .btn-primary:hover { background: rgb(var(--kl-gold-soft)); }
.nav :focus-visible { outline-color: rgb(var(--kl-gold)); }

.nav-links {
  display: flex;
  align-items: center;
  gap: 28px;
  font-size: 14px;
}

.nav-links a {
  text-decoration: none;
  color: rgb(var(--kl-bar-fg) / 0.72);
  transition: color 0.15s ease;
}
.nav-links a:hover { color: rgb(var(--kl-bar-fg)); }

@media (max-width: 860px) {
  .nav-links { display: none; }
}

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

.hero {
  background: rgb(var(--kl-night));
  color: rgb(var(--kl-night-fg));
  overflow: hidden;
  position: relative;
  padding-block: clamp(64px, 7.5vw, 96px) 0;
}

.hero-glow {
  position: absolute;
  inset: -20% -10% auto -10%;
  height: 620px;
  background: radial-gradient(56% 50% at 50% 10%, rgb(var(--kl-gold) / 0.14), transparent 70%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 22px;
  text-align: center;
}

.hero h1 { max-width: 15ch; }
.hero .lede { max-width: 48ch; }

/* the eyebrow rule is one-sided by default: mirror it when centred */
.hero .eyebrow::after {
  content: '';
  width: 22px;
  height: 1px;
  background: rgb(var(--kl-gold-deep));
}

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 20px;
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.66);
}

.hero-meta span { display: inline-flex; align-items: center; gap: 7px; }
.hero-meta span::before {
  content: '';
  width: 4px; height: 4px;
  border-radius: 999px;
  background: rgb(var(--kl-gold-deep));
}

/* hero stage: the machine */

.stage-wrap {
  position: relative;
  margin-top: clamp(32px, 4.5vw, 52px);
  padding-bottom: clamp(48px, 7vw, 80px);
}

.stage {
  width: 100%;
  height: auto;
  overflow: visible;
}

.stage-caption {
  margin-top: 20px;
  text-align: center;
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.62);
}

/* hero stage animation */

.beam {
  stroke-dasharray: 90 900;
  stroke-dashoffset: 990;
  animation: travel 3.6s linear infinite;
  animation-delay: var(--d, 0s);
}

@keyframes travel {
  to { stroke-dashoffset: 0; }
}

.spin { animation: turn 18s linear infinite; }

@keyframes turn {
  to { transform: rotate(360deg); }
}

.src { animation: breathe 3.6s ease-in-out infinite; animation-delay: var(--d, 0s); }

@keyframes breathe {
  0%, 100% { opacity: 0.82; }
  12% { opacity: 1; }
}

.src:nth-of-type(2) { animation-delay: 0.9s; }
.src:nth-of-type(3) { animation-delay: 1.8s; }

.outmap-nodes circle {
  opacity: 0;
  animation: pop 3.6s ease-out infinite;
  animation-delay: var(--d, 0s);
  transform-box: fill-box;
  transform-origin: center;
}

@keyframes pop {
  0% { opacity: 0; transform: scale(0.4); }
  10% { opacity: 1; transform: scale(1.18); }
  16%, 92% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}

.outmap-edges path {
  stroke-dasharray: 120;
  stroke-dashoffset: 120;
  animation: draw 3.6s ease-out infinite;
  animation-delay: 2.6s;
}

@keyframes draw {
  0% { stroke-dashoffset: 120; }
  22%, 92% { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .beam, .spin, .src, .outmap-nodes circle, .outmap-edges path { animation: none; }
  .outmap-nodes circle { opacity: 1; }
  .outmap-edges path { stroke-dashoffset: 0; }
  .beam { opacity: 0; }
}

/* ---------- input cards (problem section) ---------- */

.trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin-top: 44px;
}

@media (max-width: 800px) { .trio { grid-template-columns: 1fr; } }

.paper {
  background: rgb(var(--kl-card));
  border: 1px solid rgb(var(--kl-border));
  border-radius: var(--r-lg);
  padding: 22px;
  display: grid;
  gap: 12px;
  align-content: start;
  position: relative;
  overflow: hidden;
}

.paper-kind {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--kl-muted));
}

.paper h3 { font-size: 21px; }

.paper p { color: rgb(var(--kl-ink-soft)); font-size: 14px; }

.paper-lines { display: grid; gap: 6px; margin-top: 4px; }
.paper-lines i {
  display: block;
  height: 6px;
  border-radius: 999px;
  background: rgb(var(--kl-surface));
}
.paper-lines i:nth-child(2) { width: 88%; }
.paper-lines i:nth-child(3) { width: 62%; }

/* ---------- steps ---------- */

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  margin-top: 48px;
  background: rgb(var(--kl-border));
  border: 1px solid rgb(var(--kl-border));
  border-radius: var(--r-lg);
  overflow: hidden;
}

@media (max-width: 800px) { .steps { grid-template-columns: 1fr; } }

.step {
  background: rgb(var(--kl-card));
  padding: 26px 24px;
  display: grid;
  gap: 10px;
  align-content: start;
}

.step-n {
  font-family: var(--font-display);
  font-size: 13px;
  color: rgb(var(--kl-gold-deep));
  letter-spacing: 0.08em;
}

.step h3 { font-size: 19px; }
.step p { font-size: 14px; color: rgb(var(--kl-ink-soft)); }

/* ---------- demo ---------- */

.demo {
  background: rgb(var(--kl-night));
  color: rgb(var(--kl-night-fg));
}

.demo-shell { max-width: 1500px; }

.demo-frame {
  margin-top: 40px;
  border: 1px solid rgb(var(--kl-night-line));
  border-radius: var(--r-xl);
  background: rgb(var(--kl-night-soft));
  overflow: hidden;
  box-shadow: 0 24px 60px -30px rgb(34 31 26 / 0.28);
}

.demo-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  border-bottom: 1px solid rgb(var(--kl-night-line));
  background: rgb(var(--kl-night));
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.6);
  flex-wrap: wrap;
}

.demo-dot { width: 9px; height: 9px; border-radius: 999px; background: rgb(var(--kl-night-line)); }

.demo-doc {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: rgb(var(--kl-night-fg) / 0.85);
}

.demo-doc strong { font-weight: 500; }

.demo-hint {
  margin-left: auto;
  font-size: 12px;
  color: rgb(var(--kl-night-fg) / 0.6);
}

/* the frame is given one height and the panes inside fill it and scroll on
   their own, rather than each pane guessing at a max-height of its own */
.demo-body {
  display: grid;
  grid-template-columns: minmax(0, 1.34fr) minmax(0, 1fr);
  grid-auto-rows: minmax(0, 1fr);
  height: clamp(600px, 76vh, 880px);
}

@media (max-width: 980px) {
  /* the rows go back to sizing themselves as well. minmax(0, 1fr) exists to
     divide a stated height between the panes; over an automatic height it
     divides nothing and simply makes every pane as tall as the tallest, so a
     short pane is padded out with empty ground under it */
  .demo-body {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
    height: auto;
    min-height: 560px;
  }
}

/* graph pane */

.graph-pane {
  position: relative;
  border-right: 1px solid rgb(var(--kl-night-line));
  background:
    radial-gradient(70% 60% at 50% 40%, rgb(var(--kl-gold) / 0.06), transparent 72%),
    rgb(var(--kl-night-soft));
  min-height: 420px;
}

@media (max-width: 980px) {
  .graph-pane { border-right: 0; border-bottom: 1px solid rgb(var(--kl-night-line)); }
}

#graph { width: 100%; height: 100%; display: block; touch-action: pan-y; }

.graph-legend {
  position: absolute;
  left: 16px;
  bottom: 14px;
  display: grid;
  gap: 7px;
  font-size: 12px;
  color: rgb(var(--kl-night-fg) / 0.62);
  pointer-events: none;
}

.legend-row { display: flex; align-items: center; gap: 8px; }
.legend-swatch { width: 10px; height: 10px; border-radius: 999px; }

.graph-filters {
  position: absolute;
  right: 14px;
  top: 14px;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.chip {
  border: 1px solid rgb(var(--kl-night-fg) / 0.18);
  color: rgb(var(--kl-night-fg) / 0.72);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: all 0.15s ease;
  background: rgb(var(--kl-night) / 0.5);
  backdrop-filter: blur(4px);
}

.chip:hover { border-color: rgb(var(--kl-night-fg) / 0.4); color: rgb(var(--kl-night-fg)); }

.chip[aria-pressed='true'] {
  border-color: rgb(var(--kl-gold) / 0.55);
  color: rgb(var(--kl-ink));
  background: rgb(var(--kl-gold));
}

.chip .dot { width: 7px; height: 7px; border-radius: 999px; }

/* graph svg pieces */

.edge {
  stroke: rgb(var(--kl-night-fg) / 0.22);
  stroke-width: 1;
  transition: stroke 0.2s ease, stroke-width 0.2s ease, opacity 0.2s ease;
}

.edge.is-lit { stroke: rgb(var(--kl-gold-deep) / 0.8); stroke-width: 1.6; }
.edge.is-dim { opacity: 0.18; }

.node { cursor: pointer; }
.node-halo { fill: rgb(var(--kl-gold-deep) / 0.18); opacity: 0; transition: opacity 0.2s ease; }
.node.is-active .node-halo,
.node:hover .node-halo { opacity: 1; }

.node-core {
  fill: rgb(var(--kl-night-fg) / 0.9);
  stroke: rgb(var(--kl-night));
  stroke-width: 1.5;
  transition: fill 0.2s ease, r 0.2s ease;
}

.node.is-active .node-core { fill: rgb(var(--kl-gold)); }
.node.is-dim { opacity: 0.3; }

.node-label {
  font-family: var(--font-body);
  font-size: 10.5px;
  fill: rgb(var(--kl-night-fg) / 0.72);
  text-anchor: middle;
  pointer-events: none;
  transition: fill 0.2s ease;
}

.node.is-active .node-label { fill: rgb(var(--kl-night-fg)); }
.node:hover .node-label { fill: rgb(var(--kl-night-fg)); }

.sat { pointer-events: none; }
.sat-mcq { fill: rgb(var(--kl-info) / 0.95); }
.sat-lec { fill: rgb(var(--kl-success) / 0.95); }

/* detail pane */

.detail-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  position: relative;
  background: rgb(var(--kl-night));
}

.detail-head {
  padding: 22px 24px 18px;
  border-bottom: 1px solid rgb(var(--kl-night-line));
  display: grid;
  gap: 10px;
}

.detail-head h3 { font-size: 25px; }

.detail-head p { font-size: 14px; color: rgb(var(--kl-night-fg) / 0.66); }

.alias-row { display: flex; flex-wrap: wrap; gap: 6px; }

.alias {
  font-size: 11.5px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid rgb(var(--kl-night-fg) / 0.16);
  color: rgb(var(--kl-night-fg) / 0.6);
}

.lens-tabs {
  display: flex;
  gap: 2px;
  padding: 10px 16px 0;
  border-bottom: 1px solid rgb(var(--kl-night-line));
}

.lens-tab {
  padding: 9px 14px 11px;
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.66);
  border-bottom: 2px solid transparent;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.lens-tab:hover { color: rgb(var(--kl-night-fg) / 0.85); }

.lens-tab[aria-selected='true'] {
  color: rgb(var(--kl-night-fg));
  border-bottom-color: rgb(var(--kl-gold-deep));
}

.lens-count {
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgb(var(--kl-night-fg) / 0.1);
  color: rgb(var(--kl-night-fg) / 0.7);
}

.detail-scroll {
  overflow-y: auto;
  padding: 20px 24px 28px;
  display: grid;
  gap: 14px;
  align-content: start;
  flex: 1 1 auto;
  min-height: 0;
  /* the scrollbar is the only sign a column carries more than it shows, so it
     is drawn to be seen rather than hidden away */
  /* a column that has been read to the end does not take the page with it */
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgb(var(--kl-night-fg) / 0.34) rgb(var(--kl-night-fg) / 0.07);
}

@media (max-width: 980px) {
  .detail-scroll { flex: none; max-height: 460px; }
}

.detail-scroll::-webkit-scrollbar { width: 10px; }
.detail-scroll::-webkit-scrollbar-track { background: rgb(var(--kl-night-fg) / 0.07); }
.detail-scroll::-webkit-scrollbar-thumb {
  background: rgb(var(--kl-night-fg) / 0.32);
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 999px;
}

.item {
  border: 1px solid rgb(var(--kl-night-line));
  border-radius: var(--r-md);
  background: rgb(var(--kl-night-soft));
  padding: 16px 17px;
  display: grid;
  gap: 10px;
  animation: rise 0.32s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.item:nth-child(2) { animation-delay: 0.04s; }
.item:nth-child(3) { animation-delay: 0.08s; }
.item:nth-child(4) { animation-delay: 0.12s; }
.item:nth-child(n + 5) { animation-delay: 0.16s; }

@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.item-top {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 12px;
  color: rgb(var(--kl-night-fg) / 0.62);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid transparent;
}

.badge-book { background: rgb(var(--kl-gold) / 0.16); border-color: rgb(var(--kl-gold-deep) / 0.35); color: rgb(var(--kl-gold-deep)); }
.badge-mcq { background: rgb(var(--kl-info-soft)); border-color: rgb(var(--kl-info-line)); color: rgb(var(--kl-info)); }
.badge-lec { background: rgb(var(--kl-success-soft)); border-color: rgb(var(--kl-success-line)); color: rgb(var(--kl-success)); }
.badge-strong { background: rgb(var(--kl-night-fg) / 0.08); border-color: rgb(var(--kl-night-fg) / 0.16); color: rgb(var(--kl-night-fg) / 0.65); }

.item-prose {
  font-size: 13.5px;
  line-height: 1.72;
  color: rgb(var(--kl-night-fg) / 0.78);
}

.item-prose em { color: rgb(var(--kl-gold-deep)); font-style: normal; background: rgb(var(--kl-gold-soft) / 0.45); padding: 0 3px; border-radius: 3px; }

.item-title { font-size: 14.5px; color: rgb(var(--kl-night-fg)); font-weight: 500; }

.opts { display: grid; gap: 6px; }

.opt {
  display: flex;
  gap: 10px;
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.7);
  padding: 7px 10px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  text-align: left;
  width: 100%;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.opt:hover { background: rgb(var(--kl-night-fg) / 0.05); }
.opt .k { color: rgb(var(--kl-night-fg) / 0.6); width: 14px; flex: none; }

.opt.is-right {
  border-color: rgb(var(--kl-success-line));
  background: rgb(var(--kl-success-soft));
  color: rgb(var(--kl-ink));
}
.opt.is-right .k { color: rgb(var(--kl-success)); }

.opt.is-wrong {
  border-color: rgb(var(--kl-error, 178 74 58) / 0.4);
  background: rgb(178 74 58 / 0.09);
}

.reveal {
  font-size: 12.5px;
  color: rgb(var(--kl-night-fg) / 0.6);
  border-top: 1px solid rgb(var(--kl-night-line));
  padding-top: 10px;
}

.waveform { display: flex; align-items: flex-end; gap: 2px; height: 22px; }
.waveform i {
  width: 2px;
  background: rgb(var(--kl-success) / 0.55);
  border-radius: 999px;
  display: block;
}

.page-ref {
  font-size: 12px;
  color: rgb(var(--kl-night-fg) / 0.6);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.empty-note {
  font-size: 13px;
  color: rgb(var(--kl-night-fg) / 0.6);
  padding: 12px 2px;
}

/* ---------- segmented control ---------- */

.seg {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  border: 1px solid rgb(var(--kl-night-fg) / 0.14);
  border-radius: 999px;
  background: rgb(var(--kl-night-soft));
}

.seg-btn {
  padding: 5px 14px;
  border-radius: 999px;
  font-size: 12.5px;
  color: rgb(var(--kl-night-fg) / 0.6);
  transition: background-color 0.16s ease, color 0.16s ease;
  white-space: nowrap;
}

.seg-btn:hover { color: rgb(var(--kl-night-fg) / 0.9); }

.seg-btn[aria-selected='true'] {
  background: rgb(var(--kl-ink));
  color: rgb(var(--kl-onink));
}

/* ---------- study view (lecture · page · questions) ---------- */

/* the sheet in the middle is the thing being read, so it takes the room; the
   two rails either side are wide enough for a card and no wider. The whole
   view is one height and each column scrolls inside it. */
.study {
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.72fr) minmax(0, 1.04fr);
  /* the row is pinned to the frame's height: without this the row grows to
     fit the tallest column, the frame clips it, and nothing ever scrolls */
  grid-auto-rows: minmax(0, 1fr);
  height: clamp(600px, 76vh, 880px);
  background: rgb(var(--kl-night));
}

/* not enough width for three: the sheet goes full width and the two rails sit
   under it side by side, which reads better than three squeezed columns */
@media (max-width: 1240px) {
  .study {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    /* and the rows with it: an fr row over an automatic height gives every row
       the height of the tallest one, so the sheet's length is charged to the
       two rails underneath it as blank ground */
    grid-auto-rows: auto;
    height: auto;
  }
  .study-page { order: -1; grid-column: 1 / -1; }
}

@media (max-width: 1040px) {
  .study { grid-template-columns: 1fr; }
}

.study-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  /* a grid item's automatic minimum is its content, which would push the
     column past the frame instead of letting .col-body scroll */
  min-height: 0;
  position: relative;
  border-right: 1px solid rgb(var(--kl-night-line));
}

/* .has-more is set by app.js on any column with something under the fold, and
   comes off at the end of the scroll, so the fade is never a lie */
.study-col::after,
.detail-pane::after {
  content: '';
  position: absolute;
  left: 0;
  /* clear of the scrollbar gutter: the fade is for the text, not for the bar */
  right: 12px;
  bottom: 0;
  height: 52px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
  background: linear-gradient(to top, rgb(var(--kl-night)) 12%, rgb(var(--kl-night) / 0));
}

.study-page::after {
  background: linear-gradient(to top, rgb(var(--kl-night-soft)) 12%, rgb(var(--kl-night-soft) / 0));
}

.study-col.has-more::after,
.detail-pane.has-more::after { opacity: 1; }

.study-col:last-child { border-right: 0; }

@media (max-width: 1240px) {
  .study-page { border-right: 0; border-bottom: 1px solid rgb(var(--kl-night-line)); }
  .study-questions { border-right: 0; }
}

@media (max-width: 1040px) {
  .study-col { border-right: 0; border-bottom: 1px solid rgb(var(--kl-night-line)); }
  .study-col:last-child { border-bottom: 0; }
}

.col-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  border-bottom: 1px solid rgb(var(--kl-night-line));
  min-height: 46px;
}

.col-title { font-size: 13px; color: rgb(var(--kl-night-fg) / 0.82); }

.col-count {
  font-size: 11px;
  min-width: 20px;
  text-align: center;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgb(var(--kl-night-fg) / 0.1);
  color: rgb(var(--kl-night-fg) / 0.7);
}

.col-note {
  font-size: 12px;
  color: rgb(var(--kl-night-fg) / 0.62);
  padding: 10px 16px 0;
  display: flex;
  align-items: center;
  gap: 7px;
}

.col-note::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: 999px;
  background: rgb(var(--kl-gold));
  flex: none;
}

.col-note em { font-style: normal; color: rgb(var(--kl-gold-deep)); }

.col-body {
  overflow-y: auto;
  padding: 16px 18px 22px;
  display: grid;
  gap: 12px;
  align-content: start;
  flex: 1 1 auto;
  min-height: 0;
  /* the scrollbar is the only sign a column carries more than it shows, so it
     is drawn to be seen rather than hidden away */
  scrollbar-width: thin;
  scrollbar-color: rgb(var(--kl-night-fg) / 0.34) rgb(var(--kl-night-fg) / 0.07);
}

/* once the view stacks there is no frame height to fill, so each column goes
   back to a cap of its own */
@media (max-width: 1240px) {
  .col-body { flex: none; max-height: 440px; }
  .page-body { max-height: 620px; }
}

.col-body::-webkit-scrollbar { width: 10px; }
.col-body::-webkit-scrollbar-track { background: rgb(var(--kl-night-fg) / 0.07); }
.col-body::-webkit-scrollbar-thumb {
  background: rgb(var(--kl-night-fg) / 0.32);
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 999px;
}
.col-body::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--kl-night-fg) / 0.5);
  background-clip: padding-box;
}

/* centre: the page sheet */

.page-head { background: rgb(var(--kl-night)); }
.page-nav { font-size: 12px; color: rgb(var(--kl-night-fg) / 0.62); }

.page-body {
  background: rgb(var(--kl-night-soft));
  padding: 22px 26px 30px;
  display: block;
}

.page-sheet {
  background: rgb(var(--kl-card));
  color: rgb(var(--kl-ink));
  border-radius: var(--r-sm);
  padding: 32px 36px 36px;
  box-shadow: 0 16px 40px -24px rgb(34 31 26 / 0.24);
  display: grid;
  gap: 15px;
  /* a line of the sheet stays around 75 characters, the way a printed page
     does: the extra width in the column becomes margin, not longer lines */
  max-width: 660px;
  margin-inline: auto;
}

.page-crumb {
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgb(var(--kl-muted));
}

.page-sheet h4 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -0.02em;
  margin: 0;
}

.page-sheet p {
  font-size: 14px;
  line-height: 1.78;
  color: rgb(var(--kl-ink-soft));
}

/* every paragraph is a click target, and that is how the panels either side are driven */
.para {
  position: relative;
  width: 100%;
  text-align: left;
  display: block;
  padding: 8px 12px 8px 14px;
  margin-inline: -12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color 0.16s ease;
}

.para::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 2px;
  border-radius: 999px;
  background: rgb(var(--kl-gold-deep));
  opacity: 0;
  transition: opacity 0.16s ease;
}

.para:hover { background: rgb(var(--kl-surface) / 0.55); }
.para:hover::before { opacity: 0.4; }

.para.is-on { background: rgb(var(--kl-gold-soft) / 0.24); }
.para.is-on::before { opacity: 1; }

/* the paragraph's margin mark: dots, no words. Blue is a question, green is
   a lecture moment, and the count is the number of dots. */
.para-dots {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
  min-height: 6px;
}

.dot-run {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.para-dots .dot {
  width: 5px;
  height: 5px;
  border-radius: 999px;
  display: block;
  opacity: 0.55;
  transition: opacity 0.16s ease, transform 0.16s ease;
}

.para-dots .dot-q { background: #3e6b8c; }
.para-dots .dot-l { background: #4d7638; }
.para-dots .dot-none {
  background: none;
  box-shadow: inset 0 0 0 1px rgb(var(--kl-border));
  opacity: 1;
}

.dot-more {
  font-size: 10px;
  line-height: 1;
  color: rgb(var(--kl-muted));
  opacity: 0.7;
  margin-left: 1px;
}

.para:hover .para-dots .dot { opacity: 0.8; }
.para.is-on .para-dots .dot { opacity: 1; }
.para.is-on .dot-q,
.para.is-on .dot-l { transform: scale(1.15); }
.para.is-on .dot-more { opacity: 1; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid rgb(var(--kl-border));
  background: rgb(var(--kl-card));
  font-size: 11px;
}


.sheet-hint {
  font-size: 11.5px;
  color: rgb(var(--kl-muted));
  display: flex;
  align-items: center;
  gap: 7px;
  padding-bottom: 2px;
}

.sheet-hint::before {
  content: '';
  width: 14px;
  height: 1px;
  background: rgb(var(--kl-gold-deep));
}

.page-sheet mark {
  background: rgb(var(--kl-gold-soft) / 0.55);
  color: rgb(var(--kl-ink));
  padding: 1px 2px;
  border-radius: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.page-foot {
  border-top: 1px solid rgb(var(--kl-border));
  padding-top: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  color: rgb(var(--kl-muted));
  flex-wrap: wrap;
}

.tagpair { display: flex; gap: 6px; flex-wrap: wrap; }

/* lecture rail cards */

.seg-card {
  border: 1px solid rgb(var(--kl-night-line));
  border-radius: var(--r-md);
  background: rgb(var(--kl-night-soft));
  padding: 13px 14px;
  display: grid;
  gap: 9px;
}

.seg-time {
  font-size: 11.5px;
  color: rgb(var(--kl-night-fg) / 0.62);
  display: flex;
  align-items: center;
  gap: 8px;
}

.seg-time b {
  font-weight: 400;
  color: rgb(var(--kl-success));
  background: rgb(var(--kl-success-soft));
  border: 1px solid rgb(var(--kl-success-line));
  border-radius: 999px;
  padding: 1px 8px;
}

.seg-card h5 {
  margin: 0;
  font-size: 13.5px;
  font-weight: 500;
  color: rgb(var(--kl-night-fg) / 0.94);
}

.seg-card p { font-size: 12.5px; line-height: 1.66; color: rgb(var(--kl-night-fg) / 0.62); }

.col-empty {
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
  padding: 40px 18px;
  color: rgb(var(--kl-night-fg) / 0.6);
  font-size: 12.5px;
  line-height: 1.6;
}

.col-empty b {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 16px;
  color: rgb(var(--kl-night-fg) / 0.8);
}

.col-empty .icon {
  width: 40px; height: 40px;
  border-radius: var(--r-md);
  background: rgb(var(--kl-night-fg) / 0.07);
  display: grid;
  place-items: center;
}

/* ---------- lenses / feature rows ---------- */

.rows { display: grid; gap: clamp(48px, 7vw, 84px); margin-top: clamp(44px, 6vw, 72px); }

.row {
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(28px, 5vw, 60px);
  align-items: center;
}

.row.is-flipped { grid-template-columns: minmax(0, 1.14fr) minmax(0, 0.86fr); }
.row.is-flipped .row-text { order: 2; }

@media (max-width: 900px) {
  .row, .row.is-flipped { grid-template-columns: 1fr; }
  .row.is-flipped .row-text { order: 0; }
}

.row-text { display: grid; gap: 14px; align-content: start; }
.row-text h3 { font-size: clamp(24px, 3vw, 32px); }
.row-text p { color: rgb(var(--kl-ink-soft)); }

.row-list { display: grid; gap: 9px; margin-top: 4px; }
.row-list li {
  list-style: none;
  padding-left: 22px;
  position: relative;
  font-size: 14px;
  color: rgb(var(--kl-ink-soft));
}
.row-list li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 9px;
  width: 8px; height: 8px;
  background: rgb(var(--kl-gold));
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}

/* image slots: a coded frame (see below), or an image once one exists */

.shot {
  border: 1px solid rgb(var(--kl-border));
  border-radius: var(--r-lg);
  background: rgb(var(--kl-card));
  overflow: hidden;
  position: relative;
  box-shadow: 0 18px 44px -34px rgb(34 31 26 / 0.5);
  /* the frames inside size themselves off this box, not off the viewport */
  container-type: inline-size;
}

.shot img { width: 100%; height: auto; display: block; }

.shot-cap {
  border-top: 1px solid rgb(var(--kl-border));
  padding: 10px 14px;
  font-size: 12.5px;
  color: rgb(var(--kl-muted));
  background: rgb(var(--kl-card));
}

/* ---------- coded product frames ----------
   What sits where a screenshot would: the product's own screens, rebuilt in
   markup off the same tokens the app is built from (klaro-frontend). Every
   size here is the real one divided by ten and written in em, and the em is
   derived from the frame's own width (--ui-w is the design width in tens of
   px), so a single number scales a whole screen down into the column it
   lands in. Nothing inside is focusable: each frame is one role="img" whose
   label carries what the caption on a screenshot would have said. */

.ui {
  --ui-w: 90;
  --ui-paper: 255 253 250;
  /* the fallback for a browser without container units: a fixed 70% scale.
     No lower bound on the real rule: a floor would stop the frame shrinking
     with its column and the far rail would be cut off on a phone. */
  font-size: 7px;
  font-size: min(calc(100cqi / var(--ui-w)), 12px);
  aspect-ratio: 16 / 10;
  display: grid;
  overflow: hidden;
  background: rgb(var(--kl-page));
  color: rgb(var(--kl-ink));
  line-height: 1.45;
  -webkit-user-select: none;
  user-select: none;
}

.ui b { font-weight: 500; }
.ui em { font-style: normal; }
.ui .mut { color: rgb(var(--kl-muted)); }
.ui .soft { color: rgb(var(--kl-ink-soft)); }
.ui .num { font-variant-numeric: tabular-nums; }

.ui-i {
  width: 1.4em; height: 1.4em; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.ui-i.fill { fill: currentColor; stroke: none; }

/* ---------- shared chrome ---------- */

.ui-top {
  display: flex; align-items: center; gap: 1.2em;
  height: 5.2em; padding: 0 1.8em;
  background: rgb(var(--kl-card));
  border-bottom: 1px solid rgb(var(--kl-border));
}
.ui-crumb {
  display: flex; align-items: center; gap: .6em;
  min-width: 0; font-size: 1.3em; color: rgb(var(--kl-muted)); white-space: nowrap;
}
.ui-crumb b { color: rgb(var(--kl-ink)); }
.ui-crumb .cut { overflow: hidden; text-overflow: ellipsis; }
.ui-av {
  margin-left: auto; flex: none;
  width: 3.2em; height: 3.2em; border-radius: 999px;
  background: rgb(var(--kl-ink)); color: rgb(var(--kl-gold));
  display: grid; place-items: center;
  font-size: 1.1em; font-weight: 500; letter-spacing: .04em;
}
.ui-mark {
  display: inline-flex; align-items: center; gap: .18em;
  font-family: var(--font-display); font-weight: 500; letter-spacing: -0.02em;
  font-size: 2em; line-height: 1;
}
.ui-mark svg { width: .5em; height: .5em; fill: rgb(var(--kl-gold)); stroke: none; align-self: flex-end; margin-bottom: .27em; }

.ui-btn {
  display: inline-flex; align-items: center; gap: .5em;
  height: 2.7em; padding: 0 1.1em; border-radius: .55em;
  font-size: 1.2em; font-weight: 500; white-space: nowrap;
}
.ui-btn.pri { background: rgb(var(--kl-ink)); color: rgb(var(--kl-card)); }
.ui-btn.sec { border: 1px solid rgb(var(--kl-border)); color: rgb(var(--kl-ink)); }
.ui-btn .ui-i { width: 1.1em; height: 1.1em; }

.ui-chip {
  display: inline-flex; align-items: center; gap: .5em;
  height: 2em; padding: 0 .85em; border-radius: 999px;
  border: 1px solid rgb(var(--kl-border)); background: rgb(var(--kl-surface));
  color: rgb(var(--kl-ink-soft)); font-size: 1.1em; white-space: nowrap;
}

.ui-card {
  background: rgb(var(--kl-card));
  border: 1px solid rgb(var(--kl-border));
  border-radius: .9em;
}

/* ---------- 1 · the study view (three panels) ---------- */

.ui-reader { grid-template-rows: auto minmax(0, 1fr); }
.ui-cols { display: grid; grid-template-columns: 19em minmax(0, 1fr) 27em; min-height: 0; }

.ui-rail { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.ui-rail.left { background: rgb(var(--kl-card)); border-right: 1px solid rgb(var(--kl-border)); }
.ui-rail.right { background: rgb(var(--kl-page)); border-left: 1px solid rgb(var(--kl-border)); }

.ui-rh {
  display: flex; align-items: center; justify-content: space-between; gap: 1em;
  height: 4.8em; padding: 0 1.4em;
  border-bottom: 1px solid rgb(var(--kl-border)); flex: none;
}
.ui-rh .t { font-family: var(--font-display); font-weight: 500; font-size: 1.3em; letter-spacing: -0.01em; }
.ui-rh .s { display: block; margin-top: .25em; font-size: 1.1em; color: rgb(var(--kl-muted)); }
.ui-rh .n {
  flex: none; min-width: 2em; height: 2em; padding: 0 .6em; border-radius: 999px;
  background: rgb(var(--kl-surface)); color: rgb(var(--kl-ink-soft));
  display: grid; place-items: center; font-size: 1.1em; font-weight: 500;
}

.ui-seglist { padding: .8em; display: grid; gap: .3em; }
.ui-seg { display: flex; gap: 1em; align-items: flex-start; padding: 1em 1.2em; border-radius: .6em; }
.ui-seg.on { background: rgb(var(--kl-gold-soft)); }
.ui-seg .ui-i { color: rgb(var(--kl-muted)); width: 1.3em; height: 1.3em; margin-top: .15em; }
.ui-seg.on .ui-i { color: rgb(var(--kl-gold-deep)); }
.ui-seg .t { font-size: 1.3em; font-weight: 500; line-height: 1.3; }
.ui-seg.weak .t { font-weight: 400; color: rgb(var(--kl-ink-soft)); }
.ui-seg .m { margin-top: .3em; font-size: 1.1em; color: rgb(var(--kl-muted)); font-variant-numeric: tabular-nums; }
.ui-more { margin: .5em .8em 0; padding: .8em 1.2em; font-size: 1.2em; color: rgb(var(--kl-ink-soft)); }

.ui-mid { display: flex; flex-direction: column; min-width: 0; background: rgb(var(--kl-page)); }
.ui-tools {
  display: flex; align-items: center; gap: .9em; flex: none;
  height: 4.8em; padding: 0 1.2em;
  background: rgb(var(--kl-card)); border-bottom: 1px solid rgb(var(--kl-border));
  color: rgb(var(--kl-muted));
}
.ui-tools .pill {
  display: inline-flex; align-items: center; gap: .5em;
  height: 2.4em; padding: 0 .9em; border-radius: 999px;
  border: 1px solid rgb(var(--kl-border));
  color: rgb(var(--kl-ink)); font-size: 1.2em; font-weight: 500;
}
.ui-tools .pill .ui-i { width: 1.2em; height: 1.2em; }
.ui-div { width: 1px; height: 1.8em; background: rgb(var(--kl-border)); flex: none; }
.ui-tools .pg { font-size: 1.2em; }
.ui-tools .zoom { margin-left: auto; display: flex; align-items: center; gap: .6em; }
.ui-sq { width: 2.6em; height: 2.6em; border-radius: .5em; display: grid; place-items: center; flex: none; }
.ui-sq.box { border: 1px solid rgb(var(--kl-border)); }
.ui-sq .ui-i { width: 1.2em; height: 1.2em; }

.ui-scroll { flex: 1; min-height: 0; padding: 1.4em 1.8em 0; overflow: hidden; }
.ui-sheet {
  background: rgb(var(--ui-paper));
  border: 1px solid rgb(var(--kl-border));
  border-radius: .3em;
  padding: 2.4em 2.6em;
  box-shadow: 0 1.6em 3.4em -2.6em rgb(34 31 26 / 0.55);
  font-family: Georgia, 'Times New Roman', serif;
}
.ui-sheet .run {
  font-family: var(--font-body); font-size: 1em; letter-spacing: .1em;
  text-transform: uppercase; color: rgb(var(--kl-muted));
}
.ui-sheet h4 { font-family: inherit; font-size: 1.9em; font-weight: 700; margin: .5em 0 .45em; letter-spacing: 0; }
.ui-sheet p { font-size: 1.25em; line-height: 1.5; margin: 0 0 .65em; }
.ui-sheet p.pick {
  position: relative;
  margin-left: -.55em; margin-right: -.55em;
  padding: .35em .55em;
  border-radius: .25em;
  background: rgb(var(--kl-gold) / 0.14);
  box-shadow: 0 0 0 1px rgb(var(--kl-gold-deep) / 0.6);
}
.ui-sheet p.dim { color: rgb(var(--kl-ink-soft)); }
.ui-sheet sup { font-size: .7em; vertical-align: .4em; line-height: 0; }

.ui-qlist { padding: 1.4em 1.2em 1.2em; display: grid; gap: 1em; align-content: start; overflow: hidden; }
.ui-q { background: rgb(var(--kl-card)); border: 1px solid rgb(var(--kl-border)); border-radius: .8em; padding: 1.4em; }
.ui-q .qn { display: block; font-size: 1em; letter-spacing: .08em; font-weight: 500; color: rgb(var(--kl-muted)); }
.ui-q .stem { font-size: 1.3em; line-height: 1.45; margin-top: .7em; }
.ui-q .fold {
  display: flex; align-items: center; gap: .6em;
  margin-top: 1.1em; padding-top: 1.1em; border-top: 1px solid rgb(var(--kl-border));
  font-size: 1.25em; font-weight: 500; color: rgb(var(--kl-ink-soft));
}
.ui-q .fold .ui-i { width: 1.1em; height: 1.1em; stroke-width: 2.5; }
.ui-q .tail { margin-top: 1em; font-size: 1.1em; color: rgb(var(--kl-ink-soft)); text-decoration: underline; text-underline-offset: .25em; }
.ui-q .tag-row { margin-top: .7em; }

/* ---------- 2 · practising a paper question ---------- */

.ui-practice { grid-template-rows: auto minmax(0, 1fr); }
.ui-practice .ui-cols { grid-template-columns: minmax(0, 1fr) 38em; }
.ui-practice .ui-qlist { padding: 1.4em 1em 1em; }
.ui-practice .ui-q { padding: 1.1em; }
.ui-practice .ui-opt { padding: .7em .9em; }
.ui-practice .ui-opts { gap: .45em; }
/* the expander is open, so its chevron points down */
.ui-practice .fold .ui-i { transform: rotate(90deg); }

.ui-opts { display: grid; gap: .5em; margin-top: .9em; }
.ui-opt {
  display: flex; align-items: flex-start; gap: .9em;
  padding: .75em 1em; border-radius: .6em;
  border: 1px solid rgb(var(--kl-border)); background: rgb(var(--kl-card));
  font-size: 1.25em; line-height: 1.4;
}
.ui-opt .r { flex: none; width: 1.15em; height: 1.15em; margin-top: .12em; border-radius: 999px; border: 1px solid rgb(var(--kl-muted)); }
.ui-opt .l { font-weight: 500; flex: none; }
.ui-opt.ok { border-color: rgb(var(--kl-success-line)); background: rgb(var(--kl-success-soft)); }
.ui-opt.ok .r { border-color: rgb(var(--kl-success)); border-width: .34em; }
.ui-opt .ui-i { margin-left: auto; width: 1.2em; height: 1.2em; color: rgb(var(--kl-success)); }
.ui-fb {
  display: flex; gap: .8em; align-items: flex-start;
  margin-top: 1em; padding: .95em 1.05em; border-radius: .6em;
  border: 1px solid rgb(var(--kl-success-line)); background: rgb(var(--kl-success-soft));
  font-size: 1.25em; line-height: 1.45;
}
.ui-fb .ui-i { width: 1.3em; height: 1.3em; margin-top: .1em; color: rgb(var(--kl-success)); }
.ui-link {
  display: flex; align-items: flex-start; gap: .8em;
  margin-top: 1em; padding: 1em 1.1em; border-radius: .6em;
  border: 1px solid rgb(var(--kl-border)); background: rgb(var(--kl-surface));
  font-size: 1.2em; line-height: 1.4;
}
.ui-link .ui-i { width: 1.3em; height: 1.3em; margin-top: .15em; color: rgb(var(--kl-muted)); }

.ui-sheet .qhead { font-family: var(--font-body); font-size: 1.15em; font-weight: 500; color: rgb(var(--kl-ink-soft)); margin-bottom: .6em; }
.ui-sheet ol.opts { margin: .35em 0 .8em; padding-left: 1.8em; font-size: 1.2em; line-height: 1.5; list-style-type: upper-alpha; }
.ui-sheet ol.opts li { margin-bottom: .1em; }

/* ---------- 3 · the lecture player ---------- */

.ui-lecture { grid-template-rows: auto minmax(0, 1fr); }
.ui-lbody {
  padding: 2.2em 2.4em; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) 29em; gap: 2em; align-items: start;
}
.ui-video {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  border-radius: .9em; background: rgb(var(--kl-video));
  display: grid; place-items: center;
}
.ui-play {
  width: 5.4em; height: 5.4em; border-radius: 999px;
  background: rgb(var(--kl-gold)); color: rgb(var(--kl-ink));
  display: grid; place-items: center;
}
.ui-play .ui-i { width: 2.2em; height: 2.2em; }
.ui-vbar { position: absolute; inset: auto 0 0 0; padding: 1.2em; display: grid; gap: .8em; }
.ui-vtrack { height: .4em; border-radius: 999px; background: rgb(255 255 255 / 0.25); overflow: hidden; }
.ui-vtrack i { display: block; height: 100%; width: 38%; background: rgb(var(--kl-gold)); }
.ui-vrow { display: flex; align-items: center; gap: 1em; color: rgb(var(--kl-onink)); font-size: 1.15em; }
.ui-vrow .ui-i { width: 1.3em; height: 1.3em; }
.ui-vrow .end { margin-left: auto; }

.ui-ltitle {
  margin-top: 1.4em;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1.2em;
}
.ui-ltitle .t { font-family: var(--font-display); font-weight: 500; font-size: 2.2em; letter-spacing: -0.015em; line-height: 1.2; }
.ui-ltitle .s { font-size: 1.3em; color: rgb(var(--kl-muted)); margin-top: .5em; }
.ui-chip.plain { background: rgb(var(--kl-card)); height: 2.4em; font-size: 1.2em; }

.ui-rel { margin-top: 1.6em; }
.ui-rel .h { font-size: 1.25em; font-weight: 500; color: rgb(var(--kl-ink-soft)); margin-bottom: .7em; }
.ui-rel .ic {
  width: 3.2em; height: 3.2em; flex: none; border-radius: .5em;
  background: rgb(var(--kl-surface)); color: rgb(var(--kl-ink-soft));
  display: grid; place-items: center;
}

.ui-panel { overflow: hidden; background: rgb(var(--kl-card)); border: 1px solid rgb(var(--kl-border)); border-radius: .9em; }
.ui-panel-h { padding: 1.1em 1.4em; border-bottom: 1px solid rgb(var(--kl-border)); }
.ui-panel-h .t { font-family: var(--font-display); font-weight: 500; font-size: 1.3em; letter-spacing: -0.01em; }
.ui-panel-h .s { display: block; margin-top: .3em; font-size: 1.1em; color: rgb(var(--kl-muted)); }
.ui-clist { padding: .8em; display: grid; gap: .2em; }
.ui-crow { display: flex; gap: 1em; align-items: flex-start; padding: 1em 1.2em; border-radius: .6em; }
.ui-crow.on { background: rgb(var(--kl-gold-soft)); box-shadow: inset 0 0 0 1px rgb(var(--kl-gold-deep)); }
.ui-crow .tm { flex: none; width: 4em; font-size: 1.1em; color: rgb(var(--kl-muted)); font-variant-numeric: tabular-nums; margin-top: .2em; }
.ui-crow.on .tm { color: rgb(var(--kl-ink-soft)); }
.ui-crow .t { font-size: 1.3em; font-weight: 500; line-height: 1.3; }
.ui-crow.weak .t { font-weight: 400; color: rgb(var(--kl-ink-soft)); }
.ui-crow .m { margin-top: .3em; font-size: 1.1em; color: rgb(var(--kl-ink-soft)); line-height: 1.35; }
.ui-crow .ui-i { margin-left: auto; width: 1.3em; height: 1.3em; color: rgb(var(--kl-muted)); margin-top: .2em; }
.ui-panel-f { padding: .9em 1.4em; border-top: 1px solid rgb(var(--kl-border)); font-size: 1.2em; color: rgb(var(--kl-ink-soft)); }

/* ---------- 4 and 5 · the app shell (student home, tutor workspace) ---------- */

.ui-app { grid-template-columns: 22em minmax(0, 1fr); }
.ui-app.tutor { grid-template-columns: 24em minmax(0, 1fr); }

.ui-side { background: rgb(var(--kl-card)); border-right: 1px solid rgb(var(--kl-border)); display: flex; flex-direction: column; min-height: 0; }
.ui-side-h {
  display: flex; align-items: center; gap: .8em;
  height: 5.6em; padding: 0 1.8em;
  border-bottom: 1px solid rgb(var(--kl-border));
}
.ui-side-h .role {
  border-radius: 999px; background: rgb(var(--kl-surface)); color: rgb(var(--kl-ink-soft));
  padding: .25em .8em; font-size: 1.1em; font-weight: 500;
}
.ui-nav { padding: .8em; display: grid; gap: .1em; }
.ui-nav .it { display: flex; align-items: center; gap: 1em; padding: .85em 1em; border-radius: .6em; font-size: 1.3em; color: rgb(var(--kl-muted)); }
.ui-nav .it.on { background: rgb(var(--kl-surface)); color: rgb(var(--kl-ink)); font-weight: 500; }
.ui-nav .it .ui-i { width: 1.4em; height: 1.4em; stroke-width: 1.6; }
.ui-nav .it .dot { margin-left: auto; width: .45em; height: .45em; border-radius: 999px; background: rgb(var(--kl-gold)); }
.ui-side-f { margin-top: auto; border-top: 1px solid rgb(var(--kl-border)); padding: 1.1em 1.4em; display: flex; align-items: center; gap: .9em; }
.ui-side-f .who { min-width: 0; }
.ui-side-f .who b { display: block; font-size: 1.2em; }
.ui-side-f .who em { display: block; font-size: 1.1em; color: rgb(var(--kl-muted)); }
.ui-side-f .ui-av { margin-left: 0; }

.ui-col { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.ui-hd { display: flex; align-items: center; gap: 1.2em; height: 5.6em; padding: 0 2em; background: rgb(var(--kl-card)); border-bottom: 1px solid rgb(var(--kl-border)); flex: none; }
.ui-search {
  margin-left: auto; display: flex; align-items: center; gap: .7em;
  height: 3.4em; width: 17em; padding: 0 1.1em; border-radius: 999px;
  border: 1px solid rgb(var(--kl-border)); background: rgb(var(--kl-surface));
  color: rgb(var(--kl-muted)); font-size: 1.2em;
}
.ui-search .ui-i { width: 1.2em; height: 1.2em; }

.ui-body { padding: 2.2em 2.4em; display: grid; gap: 1.6em; align-content: start; min-height: 0; overflow: hidden; }
.ui-h1 { font-family: var(--font-display); font-weight: 500; font-size: 2.5em; letter-spacing: -0.02em; line-height: 1.1; }
.ui-date { font-size: 1.3em; color: rgb(var(--kl-muted)); margin-top: .45em; }
.ui-sech { display: flex; align-items: flex-end; justify-content: space-between; gap: 1em; margin-bottom: .8em; }
.ui-sech .t { font-family: var(--font-display); font-weight: 500; font-size: 1.7em; letter-spacing: -0.01em; }
.ui-sech .a { font-size: 1.2em; color: rgb(var(--kl-muted)); }

.ui-g2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.2em; }
.ui-g3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.2em; }
.ui-g4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.1em; }

.ui-stat { background: rgb(var(--kl-surface)); border-radius: .8em; padding: 1.2em; }
.ui-stat .l { font-size: 1.25em; color: rgb(var(--kl-ink-soft)); }
.ui-stat .v { font-family: var(--font-display); font-weight: 500; font-size: 2.6em; line-height: 1; margin-top: .4em; }
.ui-stat .v small { font-family: var(--font-body); font-weight: 400; font-size: .5em; color: rgb(var(--kl-ink-soft)); margin-left: .3em; }
.ui-stat .h { font-size: 1.15em; color: rgb(var(--kl-ink-soft)); margin-top: .5em; }

.ui-lec { overflow: hidden; display: flex; flex-direction: column; }
.ui-thumb { position: relative; aspect-ratio: 16 / 9; background: rgb(var(--kl-video)); display: grid; place-items: center; }
.ui-thumb .ui-play { width: 3.6em; height: 3.6em; }
.ui-thumb .ui-play .ui-i { width: 1.5em; height: 1.5em; }
.ui-thumb .dur {
  position: absolute; right: .7em; bottom: .7em;
  background: rgb(var(--kl-card)); color: rgb(var(--kl-ink));
  border-radius: .4em; padding: .15em .5em; font-size: 1.1em; font-weight: 500;
}
.ui-lec .bd { padding: 1.3em; display: grid; grid-template-rows: 1fr auto; gap: 1em; flex: 1; }
.ui-lec .t { font-family: var(--font-display); font-weight: 500; font-size: 1.45em; line-height: 1.25; }
.ui-lec .s { font-size: 1.2em; color: rgb(var(--kl-muted)); margin-top: .35em; }
.ui-bar { height: .5em; border-radius: 999px; background: rgb(var(--kl-surface)); overflow: hidden; }
.ui-bar i { display: block; height: 100%; background: rgb(var(--kl-gold)); }
.ui-pct { font-size: 1.1em; color: rgb(var(--kl-muted)); margin-top: .5em; }

.ui-rows { overflow: hidden; }
.ui-row { display: flex; align-items: center; gap: 1.2em; padding: 1em 1.5em; border-bottom: 1px solid rgb(var(--kl-border)); }
.ui-row:last-child { border-bottom: 0; }
.ui-row .who { min-width: 0; flex: 1; }
.ui-row .who b { display: block; font-size: 1.25em; }
.ui-row .who em { display: block; font-size: 1.2em; color: rgb(var(--kl-muted)); margin-top: .2em; }
.ui-row .meta { font-size: 1.2em; color: rgb(var(--kl-ink-soft)); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ui-row .ui-av { margin-left: 0; }

.ui-act { display: flex; align-items: center; gap: 1.1em; padding: 1.2em; }
.ui-act .ic {
  width: 3.4em; height: 3.4em; flex: none; border-radius: .6em;
  background: rgb(var(--kl-surface)); color: rgb(var(--kl-ink-soft));
  display: grid; place-items: center;
}
.ui-act .t { display: block; font-size: 1.25em; font-weight: 500; }
.ui-act .s { display: block; font-size: 1.15em; color: rgb(var(--kl-muted)); margin-top: .2em; }

/* ---------- provenance strip ---------- */

.facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: rgb(var(--kl-night-line));
  border: 1px solid rgb(var(--kl-night-line));
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-top: 44px;
}

@media (max-width: 860px) { .facts { grid-template-columns: repeat(2, 1fr); } }

.fact {
  background: rgb(var(--kl-night));
  padding: 22px 20px;
  display: grid;
  gap: 6px;
}

.fact b {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 27px;
  color: rgb(var(--kl-night-fg));
  letter-spacing: -0.02em;
}

.fact span { font-size: 13px; color: rgb(var(--kl-night-fg) / 0.66); line-height: 1.5; }

/* ---------- audience ---------- */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-top: 44px;
}

@media (max-width: 840px) { .split { grid-template-columns: 1fr; } }

.card {
  border: 1px solid rgb(var(--kl-border));
  border-radius: var(--r-lg);
  background: rgb(var(--kl-card));
  padding: clamp(24px, 3vw, 34px);
  display: grid;
  gap: 14px;
  align-content: start;
}

.card h3 { font-size: 24px; }
.card p { color: rgb(var(--kl-ink-soft)); font-size: 14.5px; }

/* ---------- faq ---------- */

.faq { margin-top: 40px; border-top: 1px solid rgb(var(--kl-border)); }

.faq details {
  border-bottom: 1px solid rgb(var(--kl-border));
}

.faq summary {
  cursor: pointer;
  list-style: none;
  padding: 20px 44px 20px 0;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 500;
  position: relative;
  transition: color 0.15s ease;
}

.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: rgb(var(--kl-gold-deep)); }

.faq summary::after {
  content: '';
  position: absolute;
  right: 8px;
  top: 50%;
  width: 11px; height: 11px;
  margin-top: -6px;
  border-right: 1.5px solid rgb(var(--kl-muted));
  border-bottom: 1.5px solid rgb(var(--kl-muted));
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.faq details[open] summary::after { transform: rotate(-135deg); margin-top: -2px; }

.faq .answer {
  padding: 0 60px 22px 0;
  color: rgb(var(--kl-ink-soft));
  font-size: 14.5px;
  max-width: 70ch;
}

/* ---------- cta + footer ---------- */

.cta {
  background: rgb(var(--kl-night));
  color: rgb(var(--kl-night-fg));
  position: relative;
  overflow: hidden;
}

.cta-glow {
  position: absolute;
  inset: auto -10% -60% -10%;
  height: 420px;
  background: radial-gradient(50% 60% at 50% 100%, rgb(var(--kl-gold) / 0.16), transparent 70%);
  pointer-events: none;
}

.cta-inner {
  position: relative;
  display: grid;
  gap: 22px;
  justify-items: center;
  text-align: center;
}

.cta h2 { max-width: 18ch; }
.cta .lede { text-align: center; }

.footer {
  background: rgb(var(--kl-bar));
  color: rgb(var(--kl-bar-fg) / 0.55);
  border-top: 1px solid rgb(var(--kl-bar-line));
  padding-block: 30px;
  font-size: 13px;
}

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

.footer .wordmark { font-size: 18px; color: rgb(var(--kl-bar-fg) / 0.85); }

.footer-links { display: flex; gap: 22px; flex-wrap: wrap; }
.footer-links a { text-decoration: none; transition: color 0.15s ease; }
.footer-links a:hover { color: rgb(var(--kl-bar-fg)); }
.footer :focus-visible { outline-color: rgb(var(--kl-gold)); }

/* ---------- scroll reveal ---------- */

.rv {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.6s cubic-bezier(0.2, 0.7, 0.3, 1), transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; }
}

/* ---------- mobile navigation ---------- */
/* Under 860px the inline links are gone, and with the site split across pages
   they have to come back somewhere. This is the disclosure that brings them
   back: a button in the bar, a panel under it, closed by default and closed
   again on Escape. Wired in /site.js. */

.nav-toggle {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid rgb(var(--kl-bar-fg) / 0.22);
  border-radius: 999px;
  color: rgb(var(--kl-bar-fg) / 0.9);
  font-size: 13px;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.nav-toggle:hover { border-color: rgb(var(--kl-bar-fg) / 0.5); background: rgb(var(--kl-bar-fg) / 0.06); }

.nav-toggle i {
  display: block;
  width: 15px;
  height: 9px;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
}

.nav-panel {
  background: rgb(var(--kl-bar));
  border-top: 1px solid rgb(var(--kl-bar-line));
}

.nav-panel-in {
  display: grid;
  gap: 2px;
  padding-block: 10px 18px;
}

.nav-panel a {
  padding: 11px 0;
  text-decoration: none;
  font-size: 15px;
  color: rgb(var(--kl-bar-fg) / 0.72);
  border-bottom: 1px solid rgb(var(--kl-bar-line));
}
.nav-panel a:last-child { border-bottom: 0; }
.nav-panel a.is-current { color: rgb(var(--kl-gold)); }

@media (max-width: 860px) {
  .nav-toggle { display: inline-flex; }
  .nav .btn-primary { display: none; }
}
@media (min-width: 861px) {
  .nav-panel { display: none !important; }
}

/* the page you are on, marked in the bar */
.nav-links a.is-current { color: rgb(var(--kl-gold)); }

/* ---------- page hero ---------- */
/* The opening band of every page that is not the landing page. It carries the
   heading the section used to carry inline, so a page that starts halfway down
   the old scroll still starts with a title rather than with a paragraph. */

.page-hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(56px, 7vw, 96px);
}

.page-hero-inner {
  position: relative;
  display: grid;
  gap: 20px;
  max-width: 62ch;
}

.page-hero .lede { max-width: 58ch; }

.page-hero-count {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--kl-night-fg) / 0.6);
}

/* ---------- the header block ---------- */
/* The bar and the opening band under it are one dark header, and the page
   starts beneath it: paper from there down to the footer, which closes the
   frame in the same ink.

   The band redefines the section tokens instead of restating every rule that
   uses them. The hero's diagram, its eyebrow, its meta line and the stacked
   version of the diagram on a phone are all dressed in these four names, so
   they come back dark here without a second copy of any of it. Gold is the
   one thing that changes meaning as well as value: against paper it has to be
   the deep tone to be read, against ink the bright one. */

/* .cta is the opening band of the request-access page: that page's header,
   and the only place it is used, so it belongs in this group */
.hero,
.page-hero,
.cta {
  --kl-night: 26 24 20;
  --kl-night-soft: 40 37 31;
  --kl-night-line: 62 57 47;
  --kl-night-fg: 244 240 232;
  --kl-gold-deep: 227 178 60;
}

/* the ink pill the paper sections use would be a hole in this band */
.hero .btn-primary,
.page-hero .btn-primary,
.cta .btn-primary { background: rgb(var(--kl-gold)); color: rgb(var(--kl-ink)); }
.hero .btn-primary:hover,
.page-hero .btn-primary:hover,
.cta .btn-primary:hover { background: rgb(var(--kl-gold-soft)); }

.hero .btn-ghost,
.page-hero .btn-ghost,
.cta .btn-ghost {
  border-color: rgb(var(--kl-night-fg) / 0.22);
  color: rgb(var(--kl-night-fg) / 0.9);
  background: transparent;
}
.hero .btn-ghost:hover,
.page-hero .btn-ghost:hover,
.cta .btn-ghost:hover {
  border-color: rgb(var(--kl-night-fg) / 0.5);
  background: rgb(var(--kl-night-fg) / 0.06);
}

/* ---------- the pager ---------- */
/* What was a scroll is now a sequence, so the bottom of every page says where
   the sequence goes next. Two links, previous on the left and next on the
   right, and the arrow leans the way the reader is going. */

.pager {
  border-top: 1px solid rgb(var(--kl-border));
  background: rgb(var(--kl-page));
}

.pager-in {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.pager-link {
  display: grid;
  align-content: center;
  gap: 6px;
  padding: clamp(26px, 3.4vw, 40px) 0;
  text-decoration: none;
  transition: opacity 0.18s ease;
}

/* the hairline between the two, drawn by the one on the right so a page with
   only a next link carries no stray line */
.pager-next {
  text-align: right;
  border-inline-start: 1px solid rgb(var(--kl-border));
  padding-inline-start: clamp(20px, 3vw, 44px);
}
.pager-prev { padding-inline-end: clamp(20px, 3vw, 44px); }

/* a page at either end of the sequence has one link, and it sits on its own
   side rather than stretching across */
.pager-link:only-child { grid-column: 2; border-inline-start: 0; padding-inline-start: 0; }
.pager-prev:only-child { grid-column: 1; text-align: left; }

.pager-dir {
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--kl-muted));
}

.pager-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(20px, 2.4vw, 27px);
  letter-spacing: -0.02em;
  color: rgb(var(--kl-ink));
  display: flex;
  align-items: center;
  gap: 10px;
}
.pager-next .pager-name { justify-content: flex-end; }

.pager-name::before,
.pager-name::after {
  color: rgb(var(--kl-gold-deep));
  font-family: var(--font-body);
  font-size: 18px;
  transition: transform 0.18s ease;
}
.pager-prev .pager-name::before { content: '\2190'; }
.pager-next .pager-name::after { content: '\2192'; }
.pager-prev:hover .pager-name::before { transform: translateX(-4px); }
.pager-next:hover .pager-name::after { transform: translateX(4px); }

@media (max-width: 640px) {
  .pager-in { grid-template-columns: 1fr; }
  .pager-next, .pager-next .pager-name { text-align: left; justify-content: flex-start; }
  .pager-next {
    border-inline-start: 0;
    border-top: 1px solid rgb(var(--kl-border));
    padding-inline-start: 0;
  }
  .pager-prev { padding-inline-end: 0; }
  .pager-link:only-child, .pager-prev:only-child { grid-column: 1; }
}

/* ---------- the transition veil ---------- */
/* One element, two jobs, and the only full-page motion on this site.

   On the landing page it is the way in: the wordmark rises on a night ground, a
   gold hairline draws out from under it, and then the panel lifts off the page
   it was already sitting on top of.

   On every page it is also the panel that covers the page while an internal
   link is being followed, carrying the name of the page being loaded. It comes
   up from below, and on the next page it carries on upward and off, so one
   click reads as one movement rather than as two.

   HOW IT MOVES, because the shape of it is the point.

   - It travels on one axis, upward, always. Arriving and leaving are the same
     gesture continued, not two gestures pointed at each other.
   - Its leading edge is a curve, not a straight line. A flat edge crossing the
     viewport reads as a wipe; an edge that swells and then flattens reads as
     something passing. That is .veil-lip, and it is the one place on this site
     where a radius is not one of the --r tokens: it is an edge in motion, not
     a surface corner.
   - Nothing here runs under half a second, and the durations overlap. The name
     arrives before the panel has finished and leaves before the panel has
     cleared. The overlaps are the effect; squaring them up so each step waits
     for the last is what makes this kind of thing feel cheap.

   Four things about how it is built, all four load bearing.

   1. It covers the page, it never gates it. The page is in the document and
      painted underneath the whole time. Nothing waits for the veil, no content
      is withheld from a reader or a crawler, and the veil never takes focus:
      it is aria-hidden with nothing focusable inside it.
   2. CSS runs it, not JavaScript. Every animation here has a forwards or both
      fill, so a script that fails cannot leave a panel sitting on top of the
      site. With scripting off, every link is still an ordinary link.
   3. data-nav on the root element picks which animation runs, and it is set
      before first paint by the inline script in the head: one unstyled frame
      of night is worse than no animation at all. "out" is leaving, "in" is
      arriving, "done" is spent.
   4. Reduced motion removes it entirely, and the click handler in /site.js
      stands down with it, so links navigate the ordinary way. */

:root {
  /* the veil's own clock. Named for the job, because the numbers only make
     sense next to each other and the overlaps between them are the effect. */
  --veil-cover: 520ms;   /* the panel arriving over the page being left */
  --veil-clear: 880ms;   /* the panel leaving the page arrived at */
  --veil-say: 820ms;     /* a name or a mark rising into place */
  --veil-unsay: 600ms;   /* the same, going */
  --veil-lip: 920ms;     /* the curved edge swelling, or flattening out */
  /* the veil's own curves. Quartic in gathers speed into place, quartic out
     arrives fast and settles slowly, cubic in-out passes through and away. */
  --veil-in: cubic-bezier(0.895, 0.03, 0.685, 0.22);
  --veil-out: cubic-bezier(0.165, 0.84, 0.44, 1);
  --veil-through: cubic-bezier(0.645, 0.045, 0.355, 1);
  --veil-lip-h: 7vh;     /* how far the curved edge stands off the panel */
  --veil-rise: 50px;     /* how far anything on the panel travels as it appears */
}

/* the same rise across a 780px viewport reads as a lurch, so both halve */
@media (max-width: 540px) {
  :root { --veil-lip-h: 4vh; --veil-rise: 32px; }
}

.veil {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: rgb(var(--kl-veil));
  color: rgb(var(--kl-veil-fg));
  visibility: hidden;
  pointer-events: none;
  /* parked below the fold, so that even with the animations stripped out the
     panel is never sitting over the page */
  transform: translate3d(0, 100%, 0);
  /* The panel and its edges are told about the transform up front. Without
     this the browser promotes them on the first frame of the animation
     instead, and that frame is dropped: the hitch at the start of the move. */
  will-change: transform;
}

/* both children sit in the same cell. The intro and the destination name are
   never on screen together, and stacking them stops the panel reflowing. */
.veil-intro,
.veil-to {
  grid-area: 1 / 1;
  justify-self: center;
  align-self: center;
  padding-inline: var(--gut);
}

/* The curved leading and trailing edges. They are full width blocks parked
   just outside the panel and squashed flat, so the thing that animates is a
   scale on the compositor rather than a height that repaints. Lead goes first,
   so it swells as the panel comes up; trail goes last, so it flattens as the
   panel leaves. */
.veil-lip {
  position: absolute;
  inset-inline: 0;
  height: var(--veil-lip-h);
  background: rgb(var(--kl-veil));
  will-change: transform;
}
.veil-lip-lead {
  bottom: 100%;
  transform-origin: 50% 100%;
  transform: scaleY(0);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.veil-lip-trail {
  top: 100%;
  transform-origin: 50% 0;
  transform: scaleY(1);
  border-radius: 0 0 50% 50% / 0 0 100% 100%;
}
@keyframes veil-lip-swell { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes veil-lip-flatten { from { transform: scaleY(1); } to { transform: scaleY(0); } }

/* Nothing but transform and opacity is animated anywhere in this section, so
   every one of these runs on the compositor and none of them can be held up by
   whatever the main thread is doing. Visibility used to be a keyframe here and
   is now a plain state rule below: a single non-composited property in a
   keyframe list is enough to demote the whole animation to the main thread,
   which is where the stutter came from. */
@keyframes veil-cover {
  from { transform: translate3d(0, 100%, 0); }
  to { transform: translate3d(0, 0, 0); }
}
@keyframes veil-clear {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(0, -100%, 0); }
}
@keyframes veil-say {
  from { opacity: 0; transform: translate3d(0, var(--veil-rise), 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes veil-unsay { from { opacity: 1; } to { opacity: 0; } }

/* Whether the panel is on screen at all, as a plain state rule rather than as
   a keyframe. Three states show it: leaving, arriving, and the landing page's
   way in. "done" is none of them, so a spent panel is hidden and out of the
   way of hit testing. */
:root[data-nav='out'] .veil,
:root[data-nav='in'] .veil,
:root:not([data-nav]) .veil[data-intro] { visibility: visible; }

/* --- leaving: the panel comes up and the name rises with it --- */
/* The panel body gathers speed on the way in, which is the right shape for
   something arriving but leaves the first 200ms of a 520ms move almost still.
   After a click that reads as nothing having happened, so the curved edge is
   given the opposite curve and leads: it swells out of the bottom of the
   screen immediately, the body follows, and the click is answered on the
   first frame. */
:root[data-nav='out'] .veil { animation: veil-cover var(--veil-cover) var(--veil-in) both; }
:root[data-nav='out'] .veil-lip-lead { animation: veil-lip-swell var(--veil-cover) var(--veil-out) both; }
:root[data-nav='out'] .veil-to { animation: veil-say var(--veil-cover) var(--veil-out) both; }

/* --- arriving: the name goes first, then the panel carries on upward --- */
:root[data-nav='in'] .veil { animation: veil-clear var(--veil-clear) var(--veil-through) both; }
:root[data-nav='in'] .veil-lip-trail { animation: veil-lip-flatten var(--veil-lip) var(--veil-through) both; }
:root[data-nav='in'] .veil-to { animation: veil-unsay var(--veil-unsay) linear both; }

/* The name of the page being loaded. It is carried as a custom property rather
   than as a text node because on the page being arrived at it has to be on
   screen at first paint, before there is a body to write into. Empty when
   nothing set it, which is every ordinary page load.

   Georgia rather than var(--font-display), deliberately. Fraunces comes off a
   CDN and is not registered on the first frame of a new document, so a name
   set in it would paint in the fallback and then change letterforms while it
   is on screen. Georgia is the fallback the display stack already names, and
   it is there before the first frame. */
.veil-to {
  margin: 0;
  max-width: 20ch;
  text-align: center;
  text-wrap: balance;
  font-family: Georgia, 'Times New Roman', serif;
  font-weight: 400;
  font-size: clamp(30px, 4.6vw, 54px);
  line-height: 1.06;
  letter-spacing: -0.025em;
  color: rgb(var(--kl-veil-fg));
  opacity: 0;
  position: relative;
}
.veil-to::after { content: var(--nav-title, ''); }

/* --- the intro, landing page only, marked with data-intro on the veil --- */
/* The timeline in one place, so it can be read without counting delays:
     0ms        the panel is already there, with the page painted under it
     220-1040   the wordmark rises into place
     420-1180   the gold rule draws out from under it, from the centre
     560-1380   the line beneath it rises
     1500-1800  mark, rule and line fade
     1500-2380  the panel lifts, its trailing edge flattening as it goes
     2380       gone, and the page has been readable the whole time */
.veil-intro {
  display: grid;
  justify-items: center;
  gap: 18px;
  opacity: 0;
  position: relative;
}

/* once anything has set data-nav, this page has been left or returned to and
   the intro is over: what shows then is the destination name */
:root[data-nav] .veil-intro { display: none; }

:root:not([data-nav]) .veil[data-intro] {
  animation: veil-clear var(--veil-clear) var(--veil-through) 1500ms both;
}
:root:not([data-nav]) .veil[data-intro] .veil-lip-trail {
  animation: veil-lip-flatten var(--veil-lip) var(--veil-through) 1500ms both;
}
:root:not([data-nav]) .veil[data-intro] .veil-intro {
  animation: veil-say var(--veil-say) var(--veil-out) 220ms both,
             veil-unsay 300ms linear 1500ms forwards;
}

.veil-mark {
  margin: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-family: Georgia, 'Times New Roman', serif;
  font-weight: 400;
  font-size: clamp(46px, 9vw, 88px);
  letter-spacing: -0.03em;
  line-height: 1;
  color: rgb(var(--kl-veil-fg));
}
.veil-mark svg { fill: rgb(var(--kl-gold)); align-self: flex-start; }

/* the hairline. It is a block scaled to nothing from its centre, so what draws
   is a transform rather than a width that repaints. */
.veil-rule {
  width: clamp(140px, 26vw, 260px);
  height: 1px;
  background: rgb(var(--kl-gold));
  transform: scaleX(0);
  transform-origin: 50% 50%;
}
@keyframes veil-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
:root:not([data-nav]) .veil[data-intro] .veil-rule {
  animation: veil-draw 760ms var(--veil-out) 420ms both;
}

.veil-line {
  /* a shorter rise than the group it sits inside, or the two movements add up
     and the line appears to overshoot the mark above it */
  --veil-rise: 24px;
  margin: 0;
  text-align: center;
  color: rgb(var(--kl-gold-soft) / 0.8);
  opacity: 0;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
:root:not([data-nav]) .veil[data-intro] .veil-line {
  animation: veil-say var(--veil-say) var(--veil-out) 560ms both;
}

/* Nothing else on the page may animate while the panel is moving. Two things
   were, and both of them showed up as a stutter in the lift.

   The first is the smooth scroll. Following a link to /#demo from another page
   lands the new document at an anchor most of the way down it, and with smooth
   scrolling on, the browser animates that whole distance on the main thread
   while the panel is being lifted off. The panel wins the compositor and the
   scroll wins the frame budget. Arriving is instant; once the panel is spent,
   data-nav is "done" and ordinary smooth scrolling is back. */
:root[data-nav='in'] { scroll-behavior: auto; }

/* The second is the scroll reveal. Everything above the fold of the page being
   arrived at would otherwise fade and rise underneath the panel, where nobody
   can see it, for the exact 880ms the panel needs. /site.js marks those
   elements with this class instead, and they are simply already there. */
.rv.rv-settled { transition: none; }

/* Reduced motion takes the panel away entirely. The click handler in /site.js
   checks the same query and stands down with it, so links navigate the
   ordinary way and nothing here is ever on screen. */
@media (prefers-reduced-motion: reduce) {
  .veil { display: none !important; }
}

/* ---------- the margin rail ---------- */
/* The browser's scrollbar, replaced by the one thing this site is about: a
   reader working down a page that has been read for them.

   It is drawn as a book's margin rule rather than as a bar. A hairline down the
   fore edge of the page, a tick where each section begins, gold ink filling in
   behind you as you read, and the wordmark's own spark riding the line as the
   handle. The spark turns a quarter as the page goes by, so the mark at the top
   of the page and the mark in the margin are the same object seen at two points
   in one movement.

   Everything drawn here is gold, and that is load bearing rather than decorative:
   the rail crosses paper bands and the dark footer in one unbroken line, and gold
   is the one tone on this site that holds against both. Nothing here has to know
   what it is passing over.

   Built by /site.js, which also sets data-rail on the root element. The native
   bar is only taken away once that attribute is there: with scripting off, the
   browser's own scrollbar is the only thing saying how long the page is, and it
   stays. */

html[data-rail] { scrollbar-width: none; }
html[data-rail]::-webkit-scrollbar { width: 0; height: 0; }

/* a drag is a scroll, not a selection of everything the pointer crosses */
html[data-rail-drag] { user-select: none; -webkit-user-select: none; }

.rail {
  --p: 0;                       /* how far down the page, 0 to 1, set on scroll */
  position: fixed;
  right: 12px;
  /* clear of the sticky bar at the top and off the very bottom edge, so the
     line reads as a margin inside the page rather than as browser chrome */
  top: calc(var(--bar) + 26px);
  bottom: 26px;
  width: 24px;                  /* the hit area. Everything drawn in it is thinner. */
  z-index: 40;                  /* under the bar, and under the transition veil */
  cursor: pointer;
  touch-action: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}

/* set once the page has settled: on the landing page the panel is still on its
   way off, and a line fading in underneath it is movement nobody asked for */
.rail[data-on] { opacity: 1; }
.rail[data-drag] { cursor: grabbing; }

/* the sheet under 860px holds the page still and dims it. The rail is a control
   for a page that cannot move, so it goes with the rest of the page. */
.nav[data-open] ~ .rail { opacity: 0; pointer-events: none; }

/* the rule itself */
.rail-track {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  transform: translateX(-50%);
  background: rgb(var(--kl-gold-deep) / 0.26);
}

/* what has been read. It fades in from the top rather than starting at full
   strength, because the top of the page is where you came in, not something
   you did. */
.rail-read {
  position: absolute;
  left: 50%;
  top: 0;
  width: 2px;
  height: calc(var(--p) * 100%);
  transform: translateX(-50%);
  border-radius: 999px;
  background: linear-gradient(
    to bottom,
    rgb(var(--kl-gold-deep) / 0.18),
    rgb(var(--kl-gold-deep) / 0.85)
  );
}

/* one per section, at the point on the rail where that section reaches the bar */
.rail-tick {
  position: absolute;
  left: 50%;
  top: calc(var(--at) * 100%);
  width: 7px;
  height: 1px;
  transform: translate(-50%, -50%);
  background: rgb(var(--kl-gold-deep) / 0.34);
  transition: width 0.2s ease, background-color 0.3s ease;
}
.rail-tick[data-read] { background: rgb(var(--kl-gold-deep) / 0.8); }
.rail:hover .rail-tick,
.rail[data-drag] .rail-tick { width: 11px; }

/* the handle: the wordmark's spark, on the line */
.rail-mark {
  position: absolute;
  left: 50%;
  top: calc(var(--p) * 100%);
  width: 15px;
  height: 15px;
  transform: translate(-50%, -50%) rotate(calc(var(--p) * 90deg));
  filter: drop-shadow(0 0 5px rgb(var(--kl-gold) / 0.5));
  transition: width 0.2s ease, height 0.2s ease;
}
.rail-mark svg { display: block; width: 100%; height: 100%; fill: rgb(var(--kl-gold-deep)); }
.rail:hover .rail-mark,
.rail[data-drag] .rail-mark { width: 19px; height: 19px; }

/* the name of the section the spark is sitting in, on hover and through a drag */
.rail-label {
  position: absolute;
  right: 30px;                  /* clear of the spark, which is 24px off the edge */
  top: calc(var(--p) * 100%);
  white-space: nowrap;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--kl-ink) / 0.78);
  background: rgb(var(--kl-card));
  border: 1px solid rgb(var(--kl-border));
  border-radius: 999px;
  padding: 5px 11px;
  box-shadow: 0 6px 18px rgb(var(--kl-ink) / 0.12);
  pointer-events: none;
  opacity: 0;
  transform: translate(6px, -50%);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.rail:hover .rail-label,
.rail[data-drag] .rail-label { opacity: 1; transform: translate(0, -50%); }

/* A finger scrolls the page directly and has no scrollbar to replace, so on a
   touch screen the rail stops being a control and is only the reading line. It
   also stops being 24px of dead margin down the side of a phone. */
@media (hover: none), (max-width: 700px) {
  .rail {
    right: 7px;
    width: 12px;
    top: calc(var(--bar) + 18px);
    bottom: 18px;
    pointer-events: none;
  }
  .rail-label { display: none; }
  .rail-mark { width: 12px; height: 12px; }
}

/* the spark still rides the line, it just stops turning */
@media (prefers-reduced-motion: reduce) {
  .rail-mark { transform: translate(-50%, -50%); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE PHONE
   ═══════════════════════════════════════════════════════════════════════════

   Everything above this line was drawn for a wide screen and then allowed to
   collapse. Collapsing is enough for a paragraph and for a row of cards. It is
   not enough for the three things this site is actually made of, and each of
   them is rebuilt here rather than shrunk:

     the machine in the hero   one wide picture, unreadable at a third size, so
                               a stacked version of the same sentence takes over
     the live map              a constellation of 26 dots at four pixels each,
                               so it is opened zoomed in, moved with a finger,
                               and given tap targets a thumb can actually hit
     the study view            three columns that became three nested scrollers
                               stacked on each other, so it becomes one page
                               with two disclosures under it

   The breakpoints, and why each one is where it is:

     1040px   the study view stopped being three columns, so it becomes a page
              with disclosures instead
      980px   the demo's two panes stopped being two panes (declared above)
      860px   the inline navigation is gone, so the disclosure is a real sheet
      760px   a handheld: the hero diagram, the product frames, the type
      700px   the map is small enough to need zooming rather than fitting
      560px   the tightest width worth designing for, 320px upward
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------- the machine, stacked ----------
   Off at every width where the drawn diagram is legible. The two carry the same
   description and only ever one of them is in the document, so a screen reader
   is never told about the diagram twice. */

.flow { display: none; }

/* ---------- 1040px · the study view becomes a page ---------- */

@media (max-width: 1040px) {
  /* The centre panel is the whole point: it is the page out of the book, and
     the two rails are what that page is linked to. In a row the eye finds the
     middle first; in a stack it finds the top, and the top was the lecture
     rail. So the page goes first, then the questions asked about it, then the
     lecture that teaches it. Source order stays as it reads on a wide screen. */
  .study-page { order: 1; }
  .study-questions { order: 2; }
  .study-lecture { order: 3; }

  /* :last-child is the questions column, which is no longer the last thing on
     screen. The hairline is put back where it is now wanted. */
  .study-col,
  .study-col:last-child { border-bottom: 1px solid rgb(var(--kl-night-line)); }
  .study-lecture { border-bottom: 0; }

  /* A panel that scrolls inside a page that scrolls is a trap on a touch
     screen: the finger lands on the inner box, the page stops moving, and the
     reader thinks the site has hung. Every one of these becomes ordinary
     content, and the disclosures below keep the section from running away. */
  .study .col-body {
    max-height: none;
    overflow: visible;
  }

  .study { min-height: 0; }

  /* the disclosure, built in /app.js: the head of each rail becomes the button
     that opens it. The page column is never one of them. */
  .col-fold {
    width: 100%;
    text-align: left;
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
    transition: background-color 0.16s ease;
  }
  .col-fold:hover { background: rgb(var(--kl-night-fg) / 0.03); }
  .col-fold:active { background: rgb(var(--kl-night-fg) / 0.06); }

  .col-chev {
    flex: none;
    width: 9px;
    height: 9px;
    margin-left: 2px;
    border-right: 1.5px solid rgb(var(--kl-night-fg) / 0.45);
    border-bottom: 1.5px solid rgb(var(--kl-night-fg) / 0.45);
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.2s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .col-fold[aria-expanded='true'] .col-chev { transform: translateY(1px) rotate(-135deg); }

  /* the count goes to the far side and leaves the chevron the end of the row */
  .col-fold .col-count { margin-left: auto; }

  .col-fold-body[hidden] { display: none; }
}

/* Above the stack the disclosure has nothing to disclose: the head goes back to
   being a heading, and its wrapper stops being a box in the layout. */
@media (min-width: 1041px) {
  .col-fold { pointer-events: none; }
  .col-chev { display: none; }
  .col-fold-body { display: contents; }
}

/* ---------- 860px · the navigation sheet ---------- */
/* The panel used to be a block that pushed the page down. Under the thumb that
   reads as the page having jumped rather than as a menu having opened, and what
   it pushes away is the thing you were reading. It hangs off the bar now, over
   a dimmed page, and takes the page's scroll for as long as it is open. */

@media (max-width: 860px) {
  /* the wordmark is a link home and it is twenty pixels tall, which is a
     letterform rather than a target. The box around it grows; the letters
     do not. */
  .nav .wordmark,
  .footer .wordmark { padding-block: 12px; }

  .nav-toggle {
    padding: 10px 16px;
    min-height: 42px;
    gap: 9px;
  }
  .nav-toggle:active { background: rgb(var(--kl-bar-fg) / 0.1); }

  /* the two rules become an X, on the same element, with no second icon */
  .nav-toggle i {
    position: relative;
    height: 10px;
    border: 0;
  }
  .nav-toggle i::before,
  .nav-toggle i::after {
    content: '';
    position: absolute;
    left: 0;
    width: 15px;
    height: 1px;
    background: currentColor;
    transition: transform 0.24s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .nav-toggle i::before { top: 0; }
  .nav-toggle i::after { bottom: 0; }
  .nav-toggle[aria-expanded='true'] i::before { transform: translateY(4.5px) rotate(45deg); }
  .nav-toggle[aria-expanded='true'] i::after { transform: translateY(-4.5px) rotate(-45deg); }

  .nav-panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    max-height: calc(100dvh - var(--bar));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 30px 50px -24px rgb(34 31 26 / 0.26);
    animation: sheet-down 0.28s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  }

  @keyframes sheet-down {
    from { opacity: 0; transform: translate3d(0, -10px, 0); }
    to { opacity: 1; transform: none; }
  }

  /* the page behind it, dimmed. Painted from the bar down, so the bar stays at
     full strength and the sheet reads as hanging from it. */
  .nav[data-open]::after {
    content: '';
    position: fixed;
    inset: var(--bar) 0 0 0;
    background: rgb(var(--kl-bar) / 0.62);
    z-index: -1;
    animation: scrim 0.28s ease both;
  }
  @keyframes scrim { from { opacity: 0; } to { opacity: 1; } }

  .nav-panel-in { padding-block: 6px 20px; }

  .nav-panel a {
    display: flex;
    align-items: center;
    min-height: 52px;
    padding: 12px 2px;
    font-size: 16px;
  }
  .nav-panel a:active { color: rgb(var(--kl-bar-fg)); }

  /* Request access is the one thing on this site anybody is being asked to do,
     and at this width it is hidden in the bar. It comes back here as itself
     rather than as the fifth line of a list. */
  .nav-panel a:last-child {
    justify-content: center;
    margin-top: 16px;
    padding: 15px 22px;
    border-bottom: 0;
    border-radius: 999px;
    background: rgb(var(--kl-gold));
    color: rgb(var(--kl-ink));
    font-weight: 500;
  }
  .nav-panel a:last-child:active { background: rgb(var(--kl-gold-soft)); }
}

/* ---------- 760px · the handheld ---------- */

@media (max-width: 760px) {
  .band { padding-block: clamp(54px, 12vw, 84px); }
  .band-tight { padding-block: clamp(44px, 9vw, 64px); }

  /* every control on the page clears the 44px a thumb needs */
  .btn {
    padding: 13px 22px;
    min-height: 46px;
    font-size: 14.5px;
  }

  /* the eyebrow's two rules and its text on one line at 320px, which is what
     the tracking and the size here are for */
  .eyebrow {
    font-size: 10.5px;
    letter-spacing: 0.1em;
    gap: 8px;
  }
  .eyebrow::before,
  .hero .eyebrow::after { width: 16px; }

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

  .hero { padding-block: 44px 0; }
  .hero-inner { gap: 18px; }
  .h-xl { font-size: clamp(32px, 8.6vw, 44px); line-height: 1.06; }
  .hero h1 { max-width: 17ch; text-wrap: balance; }
  .lede { font-size: 16px; text-wrap: pretty; }
  .hero .lede { max-width: 34ch; }

  /* two buttons, one row, the same width each: nothing to aim at */
  .hero .btn-row { width: 100%; gap: 10px; }
  .hero .btn-row .btn { flex: 1 1 0; justify-content: center; padding-inline: 12px; }

  /* Both of these wrap at this width, and a wrapped line under a centred
     bullet leaves the bullet stranded beside the middle of a paragraph. They
     become a short list instead: bullets down one edge, text ranged left off
     them, and the block as a whole still centred under the buttons. */
  .hero-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 7px;
    width: fit-content;
    margin-inline: auto;
    font-size: 12px;
    text-align: left;
  }
  .hero-meta span {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 8px;
  }
  .hero-meta span::before { margin-top: 7px; }

  /* ---- the section bodies ---- */

  .section-head { gap: 13px; }
  .paper, .step { padding: 20px 18px; }
  .paper p, .step p, .card p { font-size: 15px; }
  .trio { margin-top: 32px; gap: 12px; }
  .steps { margin-top: 34px; }
  .split { margin-top: 32px; gap: 14px; }
  .rows { margin-top: 36px; }
  .facts { margin-top: 32px; }
  .fact { padding: 18px 16px; }
  .fact span { font-size: 12.5px; }
  .row-list li { font-size: 15px; }

  /* ---- the coded product screens ----
     A whole screen, whole, however small that makes it. The floor in the base
     rule stops the type going under five and a half pixels, and a floor is the
     one thing a frame like this cannot have: the screen is one drawing scaled
     by a single number, so the moment that number stops falling the drawing
     stops fitting and the right hand third of it is simply cut off the edge.
     The floor comes off here and the frame is scaled to the width it has. It
     is small. It is the whole thing, and it is not something to be dragged
     around behind a window: what these are for at this size is the shape of
     the screen, and the shape is what survives. */

  .ui { font-size: calc(100cqi / var(--ui-w)); }

  /* ---- faq ---- */

  .faq { margin-top: 30px; }
  .faq summary { padding: 17px 40px 17px 0; font-size: 18px; }
  .faq summary::after { right: 6px; }
  .faq summary:active { color: rgb(var(--kl-gold-deep)); }
  .faq .answer { padding: 0 4px 20px 0; font-size: 15px; }

  /* ---- pager and footer ---- */

  .pager-link { padding-block: 24px; gap: 4px; }
  .pager-name { font-size: 21px; }

  .footer { padding-block: 26px calc(26px + env(safe-area-inset-bottom)); }
  .footer-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }
  .footer-links {
    gap: 0 20px;
    width: 100%;
  }
  .footer-links a { padding-block: 9px; }
}

/* ---------- 900px · the machine, stacked ----------
   Not 760. The drawn diagram is 1120 units wide and its labels are set at 11.5
   of them, so the size they arrive at is entirely a matter of how much width
   the shell has to give it: at a 900px viewport the scale is 0.76 and those
   labels land under nine pixels, which is a diagram you can see the shape of
   and cannot read. This is the width at which the stacked version is the
   better of the two, tablet included, rather than the width of a phone. */

@media (max-width: 900px) {
  /* ---- the machine, stacked ---- */

  .stage { display: none; }

  /* It grows a little on the wider half of this range, so a tablet gets a
     stack that was drawn for it rather than a phone's stack floated in the
     middle of a lot of empty band. */
  .flow {
    display: grid;
    justify-items: center;
    width: 100%;
    max-width: min(100%, 460px);
    margin-inline: auto;
  }

  .stage-wrap { margin-top: 34px; padding-bottom: 44px; }
  .stage-caption { margin-top: 16px; font-size: 12.5px; }

  /* three equal boxes, so the row ends where the band ends and there is no
     column of unused ground down one side of it */
  .flow-in {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    width: 100%;
  }

  .flow-src {
    display: grid;
    justify-items: center;
    align-content: start;
    gap: 5px;
    padding: 14px 6px 12px;
    text-align: center;
    background: rgb(var(--kl-night-soft));
    border: 1px solid rgb(var(--kl-night-line));
    border-radius: var(--r-md);
    animation: breathe 3.6s ease-in-out infinite;
  }
  .flow-src:nth-child(2) { animation-delay: 0.9s; }
  .flow-src:nth-child(3) { animation-delay: 1.8s; }

  .flow-ic { width: 21px; display: grid; margin-bottom: 2px; }
  .flow-ic svg { width: 100%; height: auto; }
  .flow-t { font-size: 13.5px; line-height: 1.2; color: rgb(var(--kl-night-fg)); }
  .flow-s { font-size: 11px; line-height: 1.2; color: rgb(var(--kl-night-fg) / 0.55); }

  /* the three feed lines, converging */
  .flow-merge { width: 100%; height: auto; display: block; }
  .flow-merge-line path {
    fill: none;
    stroke: rgb(var(--kl-night-line));
    stroke-width: 1;
  }
  .flow-merge-beam use {
    fill: none;
    stroke: rgb(var(--kl-gold-deep));
    stroke-width: 1.8;
    stroke-linecap: round;
    /* 10 units of beam in a 130 unit cycle over a path normalised to 100: one
       clean pass, on and off the line at either end, per repeat */
    stroke-dasharray: 10 120;
    stroke-dashoffset: 130;
    animation: flow-travel 2.6s linear infinite;
    animation-delay: var(--d, 0s);
  }
  @keyframes flow-travel { to { stroke-dashoffset: 0; } }

  /* the beam, going down the screen instead of across it */
  .flow-join {
    display: block;
    position: relative;
    width: 1px;
    height: 26px;
    background: rgb(var(--kl-night-line));
  }
  .flow-join i {
    position: absolute;
    top: -6px;
    left: -0.5px;
    width: 2px;
    height: 7px;
    border-radius: 999px;
    background: rgb(var(--kl-gold-deep));
    animation: flow-drop 2.6s cubic-bezier(0.6, 0, 0.4, 1) infinite;
  }
  .flow-join:last-of-type i { animation-delay: 0.55s; }

  @keyframes flow-drop {
    0% { transform: translateY(0); opacity: 0; }
    14% { opacity: 1; }
    62% { opacity: 1; }
    76%, 100% { transform: translateY(32px); opacity: 0; }
  }

  .flow-core {
    position: relative;
    display: grid;
    align-content: center;
    justify-items: center;
    gap: 3px;
    /* smaller than it was, because the three boxes above it are now small: a
       138px block over a row of 89px ones reads as the diagram being about
       Klaro rather than about what goes into it */
    width: 122px;
    height: 122px;
    border-radius: 26px;
    /* Dressed in the band's own four names and nothing else. The hero, the
       page hero and the closing band redefine those names for the ground they
       are on, so this box is the right way round whichever ground it lands on;
       a literal tone here, or a token the band does not redefine, is a bright
       box on a dark band the first time the two swap over. */
    background: rgb(var(--kl-night-soft));
    border: 1px solid rgb(var(--kl-gold-deep) / 0.55);
    box-shadow: 0 0 90px 10px rgb(var(--kl-gold) / 0.12);
  }
  .flow-core::before {
    content: '';
    position: absolute;
    inset: 9px;
    border-radius: 18px;
    border: 1px solid rgb(var(--kl-night-line));
  }

  .flow-mark {
    display: inline-flex;
    align-items: flex-start;
    gap: 2px;
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 25px;
    line-height: 1;
    letter-spacing: -0.025em;
    color: rgb(var(--kl-night-fg));
  }
  .flow-sub { font-size: 10.5px; color: rgb(var(--kl-night-fg) / 0.62); }

  .flow-out {
    width: 100%;
    padding: 14px 16px 11px;
    background: rgb(var(--kl-night-soft));
    border: 1px solid rgb(var(--kl-night-line));
    border-radius: var(--r-lg);
  }
  .flow-out svg { width: 100%; height: auto; }
  .flow-cap { margin-top: 2px; font-size: 12px; color: rgb(var(--kl-night-fg) / 0.62); }
}

/* ---------- 760px · the demo frame ---------- */

@media (max-width: 760px) {
  .demo-frame { margin-top: 28px; border-radius: var(--r-lg); }

  /* The bar had four things on one line and wrapped them into a shape nobody
     drew: a title broken across two lines with its page count stranded on the
     right of the first one. Three deliberate rows instead. */
  .demo-bar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
  }
  .demo-dot { grid-column: 1; grid-row: 1; }
  .demo-doc {
    grid-column: 2;
    grid-row: 1;
    display: block;
    font-size: 12.5px;
    line-height: 1.45;
  }
  .demo-doc strong { display: inline; }

  /* the view switch is the one control in here, so it gets the whole width */
  .seg {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
  .seg-btn {
    padding: 10px 8px;
    font-size: 13px;
    text-align: center;
  }

  .demo-hint {
    grid-column: 1 / -1;
    margin-left: 0;
    font-size: 11.5px;
  }

  /* ---- the page out of the book ---- */

  .study-page .col-body { padding: 0; }
  .page-body { padding: 12px 10px 18px; }
  .page-sheet {
    padding: 22px 18px 24px;
    gap: 12px;
    border-radius: var(--r-md);
  }
  .page-sheet h4 { font-size: 23px; }
  /* 15px, not 14px. This is the one block of continuous prose on the site, and
     it is the thing the whole demo is about. */
  .page-sheet p { font-size: 15px; line-height: 1.72; }
  .para { padding: 9px 11px; margin-inline: -11px; }
  .para:active { background: rgb(var(--kl-surface) / 0.8); }
  .para-dots { gap: 8px; }

  .col-head { padding: 14px 16px; min-height: 52px; }
  .col-body { padding: 12px 14px 18px; }
  .col-note { padding: 10px 16px 0; }

  .seg-card p { font-size: 13px; }
  .item-prose { font-size: 14px; }
  .opt { font-size: 13.5px; padding: 9px 11px; }

  /* ---- the detail pane, under the map ---- */

  .detail-head { padding: 18px 16px 16px; }
  .detail-head h3 { font-size: 22px; }
  .detail-scroll {
    max-height: none;
    padding: 16px 16px 24px;
  }

  /* the lenses run off the edge at this width, so they scroll rather than wrap
     into a second row that pushes the panel down */
  .lens-tabs {
    padding: 8px 12px 0;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-padding-inline: 12px;
  }
  .lens-tabs::-webkit-scrollbar { display: none; }
  .lens-tab {
    flex: none;
    padding: 11px 12px 12px;
    font-size: 13px;
  }
}

/* ---------- 700px · the map, zoomed ---------- */
/* Fitted to a phone, twenty-six concepts and their labels come out at four
   pixels each: a picture of a map rather than a map. So it opens at a scale its
   labels can be read at, centred on the concept that is open, and moves with a
   finger. /app.js adds the gestures and the two buttons; it also widens every
   node's hit area, because a six pixel dot is not a target.

   The pane stops being a box with things floating on top of it: the filters and
   the key become rows above and below the map, where they cover nothing. */

@media (max-width: 700px) {
  .graph-pane {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  /* a stated height rather than a share of what is left, so the map's own
     window keeps one shape while it is being zoomed and moved */
  #graph {
    flex: none;
    height: min(58vh, 420px);
  }

  /* the invisible disc under every dot, built in /app.js. A six pixel target
     is a miss; this is one a thumb lands on. */
  .node-hit { fill: transparent; }

  .graph-filters {
    position: static;
    order: -1;
    justify-content: flex-start;
    gap: 8px;
    padding: 12px 14px 4px;
  }

  .chip {
    padding: 8px 14px;
    min-height: 38px;
    font-size: 12.5px;
    background: rgb(var(--kl-night) / 0.6);
  }

  .graph-legend {
    position: static;
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    padding: 10px 14px 14px;
    border-top: 1px solid rgb(var(--kl-night-line));
    font-size: 11.5px;
  }

  /* the labels are in the map's own units, so they grow with the zoom: this is
     the size they read at once /app.js has opened the map in close */
  .node-label { font-size: 12px; }

  /* the two zoom buttons, built in /app.js and put at the end of the filter row */
  .graph-zoom {
    display: inline-flex;
    margin-left: auto;
    gap: 6px;
  }
  .zoom-btn {
    width: 38px;
    height: 38px;
    display: grid;
    place-items: center;
    border-radius: 999px;
    border: 1px solid rgb(var(--kl-night-fg) / 0.18);
    background: rgb(var(--kl-night) / 0.6);
    color: rgb(var(--kl-night-fg) / 0.8);
  }
  .zoom-btn:active { background: rgb(var(--kl-night-fg) / 0.12); }
  .zoom-btn svg {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
  }
}

/* ---------- 560px · the tightest width ---------- */

@media (max-width: 560px) {
  :root { --gut: 18px; }

  .nav-inner { gap: 12px; }
  .nav .wordmark { font-size: 20px; }

  .h-lg { font-size: clamp(26px, 7.4vw, 34px); }
  .page-hero { padding-block: 44px; }

  .demo-frame { border-radius: var(--r-md); }
  .page-sheet { padding: 20px 16px 22px; }
  .page-body { padding: 10px 8px 16px; }

  .footer-links { gap: 0 16px; }
  /* two across, not one. Four figures in a column is a longer strip that says
     the same thing, and the pairing is part of how they read. */
  .fact { padding: 16px; }
  .fact b { font-size: 24px; }
}

/* ---------- the phone, with the motion turned down ---------- */

@media (prefers-reduced-motion: reduce) {
  .flow-src, .flow-join i, .flow-merge-beam use { animation: none; }
  .flow-merge-beam { display: none; }
  .flow-join i { opacity: 0; }
  .nav-panel, .nav[data-open]::after { animation: none; }
}
