/* ================================================================
   Temper — the skill forge.
   Same workbench skin as everything else: one mono face, hairline
   borders, flat panels, the single 2px radius, no gradients. The
   portraits are the only images on the page, so they get the one
   piece of visual weight and nothing else competes with them.
   ================================================================ */

.tp-wrap { max-width: 1180px; margin: 0 auto; padding-top: 6px; }

/* ---------------- hero ---------------- */
.tp-hero {
  display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.tp-copy h2 { margin: 0 0 4px; font-size: 1.05rem; text-transform: lowercase; }
.tp-copy h2::before { content: ":: "; color: var(--acc); }
.tp-copy p { margin: 0; color: var(--muted); max-width: 64ch; font-family: var(--serif); font-size: 0.98rem; }
.tp-today { display: flex; margin-left: auto; border: 1px solid var(--line-2); }
.tp-today span {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 7px 14px;
  border-right: 1px solid var(--line);
  font-size: 0.58rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
  white-space: nowrap;
}
.tp-today span:last-child { border-right: none; }
.tp-today b { font-size: 1.05rem; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ---------------- the quote wall ---------------- */
.tp-quote {
  display: flex; align-items: center; gap: 14px;
  margin: 0 0 4px;
  padding: 12px 15px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--acc);
  background: var(--bg-2);
}
.tp-quote p { margin: 0; font-family: var(--serif); font-size: 1.02rem; font-style: italic; color: var(--ink); }
.tp-quote cite { display: block; margin-top: 4px; font-size: 0.66rem; font-style: normal; color: var(--muted); }

/* ---------------- portraits ----------------
   One tile, two layers: initials underneath, picture on top when the
   corpus had one. The <img> is what gets removed on error, so a dead
   URL falls back to the monogram instead of a broken-image glyph. */
.face {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--fs, 48px); height: var(--fs, 48px);
  flex: 0 0 auto;
  border: 1px solid var(--line-2);
  background: var(--bg-3);
  overflow: hidden;
}
.face .face-mono {
  font-size: calc(var(--fs, 48px) * 0.34);
  font-weight: 700; letter-spacing: 0.04em;
  color: var(--muted);
}
.face img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 22%; /* faces sit high in a portrait */
  /* Was grayscale(0.35). Stacked on the 0.4 opacity the rung strip used to
     carry, portraits read as half-loaded placeholders rather than as people —
     which is exactly what they looked like. The tinting now lives on the rung,
     not on every portrait in the app. */
}
.face.big { border-color: var(--acc); }

/* ---------------- discipline cards ---------------- */
.tp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 10px;
}
.tp-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line-2);
  background: var(--bg);
  padding: 13px 15px 11px;
  transition: border-color var(--fast);
}
.tp-card:hover { border-color: var(--muted); }

.tc-head {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer;
  margin: -13px -15px 0; padding: 13px 15px 6px; /* the whole strip is the target */
}
.tc-head:hover h3 { color: var(--acc); }
.tc-head:hover .tc-num::after { content: " ↗"; color: var(--acc); }
.tc-head h3 { margin: 0; font-size: 0.86rem; text-transform: lowercase; }
.tc-head .ic { color: var(--acc); }
.tc-num { font-size: 0.6rem; color: var(--muted); opacity: 0.6; }
.tc-rank {
  margin-left: auto;
  font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--acc);
  border: 1px solid var(--line-2);
  padding: 1px 6px;
}
.tc-rank.untested { color: var(--muted); }
.tc-blurb {
  margin: 6px 0 11px;
  font-family: var(--serif); font-size: 0.92rem;
  color: var(--ink-2);
}

/* current rung -> next rung */
.tc-now { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
/* both sides shrink, and every box down to the <b> needs min-width:0 or the
   ellipsis never engages — a long name (SpongeBob SquarePants) would otherwise
   run straight over the rung beside it */
.tc-who { display: flex; align-items: center; gap: 9px; min-width: 0; flex: 1 1 0; }
.tc-who > div { min-width: 0; }
.tc-who b {
  display: block;
  font-size: 0.78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tc-who-label {
  display: block;
  font-size: 0.55rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted);
}
.tc-who.next { justify-content: flex-end; text-align: right; flex-direction: row-reverse; }
.tc-who.next.hidden { visibility: hidden; }
.tc-arrow { color: var(--line-2); font-size: 0.9rem; }

/* the bar itself */
.rankbar {
  position: relative;
  height: 4px;
  background: var(--bg-3);
  border: 1px solid var(--line);
}
.rankbar > i { display: block; height: 100%; background: var(--acc); transition: width 420ms cubic-bezier(0.22, 1, 0.36, 1); }
.rankbar.big { height: 7px; }
/* the peak tick — the best single run you've had, which is deliberately NOT
   where the solid bar ends. Filled bar = what you hold, tick = what you've
   touched once. */
.rankbar > u {
  position: absolute; top: -2px; bottom: -2px;
  width: 2px; margin-left: -1px;
  background: var(--ink-2);
  transition: left 420ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* six rungs, faces and all. A rung you haven't reached is dimmed by
   DESATURATION, not by fading to 40% — the point of the strip is to show you
   who is ahead of you, and you can't want to become someone you can't see. */
.tc-rungs { display: flex; gap: 5px; margin: 8px 0 11px; }
.tc-rung {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  transition: opacity var(--fast);
}
.tc-rung .face img { filter: grayscale(0.85) contrast(0.95); opacity: 0.8; transition: filter var(--fast), opacity var(--fast); }
.tc-rung.on .face img { filter: grayscale(0.25); opacity: 1; }
.tc-rung.here .face img { filter: none; opacity: 1; }
.tc-rung.here .face { border-color: var(--acc); box-shadow: 0 0 0 1px var(--acc); }
.tc-rung:hover .face img { filter: none; opacity: 1; }
.tc-rung i {
  font-style: normal; font-size: 0.52rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.tc-rung.here i { color: var(--acc); }

/* The rung you actually hold breathes. It is the one live thing on a card that
   is otherwise sitting still, and it breathes on the RING rather than on the
   portrait — pulsing the picture's opacity reads as an image still loading,
   which is the opposite of alive. */
.tc-rung.here .face::after,
.dv-rung.here .face::after {
  content: "";
  position: absolute; inset: 0;
  box-shadow: inset 0 0 0 1px var(--acc);
  pointer-events: none;
  animation: breathe 3.4s steps(1) infinite;
}

/* menu-item press: one hard increment, no travel between states */
.tc-drill:hover { transform: translate(-1px, -1px); }
.tc-drill:active { transform: translate(1px, 1px); }
.tc-drill { transition: background var(--fast), color var(--fast), border-color var(--fast); }

/* drill launchers */
.tc-drills { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 9px; }
.tc-drill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 9px;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--bg-2); color: var(--ink-2);
  font-size: 0.7rem; font-family: var(--mono);
  transition: background var(--fast), color var(--fast), border-color var(--fast);
}
.tc-drill:hover { background: var(--acc); color: var(--acc-ink); border-color: var(--acc); }
.tc-drill b { font-variant-numeric: tabular-nums; color: var(--acc); }
.tc-drill:hover b { color: var(--acc-ink); }

.tc-foot {
  display: flex; align-items: center; gap: 6px;
  margin-top: auto; padding-top: 8px;
  border-top: 1px solid var(--line);
  font-size: 0.62rem; color: var(--muted);
}
.tc-foot .spacer { flex: 1; }
.tc-mini {
  display: inline-flex; align-items: center;
  padding: 3px 6px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: transparent; color: var(--muted);
}
.tc-mini:hover { color: var(--acc); border-color: var(--acc); }
.tc-mini.danger:hover { color: var(--bad); border-color: var(--bad); }

/* ================================================================
   Discipline detail — the card opened up. The portraits are the
   reason this view exists, so they get the space: 200px for the rung
   you're on, 108 for the ladder, both in full colour.
   ================================================================ */
.dv { display: flex; flex-direction: column; gap: 4px; }
.dv-head { display: flex; align-items: center; gap: 10px; }
.dv-head h2 { margin: 0; font-size: 1rem; text-transform: lowercase; }
.dv-head h2::before { content: ":: "; color: var(--acc); }
.dv-head .tc-rank { margin-left: 0; }
.dv-head .drill-x { margin-left: auto; }

.dv-hero { display: flex; gap: 20px; align-items: flex-start; margin: 12px 0 4px; }
.dv-hero .face { border-width: 1px; }
.dv-hero-copy { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.dv-hero-copy b { font-size: 1.5rem; line-height: 1.15; }
.dv-hero-copy .dv-sub { font-size: 0.66rem; color: var(--muted); }
.dv-hero-copy p {
  margin: 6px 0 10px;
  font-family: var(--serif); font-size: 1rem; color: var(--ink-2);
  max-width: 56ch;
}
.dv-hold { font-size: 0.66rem; color: var(--muted); margin-top: 6px; }

.dv-ladder { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; }
.dv-rung { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.dv-rung .face { width: 100%; height: auto; aspect-ratio: 3 / 4; }
.dv-rung .face img { filter: grayscale(0.8); opacity: 0.82; transition: filter var(--fast), opacity var(--fast); }
.dv-rung.on .face img { filter: grayscale(0.2); opacity: 1; }
.dv-rung.here .face img, .dv-rung:hover .face img { filter: none; opacity: 1; }
.dv-rung.here .face { border-color: var(--acc); box-shadow: 0 0 0 1px var(--acc); }
.dv-rung figcaption { display: flex; flex-direction: column; gap: 1px; min-width: 0; width: 100%; }
.dv-rung figcaption i {
  font-style: normal; font-size: 0.52rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.dv-rung.here figcaption i { color: var(--acc); }
.dv-rung figcaption b { font-size: 0.72rem; line-height: 1.25; }
.dv-rung figcaption span {
  font-size: 0.58rem; color: var(--muted); line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.dv-drills { display: flex; flex-direction: column; gap: 6px; }
.dv-drill {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  border: 1px solid var(--line); background: var(--bg-2);
  padding: 10px 13px;
}
.dv-drill-main { flex: 1 1 260px; min-width: 0; }
.dv-drill-main b { display: flex; align-items: center; gap: 7px; font-size: 0.8rem; }
.dv-drill-main b .ic { color: var(--acc); }
.dv-drill-main p { margin: 3px 0 0; font-family: var(--serif); font-size: 0.9rem; color: var(--ink-2); }
.dv-drill-stat { display: flex; gap: 14px; }
.dv-stat { display: flex; flex-direction: column; font-size: 0.74rem; color: var(--ink); white-space: nowrap; }
.dv-stat i {
  font-style: normal; font-size: 0.52rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}

/* ---------------- the detail's three tabs ----------------
   The curriculum roughly quadrupled what this modal has to say, and a reading
   list at the bottom of a very long scroll is a reading list nobody reaches. */
.dv-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin: 14px 0 0; }
.dv-tab {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 13px; border: 1px solid transparent; border-bottom: none;
  background: none; color: var(--muted); cursor: pointer;
  font-size: 0.68rem; letter-spacing: 0.09em; text-transform: uppercase;
  transition: color var(--fast), background var(--fast);
}
.dv-tab:hover { color: var(--ink); }
.dv-tab.on {
  color: var(--acc); border-color: var(--line); background: var(--bg-2);
  /* sit the active tab on top of the strip's own border rather than above it */
  margin-bottom: -1px; padding-bottom: 9px;
}
.dv-tab i {
  font-style: normal; font-size: 0.58rem; color: var(--muted);
  border: 1px solid var(--line); padding: 0 4px; border-radius: 2px;
}
.dv-tab.on i { color: var(--acc); border-color: var(--acc); }
.dv-body { padding-top: 14px; }
.dv-note { margin: 0 0 12px; font-family: var(--serif); font-size: 0.88rem; color: var(--muted); }

/* ---------------- the curriculum ----------------
   The half of Temper that teaches instead of scoring. Long-form on purpose:
   this is the only place in the app you are meant to sit and read. */
.cur { display: flex; flex-direction: column; gap: 4px; }
.cur .sec-label { margin-top: 20px; }
.cur .sec-label.warn { color: var(--bad); }
.cur .sec-label.warn .ic { color: var(--bad); }

.cur-thesis p {
  margin: 0; font-family: var(--serif); font-size: 1rem; line-height: 1.55; color: var(--ink);
  border-left: 2px solid var(--acc); padding-left: 14px;
}
.cur-thesis .sec-label { margin-top: 0; }

.cur-list { display: flex; flex-direction: column; gap: 8px; }
.cur-card { border: 1px solid var(--line); background: var(--bg-2); padding: 11px 14px; }
.cur-card > b { display: flex; align-items: baseline; gap: 10px; font-size: 0.86rem; }
.cur-card > p { margin: 5px 0 0; font-family: var(--serif); font-size: 0.92rem; line-height: 1.5; color: var(--ink-2); }
.cur-card.do { border-left: 2px solid var(--acc); }
.cur-card.trap { border-left: 2px solid var(--bad); }

.cur-cost {
  margin-left: auto; flex: none;
  font-size: 0.56rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--acc);
  border: 1px solid var(--line); padding: 2px 6px; white-space: nowrap;
}
.cur-tell { font-size: 0.78rem; color: var(--muted); }
/* the exercise handing itself to the forge timer below */
.cur-run {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 9px;
  padding: 5px 10px; border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--bg-3); color: var(--ink-2);
  font-size: 0.68rem; font-family: var(--mono); cursor: pointer;
  transition: background var(--fast), color var(--fast), border-color var(--fast);
}
.cur-run:hover { background: var(--acc); border-color: var(--acc); color: var(--acc-ink); }
.cur-tell i {
  font-style: normal; text-transform: uppercase; letter-spacing: 0.08em;
  font-size: 0.56rem; color: var(--ok); margin-right: 6px;
}

.cur-steps { margin: 8px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.cur-steps li { font-size: 0.82rem; line-height: 1.45; color: var(--ink-2); }
.cur-steps li::marker { color: var(--acc); font-size: 0.7rem; }

.cur-sources { display: flex; flex-direction: column; gap: 5px; }
.cur-src {
  display: flex; align-items: flex-start; gap: 11px;
  border: 1px solid var(--line); background: var(--bg-2); padding: 10px 13px;
  color: inherit; text-decoration: none;
  transition: border-color var(--fast), background var(--fast);
}
.cur-src:hover { border-color: var(--acc); background: var(--bg-3); }
.cur-src .ic { margin-left: auto; flex: none; color: var(--muted); align-self: center; }
.cur-src:hover .ic { color: var(--acc); }
.cur-kind {
  flex: none; width: 52px; padding-top: 2px;
  font-size: 0.52rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--acc);
}
.cur-src-main { min-width: 0; }
.cur-src-main b { font-size: 0.84rem; }
.cur-src-main i { font-style: normal; font-size: 0.72rem; color: var(--muted); margin-left: 7px; }
.cur-src-main p { margin: 4px 0 0; font-family: var(--serif); font-size: 0.88rem; line-height: 1.45; color: var(--ink-2); }

/* ---------------- the forge timer ---------------- */
.tp-timer {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  border: 1px solid var(--line-2);
  background: var(--bg);
  padding: 14px 16px;
}
.ft-dial {
  font-size: 2.1rem; font-weight: 700; font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em; line-height: 1;
}
.ft-label { font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin-top: 3px; }
.ft-mid { flex: 1 1 260px; min-width: 220px; }
/* the heat bar: fills as the session burns down, empties when it's over */
.ft-heat { height: 6px; background: var(--bg-3); border: 1px solid var(--line); margin-bottom: 8px; }
.ft-heat > i { display: block; height: 100%; background: var(--acc); transition: width 500ms linear; }
.ft-presets { display: flex; align-items: center; gap: 5px; margin-bottom: 6px; }
.ft-preset {
  padding: 4px 10px;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--bg-2); color: var(--muted);
  font-size: 0.7rem; font-family: var(--mono);
}
.ft-preset:hover { color: var(--ink); }
.ft-preset.active { background: var(--acc); color: var(--acc-ink); border-color: var(--acc); font-weight: 700; }
.ft-custom { width: 74px; padding: 4px 8px; font-size: 0.7rem; }
.ft-stats { font-size: 0.62rem; color: var(--muted); }

/* the idea panel's hook into this tab — matches .think-embed above it */
.focus-embed {
  display: flex; align-items: center; gap: 10px;
  margin-top: 10px; padding-top: 12px;
  border-top: 1px solid var(--line-2);
}
.focus-total { font-size: 0.66rem; color: var(--muted); }

/* ---------------- the log ---------------- */
.tp-log { border: 1px solid var(--line-2); background: var(--bg); }
.tp-log.empty p { margin: 0; padding: 18px; text-align: center; color: var(--muted); font-family: var(--serif); }
/* Every cell is nowrap by design — a wrapped timestamp reads as two runs —
   so the table gets its own scroller rather than pushing the page sideways
   when the window is narrow. The footer below it stays put. */
.tp-log-scroll { overflow-x: auto; }
.tp-log table { width: 100%; border-collapse: collapse; font-size: 0.72rem; }
.tp-log td { padding: 6px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.tp-log tr:last-child td { border-bottom: none; }
.tp-log .lg-when { color: var(--muted); font-size: 0.64rem; width: 1%; }
.tp-log .lg-drill { display: flex; align-items: center; gap: 6px; }
.tp-log .lg-drill .ic { color: var(--acc); }
.tp-log .lg-d { color: var(--muted); }
.tp-log .lg-score { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
.tp-log .lg-score b { color: var(--ink); }
.tp-log .lg-rank { width: 1%; text-align: right; }
.tp-log-foot {
  padding: 7px 12px;
  border-top: 1px solid var(--line-2);
  font-size: 0.62rem; color: var(--muted);
}
.rank-chip {
  display: inline-block;
  font-size: 0.56rem; letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid var(--line-2); padding: 1px 6px;
  color: var(--muted);
}
.rank-chip.r3 { color: var(--st-shaping); border-color: var(--st-shaping); }
.rank-chip.r4 { color: var(--st-spark); border-color: var(--st-spark); }
.rank-chip.r5 { color: var(--acc); border-color: var(--acc); }

/* ================================================================
   Drills — everything below is scoped to .drill, which only exists
   inside the drill modal, so none of these short names can collide
   with the rest of the app.
   ================================================================ */
.drill { display: flex; flex-direction: column; gap: 10px; }
.drill-head { display: flex; align-items: center; gap: 10px; }
.drill-head h2 { margin: 0; font-size: 0.95rem; text-transform: lowercase; }
.drill-head h2::before { content: ":: "; color: var(--acc); }
.drill-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line); padding: 2px 7px;
}
.drill-tag .ic { color: var(--acc); }
.drill-hud {
  margin-left: auto;
  font-size: 0.72rem; color: var(--muted); font-variant-numeric: tabular-nums;
}
.drill-hud b { color: var(--acc); }
.drill-head.run .drill-tag { margin-right: 0; }
.drill-x {
  margin-left: auto;
  display: inline-flex; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: transparent; color: var(--muted);
}
.drill-head.run .drill-x { margin-left: 10px; }
.drill-x:hover { color: var(--bad); border-color: var(--bad); }
.drill-blurb { margin: 0; font-family: var(--serif); font-size: 0.98rem; color: var(--ink-2); }
.drill-rules { display: flex; flex-direction: column; gap: 3px; }
.drill .rule, .drill-rules .rule { display: flex; gap: 7px; font-size: 0.74rem; color: var(--ink-2); }
.drill .rule i { font-style: normal; color: var(--acc); }
.drill-scale { display: flex; gap: 4px; }
.drill-scale .ds-step {
  flex: 1 1 0;
  display: flex; flex-direction: column; gap: 1px;
  border: 1px solid var(--line); padding: 4px 6px;
  font-size: 0.62rem; color: var(--muted); font-variant-numeric: tabular-nums;
}
.drill-scale b { font-size: 0.52rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2); font-weight: 500; }
.drill-foot { display: flex; align-items: center; gap: 8px; padding-top: 4px; }
.drill-foot .spacer { flex: 1; }
.drill-pb, .drill-pbline { font-size: 0.66rem; color: var(--muted); margin: 0; }
.drill-detail { margin: 0; font-family: var(--serif); font-size: 0.92rem; color: var(--ink-2); }

/* the running surface */
.drill-time { height: 3px; background: var(--bg-3); border: 1px solid var(--line); }
.drill-time > i { display: block; height: 100%; background: var(--acc); }
.drill-stage {
  min-height: 300px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  border: 1px solid var(--line);
  background: var(--bg-2);
  padding: 16px;
  outline: none;
}
.drill-count { font-size: 3.4rem; font-weight: 800; color: var(--acc); animation: tp-pop 380ms ease both; }
.drill-load { color: var(--muted); font-size: 0.78rem; }
.drill-load.err { color: var(--bad); }
@keyframes tp-pop { from { opacity: 0; transform: scale(0.82); } to { opacity: 1; transform: none; } }

/* result */
.drill-result { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.dr-score { display: flex; align-items: baseline; gap: 7px; }
.dr-score b { font-size: 2.6rem; line-height: 1; color: var(--acc); font-variant-numeric: tabular-nums; }
.dr-score span { font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.dr-rank { flex: 1 1 220px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dr-rank .rankbar { flex: 1 1 120px; }
.dr-rank-name { font-size: 0.8rem; font-weight: 700; color: var(--ink); text-transform: lowercase; }
.dr-rank-sub { font-size: 0.6rem; color: var(--muted); }
.dr-face { display: flex; align-items: center; gap: 8px; font-size: 0.72rem; color: var(--ink-2); }
.dr-face span { display: flex; flex-direction: column; }
.dr-face span i { font-style: normal; font-size: 0.58rem; color: var(--muted); }

/* --- span --- */
.drill .sp-mode { margin: 0; font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.drill .sp-show { display: flex; align-items: center; justify-content: center; min-height: 120px; }
.drill .sp-digit { font-size: 4.4rem; font-weight: 800; line-height: 1; }
.drill .sp-digit.in { animation: tp-pop 200ms ease both; }
.drill .sp-input { width: 220px; text-align: center; font-size: 1.4rem; letter-spacing: 0.22em; padding: 8px; }
/* Not a convenience button. inputmode="numeric" gets you a bare keypad on
   iOS with no return key on it, so without this the span drill is the one
   drill a phone cannot finish. Enter still works wherever there is one. */
.drill .sp-go { min-width: 140px; }
.drill .sp-hint { font-size: 0.62rem; color: var(--muted); }
.drill .sp-verdict { font-size: 1.4rem; font-weight: 700; text-transform: lowercase; }
.drill .sp-verdict.ok { color: var(--ok); }
.drill .sp-verdict.no { color: var(--bad); }

/* --- n-back --- */
.drill .nb-intro { text-align: center; }
.drill .nb-intro b { display: block; font-size: 2.2rem; color: var(--acc); margin: 6px 0; }
.drill .nb-intro p { margin: 0; font-family: var(--serif); font-size: 0.94rem; color: var(--ink-2); max-width: 40ch; }
.drill .nb-block { font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.drill .nb-stage { display: flex; align-items: center; justify-content: center; min-height: 150px; }
.drill .nb-letter { font-size: 5rem; font-weight: 800; line-height: 1; }
.drill .nb-letter.in { animation: tp-pop 180ms ease both; }
.drill .nb-match { min-width: 200px; }
.drill .nb-match.hit { background: var(--ok); border-color: var(--ok); color: var(--acc-ink); }
.drill .nb-match.miss { background: var(--bad); border-color: var(--bad); color: var(--acc-ink); }

/* --- faces / profile --- */
.drill .fc-study { display: flex; flex-direction: column; align-items: center; gap: 7px; animation: tp-pop 220ms ease both; }
.drill .fc-study img, .drill .fc-quiz img {
  width: 150px; height: 190px; object-fit: cover; object-position: 50% 20%;
  border: 1px solid var(--line-2);
}
.drill .fc-study b { font-size: 1.05rem; }
.drill .fc-study span { font-size: 0.66rem; color: var(--muted); }
.drill .fc-quiz { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; justify-content: center; }
.drill .fc-options { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
.drill .fc-opt { justify-content: flex-start; }
.drill .fc-opt.right { background: var(--ok); border-color: var(--ok); color: var(--acc-ink); }
.drill .fc-opt.wrong { background: var(--bad); border-color: var(--bad); color: var(--acc-ink); }
.drill .pf-card { align-self: stretch; border: 1px solid var(--line); background: var(--bg); padding: 12px 14px; }

/* ---- face & name: the typed answer, the gap, the verdict ----
   The input is the whole change from the old drill — you produce the name
   rather than picking it — so it gets the width and the focus ring. */
.drill .fc-ask { display: flex; flex-direction: column; gap: 8px; min-width: 240px; flex: 1 1 240px; }
.drill .fc-ask p, .drill .fc-prompt { margin: 0; font-size: 0.8rem; color: var(--muted); }
.drill .fc-prompt { text-align: center; margin-bottom: 12px; }
.drill .fc-input { width: 100%; font-size: 1rem; }

.drill .fc-gap { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 18px 0; }
.drill .fc-gap p { margin: 0; font-size: 0.72rem; color: var(--muted); text-transform: lowercase; }
.drill .fc-gap b { font-size: 3.2rem; font-variant-numeric: tabular-nums; line-height: 1; }
.drill .fc-gap-opts { display: flex; gap: 8px; }

/* trade -> face: four portraits, one answer */
.drill .fc-lineup { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.drill .fc-face {
  padding: 0; border: 1px solid var(--line); background: var(--bg);
  cursor: pointer; line-height: 0; transition: border-color var(--fast), transform var(--fast);
}
.drill .fc-face img { width: 104px; height: 132px; object-fit: cover; display: block; }
.drill .fc-face:hover:not(:disabled) { border-color: var(--acc); transform: translateY(-2px); }
.drill .fc-face.right { border-color: var(--ok); box-shadow: 0 0 0 2px var(--ok); }
.drill .fc-face.wrong { border-color: var(--bad); box-shadow: 0 0 0 2px var(--bad); }

.drill .fc-verdict { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 28px 0; }
.drill .fc-verdict b { font-size: 1.3rem; text-transform: lowercase; }
.drill .fc-verdict.ok b { color: var(--ok); }
.drill .fc-verdict.no b { color: var(--bad); }
.drill .fc-verdict span { font-size: 0.9rem; color: var(--ink-2); }

/* ---- cold read: the observations, and the line that settled it ----
   The scene is a full sentence, so it gets sentence styling rather than the
   tiny uppercase label the old drill used for a series name. */
.drill .pf-scene { display: block; font-family: var(--serif); font-size: 0.86rem; color: var(--muted); }
.drill .pf-tells { margin: 8px 0 0; padding: 0 0 0 16px; display: flex; flex-direction: column; gap: 5px; }
.drill .pf-tells li { font-family: var(--serif); font-size: 0.92rem; color: var(--ink-2); }
.drill .pf-tells li::marker { color: var(--acc); }
.drill .pf-ask { margin: 12px 0 8px; font-size: 0.78rem; color: var(--muted); }
.drill .pf-opts { min-width: 0; }
/* the Barnum option is only ever revealed AFTER the click — marking it before
   would hand you the answer, which is the one thing this round must not do */
.drill .fc-opt.barnum { border-color: var(--warn, var(--acc)); border-style: dashed; }
.drill .pf-why {
  margin: 10px 0 0; font-size: 0.8rem; color: var(--ink-2);
  border-left: 2px solid var(--ok); padding-left: 10px; animation: tp-pop 200ms ease both;
}
.drill .pf-why b { color: var(--ok); }
.drill .pf-why.barnum { border-left-color: var(--bad); }
.drill .pf-why.barnum b { color: var(--bad); }

/* --- observation grids --- */
.drill .ob-mode { margin: 0; font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.drill .ob-grid {
  display: grid; gap: 4px;
  grid-template-columns: repeat(var(--cols, 5), 1fr);
}
.drill .ob-grid[data-cols="4"] { --cols: 4; }
.drill .ob-grid[data-cols="5"] { --cols: 5; }
.drill .ob-grid[data-cols="6"] { --cols: 6; }
.drill .ob-cell {
  width: 46px; height: 46px;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 1.15rem; color: var(--ink-2);
  transition: background var(--fast), border-color var(--fast);
}
.drill .ob-cell:not(:disabled):hover { border-color: var(--acc); }
.drill .ob-cell.lit { background: var(--acc); border-color: var(--acc); }
.drill .ob-cell.pick { background: var(--bg-3); border-color: var(--ink-2); }
.drill .ob-cell.blank { color: transparent; background: var(--bg-3); }
.drill .ob-cell.right { border-color: var(--ok); color: var(--ok); }
.drill .ob-cell.wrong { border-color: var(--bad); color: var(--bad); }

/* --- chain --- */
.drill .ch-clues { align-self: stretch; display: flex; flex-direction: column; gap: 3px; }
.drill .ch-board { display: flex; gap: 5px; flex-wrap: wrap; justify-content: center; }
.drill .ch-slot {
  min-width: 82px; height: 44px;
  display: flex; align-items: center; justify-content: center; gap: 5px;
  border: 1px dashed var(--line-2); border-radius: var(--r);
  background: var(--bg); color: var(--ink);
  font-family: var(--mono); font-size: 0.78rem;
}
.drill .ch-slot i { font-style: normal; font-size: 0.56rem; color: var(--muted); }
.drill .ch-slot.filled { border-style: solid; }
.drill .ch-slot.right { border-color: var(--ok); color: var(--ok); }
.drill .ch-slot.wrong { border-color: var(--bad); color: var(--bad); }
.drill .ch-pool { display: flex; gap: 5px; flex-wrap: wrap; justify-content: center; }
.drill .ch-name:disabled { opacity: 0.3; }

/* --- stroop --- */
.drill .st-ask { margin: 0; font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.drill .st-word { font-size: 3.4rem; font-weight: 800; letter-spacing: 0.04em; line-height: 1.1; }
.drill .st-options { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.drill .st-opt kbd { margin-right: 2px; }
.drill .st-opt.right { background: var(--ok); border-color: var(--ok); color: var(--acc-ink); }
.drill .st-opt.wrong { background: var(--bad); border-color: var(--bad); color: var(--acc-ink); }

/* --- reaction --- */
.drill .rx-pad {
  align-self: stretch; flex: 1;
  min-height: 220px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-2);
  font-size: 1rem; letter-spacing: 0.12em; text-transform: uppercase;
  cursor: pointer; user-select: none;
}
.drill .rx-pad.wait { background: var(--bg-3); color: var(--muted); }
.drill .rx-pad.go { background: var(--acc); color: var(--acc-ink); border-color: var(--acc); }
.drill .rx-pad.early { background: var(--bad); color: var(--acc-ink); border-color: var(--bad); }
.drill .rx-arrow { font-size: 3.4rem; }
.drill .rx-ms { font-size: 2rem; font-weight: 800; }

/* --- anagram / sequence --- */
.drill .lg-hint { margin: 0; font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); text-align: center; }
.drill .lg-scramble { display: flex; gap: 6px; }
.drill .lg-scramble span {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 48px;
  border: 1px solid var(--line-2); background: var(--bg);
  font-size: 1.5rem; font-weight: 700; text-transform: uppercase;
}
.drill .lg-scramble.right span { border-color: var(--ok); color: var(--ok); }
.drill .lg-scramble.shown span { border-color: var(--muted); color: var(--muted); }
.drill .lg-in { width: 260px; text-align: center; font-size: 1.3rem; letter-spacing: 0.18em; padding: 8px; text-transform: uppercase; }
.drill .pt-seq { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center; }
.drill .pt-seq span {
  min-width: 58px; padding: 8px 10px;
  border: 1px solid var(--line-2); background: var(--bg);
  font-size: 1.25rem; font-variant-numeric: tabular-nums; text-align: center;
}
.drill .pt-seq .pt-blank { border-style: dashed; border-color: var(--acc); color: var(--acc); }
.drill .pt-seq.right span { border-color: var(--ok); color: var(--ok); }

/* --- rotate --- */
.drill .sp-pair { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; justify-content: center; }
.drill .sp-grid .ob-cell { width: 34px; height: 34px; }
.drill .sp-vs { font-size: 1.4rem; color: var(--muted); }
.drill .sp-answers { display: flex; gap: 8px; }
.drill .sp-ans.right { background: var(--ok); border-color: var(--ok); color: var(--acc-ink); }
.drill .sp-ans.wrong { background: var(--bad); border-color: var(--bad); color: var(--acc-ink); }

/* --- sprint --- */
.drill .nm-q { font-size: 2.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.drill .nm-q.right { color: var(--ok); }
.drill .nm-in { width: 200px; text-align: center; font-size: 1.5rem; padding: 8px; }
.drill .nm-skip { opacity: 0.7; }

/* ---------------- the drill and detail modals ----------------
   Both are surfaces you play on rather than dialogs you read, which is why
   they get treated differently from every other modal in the app: a
   pull-to-refresh caught at the top of a scrolled drill would end a run, and
   on a phone the backdrop's gutter is room the stage needs more. */
.modal-bd:has(.drill), .modal-bd:has(.dv),
.modal:has(.drill), .modal:has(.dv) { overscroll-behavior: contain; }
@media (max-width: 640px) {
  .modal-bd:has(.drill), .modal-bd:has(.dv) { padding: 10px; }
  /* dvh, not vh: with the URL bar showing, 86vh is taller than the screen,
     and the drill's foot buttons end up under the browser chrome */
  .modal:has(.drill), .modal:has(.dv) { padding: 14px; max-height: 92dvh; }
}

/* ---------------- responsive ----------------
   Two rules govern everything below the tablet tier, and they pull in
   different directions:

   · nothing may be wider than the screen. Fixed pixel widths are what break
     that, and this page is full of them — six 42px portraits in a rung strip,
     a 6×6 grid of 46px cells, a 260px input. Below 640 they all become
     fractions of whatever room there is.

   · nothing you hit while a clock is running may be smaller than a thumb.
     That one is about the hand, not the screen, so it lives in the
     pointer:coarse block at the bottom and applies to tablets too.

   What does NOT change is what any drill measures. The rotate pair stays
   side by side rather than stacking (comparing two shapes you can see at
   once is the task), all six rungs stay on the strip, and no drill gains a
   shortcut it doesn't have on a desktop. ---------------- */
@media (max-width: 900px) {
  .dv-ladder { grid-template-columns: repeat(3, 1fr); }
  .dv-hero { gap: 14px; }
  .dv-hero .face { --fs: 130px !important; }
  .dv-hero-copy b { font-size: 1.2rem; }
}
@media (max-width: 760px) {
  .tp-grid { grid-template-columns: 1fr; }
  .dv-drill-stat { gap: 10px; }
  .drill .sp-grid .ob-cell { width: 26px; height: 26px; }
  .drill .lg-scramble span { width: 30px; height: 38px; font-size: 1.15rem; }
  .tp-today { margin-left: 0; }
  .tp-timer { gap: 12px; }
  .ft-dial { font-size: 1.7rem; }
  .drill .fc-study img, .drill .fc-quiz img { width: 120px; height: 150px; }
  .drill .ob-cell { width: 38px; height: 38px; font-size: 1rem; }
  .drill .st-word { font-size: 2.4rem; }
  .drill .sp-digit { font-size: 3.4rem; }
  .drill .nb-letter { font-size: 3.6rem; }
}

/* ---- phone ---- */
@media (max-width: 640px) {
  .tp-card { padding: 12px 12px 10px; }
  .tc-head { margin: -12px -12px 0; padding: 12px 12px 6px; }

  /* the tallies take the full row and split it evenly, and their labels are
     allowed to wrap — "day streak" on one nowrap line was the widest thing
     in the hero */
  .tp-hero { gap: 10px; margin-bottom: 12px; }
  .tp-today { width: 100%; }
  .tp-today span {
    flex: 1 1 0; min-width: 0;
    padding: 7px 4px;
    white-space: normal; text-align: center;
  }

  .tp-quote { gap: 11px; padding: 11px 12px; }
  .tp-quote p { font-size: 0.96rem; }

  /* Six portraits at a fixed 42px stop fitting somewhere under 340px. They
     become fractions of the card instead, because dropping any of them would
     defeat the strip — it exists to show you who is ahead of you. */
  .tc-rungs { gap: 4px; }
  .tc-rung .face { width: 100%; height: auto; aspect-ratio: 3 / 4; }
  .tc-rung i { font-size: 0.48rem; letter-spacing: 0.02em; }

  /* current → next: the arrow is the only thing here carrying no information
     the layout doesn't already carry, so it goes first */
  .tc-now { gap: 8px; }
  .tc-arrow { display: none; }
  .tc-who .face { --fs: 44px !important; }
  .tc-who.next .face { --fs: 38px !important; }

  /* Five nowrap columns never fit a phone. Discipline goes — the drill name
     beside it already implies it — and the rest slides in its own scroller
     so the page itself never moves sideways. */
  .tp-log-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tp-log td { padding: 6px 8px; }
  .tp-log .lg-d { display: none; }

  /* the timer stacks, and start/stop goes full width: it's the one control
     here you reach for with the clock already running */
  .tp-timer { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; }
  .ft-left { display: flex; align-items: baseline; gap: 10px; }
  .ft-label { margin-top: 0; }
  .ft-mid { flex: 1 1 auto; min-width: 0; }
  .ft-presets { flex-wrap: wrap; }
  .ft-preset { flex: 1 1 auto; }
  .ft-custom { flex: 1 1 70px; width: auto; }
  .ft-right .btn { width: 100%; justify-content: center; }

  /* discipline detail — the portrait is the reason this view exists, so it
     keeps its size and the copy moves under it rather than beside it */
  .dv-hero { flex-direction: column; align-items: center; text-align: center; }
  .dv-hero .face { --fs: 150px !important; max-width: 100%; }
  .dv-hero-copy { align-items: center; width: 100%; }
  .dv-hero-copy p { text-align: left; }
  .dv-hero-copy .rankbar { align-self: stretch; }
  .dv-drill { gap: 10px; }
  .dv-drill .btn { width: 100%; justify-content: center; }

  /* ---- the drill shell ---- */
  .drill-head { flex-wrap: wrap; }
  .drill-head h2 { font-size: 0.88rem; }
  .drill-scale { flex-wrap: wrap; }
  .drill-scale .ds-step { flex: 1 1 28%; }
  .drill-stage { min-height: 260px; padding: 12px 10px; }
  .drill-foot { flex-wrap: wrap; }
  .drill-foot .btn { flex: 1 1 auto; justify-content: center; }
  .drill-pb { flex: 1 1 100%; }
  .drill-result { gap: 12px; }
  .dr-score b { font-size: 2.1rem; }

  /* every fixed-width field in a drill becomes a fluid one */
  .drill .sp-input, .drill .lg-in, .drill .nm-in { width: 100%; max-width: 280px; }
  .drill .nb-match, .drill .fc-options, .drill .st-options { min-width: 0; width: 100%; }
  .drill .sp-go { width: 100%; max-width: 280px; justify-content: center; }

  /* Cells sized off the stage rather than off a pixel constant, so the 6×6
     change-blindness board fits the same screen the 4×4 one does. */
  .drill .ob-grid { width: 100%; max-width: 340px; }
  .drill .ob-cell { width: auto; height: auto; aspect-ratio: 1; font-size: 0.95rem; }
  /* rotate: nowrap keeps the two shapes on one line — stacked, you'd be
     comparing from memory, which is a different drill */
  .drill .sp-pair { width: 100%; gap: 8px; flex-wrap: nowrap; }
  .drill .sp-grid { flex: 1 1 0; min-width: 0; max-width: none; }
  .drill .sp-grid .ob-cell { width: auto; height: auto; }
  .drill .sp-answers { width: 100%; }
  .drill .sp-ans { flex: 1 1 0; justify-content: center; }

  /* an eight-letter anagram is eight tiles wide — let it take two rows */
  .drill .lg-scramble { flex-wrap: wrap; justify-content: center; gap: 4px; }
  .drill .lg-scramble span { width: 32px; height: 40px; font-size: 1.25rem; }

  .drill .fc-quiz { flex-direction: column; gap: 12px; }
  .drill .fc-study img, .drill .fc-quiz img { width: 132px; height: 168px; }
  .drill .ch-board, .drill .ch-pool { width: 100%; }
  .drill .ch-slot { min-width: 64px; flex: 1 1 64px; }

  .drill .st-word { font-size: 2.1rem; }
  .drill .st-opt { flex: 1 1 44%; justify-content: center; }
  .drill .sp-digit { font-size: 3rem; }
  .drill .nb-letter { font-size: 3.2rem; }
  .drill .nb-stage { min-height: 120px; }
  .drill .rx-pad { min-height: 200px; }
}

/* ---- narrow phone ---- */
@media (max-width: 400px) {
  /* three ladder columns leave the caption too little to say who anyone is */
  .dv-ladder { grid-template-columns: repeat(2, 1fr); }

  /* the tab strip has to survive three labels plus two counters */
  .dv-tab { padding: 8px 9px; font-size: 0.62rem; letter-spacing: 0.05em; }
  .cur-kind { width: 42px; }
  /* the cost chip wraps under the name rather than squeezing it to two words */
  .cur-card > b { flex-wrap: wrap; }
  .cur-cost { margin-left: 0; }
  .drill .fc-face img { width: 74px; height: 94px; }
  .dv-hero .face { --fs: 132px !important; }
  .tc-who.next { display: none; } /* the next rung is on the strip below anyway */
  .ft-dial { font-size: 1.5rem; }
}

/* ---- touch ----
   Sized for the hand, so this keys off the pointer rather than the width and
   picks up tablets too. Every drill on this page is timed, which makes a
   mis-hit a lost point rather than an annoyance. */
@media (pointer: coarse) {
  /* stops a double-tap zoom from swallowing a tap in a 45-second Stroop run */
  .tc-drill, .tc-mini, .tc-head, .ft-preset, .ft-custom, .ft-right .btn,
  .drill button, .drill .ob-cell, .drill .rx-pad, .dv-drill .btn { touch-action: manipulation; }

  .tc-drill { padding: 8px 11px; }
  .tc-mini { padding: 8px 10px; }
  .ft-preset { padding: 9px 14px; }
  .drill-x { padding: 8px; }
  /* buttons only — rotate draws its two shapes with spans, and floor-sizing
     those would push the pair wider than a 320px stage */
  .drill button.ob-cell { min-width: 30px; min-height: 30px; }
  .drill .ch-slot { height: 46px; }
  .drill .ch-name, .drill .fc-opt, .drill .st-opt, .drill .sp-ans { padding: 9px 12px; }

  /* iOS zooms the whole page in on a field under 16px. Mid-drill that isn't
     a nuisance, it's a lost run. */
  .ft-custom { font-size: 16px; }

  /* printed key hints with no keys behind them. The buttons they sit on all
     work by tap — it's only the label that was lying. */
  .drill .st-opt kbd, .drill .sp-ans kbd, .drill .nb-match kbd { display: none; }
  /* "press enter to answer" — the answer button beside it is the real path */
  .drill .sp-hint { display: none; }
}
