/* ---------------------------------------------------------------------------
   Speaking Drills - the practice booth.

   Quiet, low-light, one thing lit at a time. Amber is a signal, not a
   decoration: it appears on the timer and on whatever is currently active,
   nowhere else. Hierarchy is done with weight and space, not with borders
   and not with a card around every block.
--------------------------------------------------------------------------- */

:root {
  --ground: #101317;
  --ground-lift: #171b20;
  --ground-sunk: #0a0c0e;
  --line: #232931;
  --line-soft: #1a1f25;

  --ink: #f1ebe1;
  --ink-soft: #a49c90;
  --ink-faint: #6b665e;

  --signal: #f2a93b;
  --signal-sunk: rgba(242, 169, 59, 0.12);
  --signal-edge: rgba(242, 169, 59, 0.35);
  --warn: #e2603f;

  --font: "Inter", "Segoe UI Variable Text", system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  --gutter: 20px;
  --measure: 34rem;
  --tap: 44px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* A class that sets `display` beats the browser's own [hidden] rule, and
   several classes here do. Settle it once rather than per element. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.5;
  font-feature-settings: "tnum" 1, "cv05" 1;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* Numerals never re-flow mid-countdown. */
.num,
time,
.metric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --- shell ---------------------------------------------------------------- */

.shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 56px;
  padding-top: env(safe-area-inset-top, 0);
}

.wordmark {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
}

.wordmark b {
  font-weight: 800;
  color: var(--ink);
}

.topbar form {
  margin: 0;
}

.linkish {
  background: none;
  border: 0;
  padding: 10px 4px;
  min-height: var(--tap);
  font: inherit;
  font-size: 0.82rem;
  color: var(--ink-faint);
  cursor: pointer;
}

.linkish:hover {
  color: var(--ink-soft);
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* --- the booth ------------------------------------------------------------ */

.booth {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 8px 0 28px;
}

.drill-head {
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line-soft);
}

.drill-name {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: -0.005em;
}

.drill-instruction {
  margin: 4px 0 0;
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--ink-soft);
}

.drill-meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 12px;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.drill-meta .target {
  font-weight: 600;
  color: var(--ink-soft);
}

.dot {
  color: var(--line);
}

/* The one lit thing on the page. No card, no border -- just light. */
.prompt {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  padding: 36px 0;
}

/* Light falling on the prompt. Kept inside the element's own box on the
   horizontal axis -- a negative inline inset here grows scrollWidth and
   gives the phone a sideways drag. */
.prompt::before {
  content: "";
  position: absolute;
  inset: -12% 0;
  background: radial-gradient(
    115% 55% at 50% 45%,
    rgba(241, 235, 225, 0.05),
    transparent 70%
  );
  pointer-events: none;
}

.prompt-text {
  position: relative;
  margin: 0;
  font-size: clamp(1.45rem, 6.2vw, 2rem);
  font-weight: 300;
  line-height: 1.28;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

.prompt-context {
  position: relative;
  margin: 0 0 12px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-faint);
}

.empty {
  color: var(--ink-soft);
  font-weight: 300;
}

/* --- controls ------------------------------------------------------------- */

.controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 20px;
  border-top: 1px solid var(--line-soft);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: 8px;
  font: inherit;
  font-size: 0.98rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease,
    color 120ms ease;
}

.btn-primary {
  background: var(--signal);
  color: #16120a;
  font-weight: 700;
}

.btn-primary:hover {
  background: #ffbb4d;
}

.btn-ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--ink-soft);
}

.btn-ghost:hover {
  border-color: var(--ink-faint);
  color: var(--ink);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Segmented language switch. The selected side is "active", so it gets the
   signal colour -- and it is the only place on this screen that does. */
.segmented {
  display: inline-flex;
  padding: 3px;
  gap: 3px;
  background: var(--ground-sunk);
  border: 1px solid var(--line-soft);
  border-radius: 9px;
}

.segmented button {
  min-height: 38px;
  min-width: 56px;
  padding: 0 14px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-faint);
  cursor: pointer;
}

.segmented button:hover {
  color: var(--ink-soft);
}

.segmented button[aria-pressed="true"] {
  background: var(--signal-sunk);
  box-shadow: inset 0 0 0 1px var(--signal-edge);
  color: var(--signal);
}

.control-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* --- the drill screen ----------------------------------------------------- */

/* The one bold element on the page. Everything else stays flat. */
.timer {
  position: relative;
  display: grid;
  place-items: center;
  margin: 4px auto 0;
  width: min(62vw, 210px);
  aspect-ratio: 1;
}

.ring {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.ring-track,
.ring-arc {
  fill: none;
  stroke-width: 7;
  stroke-linecap: round;
}

.ring-track {
  stroke: var(--line-soft);
}

.ring-arc {
  stroke: var(--signal);
  /* Painted from JS on every frame -- or once a second under
     prefers-reduced-motion -- so there is no transition here to fight. */
  stroke-dashoffset: 0;
}

.is-over .ring-arc {
  stroke: var(--warn);
}

.timer-readout {
  position: absolute;
  margin: 0;
  font-size: clamp(2.6rem, 13vw, 3.4rem);
  font-weight: 200;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.is-over .timer-readout {
  color: var(--warn);
}

.timer-summary {
  margin: 18px 0 0;
  font-size: 0.85rem;
  color: var(--ink-faint);
}

.timer-summary b {
  font-weight: 600;
  color: var(--ink-soft);
}

.timer-summary .over,
.take .over {
  color: var(--warn);
}

.prompt--drill {
  /* Absorbs the slack so the prompt sits centred between the drill head and
     the ring, with the ring anchored above the controls. */
  flex: 1 1 auto;
  min-height: 0;
  padding: 22px 0 18px;
}

.prompt-text--drill {
  font-size: clamp(1.15rem, 4.9vw, 1.4rem);
  font-weight: 400;
  line-height: 1.35;
}

.btn-stop {
  background: transparent;
  border-color: var(--signal-edge);
  color: var(--signal);
}

.btn-stop:hover {
  background: var(--signal-sunk);
}

.btn-compact {
  min-height: var(--tap);
  padding: 10px 14px;
  font-size: 0.85rem;
}

.start-form {
  margin: 0;
  display: contents;
}

/* --- capture -------------------------------------------------------------- */

.capture {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 18px;
}

.capture textarea {
  min-height: 190px;
  min-height: 9lh;
  line-height: 1.55;
  resize: vertical;
  /* Dictated text arrives as one long unpunctuated run; let it wrap and
     break rather than pushing the layout sideways. */
  overflow-wrap: break-word;
}

/* State machine. The drill screen shows exactly one thing at a time. */
.drill[data-state="ready"] .timer-summary,
.drill[data-state="ready"] .capture,
.drill[data-state="ready"] [data-stop],
.drill[data-state="running"] .timer-summary,
.drill[data-state="running"] .capture,
.drill[data-state="running"] [data-start],
.drill[data-state="capture"] .timer,
.drill[data-state="capture"] .controls,
.drill[data-state="saving"] .timer,
.drill[data-state="saving"] .controls {
  display: none;
}

.drill[data-state="capture"] .prompt--drill,
.drill[data-state="saving"] .prompt--drill {
  padding: 14px 0 10px;
}

.drill[data-state="capture"] .prompt-text--drill,
.drill[data-state="saving"] .prompt-text--drill {
  font-size: 1rem;
  color: var(--ink-soft);
}

.drill[data-state="saving"] .capture button {
  opacity: 0.45;
  pointer-events: none;
}

/* --- recording ------------------------------------------------------------
   The microphone is an addition to the textarea, never a replacement, so
   every state here is a line of text next to a box that still works. Amber
   appears once: the dot that says the microphone is live, which is the
   active state.
--------------------------------------------------------------------------- */

/* Arming: the permission dialog is up. Not "ready" -- start has already been
   pressed -- and not "running" either, so it gets its own state instead of a
   flash of the wrong one. */
.drill[data-state="arming"] .timer-summary,
.drill[data-state="arming"] .capture,
.drill[data-state="arming"] [data-start],
.drill[data-state="arming"] [data-stop],
.drill[data-state="arming"] [data-capture-mode],
.drill[data-state="arming"] .capture-note {
  display: none;
}

.arming {
  display: none;
  margin: 0;
  text-align: center;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.drill[data-state="arming"] .arming {
  display: block;
}

[data-capture-mode] {
  justify-content: center;
}

/* How to capture is a decision made before speaking. Hiding the toggle
   mid-take is not cosmetic: switching away from Record while the recorder is
   running is how a microphone gets stranded. */
.drill[data-state="running"] [data-capture-mode],
.drill[data-state="capture"] [data-capture-mode],
.drill[data-state="saving"] [data-capture-mode] {
  display: none;
}

.capture-note {
  margin: 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.mic {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin: 0;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* Only while speaking. The JS unhides it; this stops it lingering into the
   capture state if anything ever gets that wrong. */
.drill:not([data-state="running"]) .mic {
  display: none;
}

.mic-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--signal);
  animation: breathe 2.4s ease-in-out infinite;
}

@keyframes breathe {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}

.capture-status {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 4px;
}

.capture-status-label {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.capture-status-label.bad {
  color: var(--warn);
}

.capture-status-detail {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--ink-faint);
}

.capture-status [data-retry] {
  align-self: flex-start;
  padding-left: 0;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- sessions list -------------------------------------------------------- */

.session-list {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}

.session-list li + li {
  border-top: 1px solid var(--line-soft);
}

.session-list a {
  display: grid;
  gap: 4px;
  padding: 16px 2px;
  min-height: var(--tap);
  text-decoration: none;
}

.session-when {
  font-size: 0.76rem;
  color: var(--ink-faint);
}

.session-drill {
  font-size: 0.95rem;
  font-weight: 700;
}

.session-prompt {
  font-size: 0.88rem;
  font-weight: 300;
  color: var(--ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.session-tail {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  font-size: 0.76rem;
  color: var(--ink-faint);
}

.session-lang {
  margin-left: auto;
}

.tag {
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink-faint);
}

.tag-open {
  border-color: var(--signal-edge);
  color: var(--signal);
}

.tag-scored {
  border-color: var(--line);
  color: var(--ink);
}

/* --- session review ------------------------------------------------------- */

.review {
  padding-bottom: 40px;
}

.review-prompt {
  margin: 20px 0 0;
  font-size: 1.05rem;
  font-weight: 300;
  line-height: 1.4;
}

.take {
  padding-top: 22px;
}

.take-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

.take-target {
  color: var(--ink-faint);
}

/* The transcript is shown as it was captured: no tidying, and the line
   breaks it arrived with are kept. */
.transcript {
  margin: 0;
  padding: 14px 16px;
  background: var(--ground-sunk);
  border-left: 2px solid var(--line);
  border-radius: 0 6px 6px 0;
  font-size: 0.95rem;
  font-weight: 300;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  color: var(--ink-soft);
}

.notice {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
  padding: 16px;
  background: var(--ground-lift);
  border-radius: 10px;
}

.notice p {
  margin: 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.notice-quiet {
  background: none;
  padding: 16px 0 0;
  border-top: 1px solid var(--line-soft);
}

.notice-bad {
  box-shadow: inset 0 0 0 1px rgba(226, 96, 63, 0.35);
}

.notice-bad b {
  color: var(--warn);
}

.notice-detail {
  font-size: 0.78rem !important;
  color: var(--ink-faint) !important;
  overflow-wrap: break-word;
}

/* --- scoring: the loading state ------------------------------------------- */

/* Hidden until HTMX marks it in-flight. */
.scoring {
  display: none;
  margin-top: 24px;
  padding: 18px 0 4px;
  border-top: 1px solid var(--line-soft);
}

.scoring.htmx-request {
  display: block;
}

.scoring-label {
  margin: 0 0 12px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.scoring-hint {
  margin: 10px 0 0;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.scoring-bar {
  height: 3px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
}

.scoring-bar span {
  display: block;
  width: 40%;
  height: 100%;
  background: var(--signal);
  border-radius: 999px;
  animation: crawl 1.6s ease-in-out infinite;
}

@keyframes crawl {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(250%);
  }
}

/* --- the result -----------------------------------------------------------
   Ordered by what you can act on. The rewrite is the payload of the whole
   app and gets the biggest type on the page; the bars come last, because
   they describe a take that is already over.
--------------------------------------------------------------------------- */

.score {
  margin-top: 26px;
}

.verdict {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-bottom: 4px;
}

.verdict-score {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.verdict-score b {
  font-size: 2.1rem;
  font-weight: 200;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.verdict-weakest {
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.verdict-weakest b {
  font-weight: 600;
  color: var(--ink-soft);
}

/* Biggest type on the page, nothing boxed around it competing for it. */
.rewrite {
  padding: 8px 0 26px;
}

.rewrite h2,
.advice h2,
.facts h2,
.dims h2 {
  margin: 0 0 10px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-faint);
}

.rewrite-text {
  margin: 0;
  font-size: clamp(1.3rem, 5.6vw, 1.65rem);
  font-weight: 300;
  line-height: 1.32;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-wrap: pretty;
}

.advice {
  padding: 20px 0;
  border-top: 1px solid var(--line-soft);
}

/* Numbered because they are ranked, not a menu: the first one is the one to
   do. No bullets, no cards -- the ranking is the information. */
.fix-list {
  margin: 0;
  padding-left: 1.3em;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.fix-list li {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.fix-list li::marker {
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}

.fix-first {
  font-size: 1rem !important;
  color: var(--ink) !important;
}

.kept {
  margin: 16px 0 0;
  font-size: 0.88rem;
  font-weight: 300;
  line-height: 1.5;
  color: var(--ink-soft);
}

.kept b {
  font-weight: 600;
  color: var(--ink);
}

/* --- the measured facts ---------------------------------------------------
   Arithmetic, not judgement. Tone is carried by the value's colour alone --
   no icons, no badges, and never amber, which belongs to the timer. */

.facts {
  padding: 20px 0;
  border-top: 1px solid var(--line-soft);
}

.facts dl {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.fact dt {
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.fact dd {
  margin: 2px 0 0;
}

/* Three steps, no new colour: recessive for a neutral reading, full ink for
   one worth noticing, warn for one that cost points. */
.fact-value {
  font-size: 1.02rem;
  font-weight: 500;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.fact--watch .fact-value {
  color: var(--ink);
}

.fact--bad .fact-value {
  color: var(--warn);
}

.fact-note {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--ink-faint);
  text-wrap: pretty;
}

/* --- the five readings ----------------------------------------------------- */

.dims {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 0;
  border-top: 1px solid var(--line-soft);
}

.dim-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 0 0 6px;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.dim-head b {
  font-weight: 700;
  color: var(--ink);
}

/* The weakest one is worth finding at a glance; weight does it, so the bars
   stay a single greyscale family. */
.dim--weakest .dim-head,
.dim--weakest .dim-head b {
  color: var(--warn);
}

.dim--weakest .bar span {
  background: var(--warn);
}

/* Greyscale on purpose: amber is the timer and the active state, not a
   decoration for every number on the screen. */
.bar {
  height: 4px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
}

.bar span {
  display: block;
  height: 100%;
  background: var(--ink-soft);
  border-radius: 999px;
}

.dims-note {
  margin: 4px 0 0;
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--ink-faint);
}

.score-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.76rem;
  color: var(--ink-faint);
}

.score-meta .linkish {
  min-height: auto;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.topbar-nav form {
  margin: 0;
}

/* --- footnote ------------------------------------------------------------- */

.footnote {
  padding: 18px 0 26px;
  font-size: 0.76rem;
  line-height: 1.6;
  color: var(--ink-faint);
}

.footnote b {
  font-weight: 600;
  color: var(--ink-soft);
}

/* --- login ---------------------------------------------------------------- */

.gate {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 22px;
  padding-bottom: 12vh;
}

.gate h1 {
  margin: 0;
  font-size: 1.6rem;
  font-weight: 300;
  letter-spacing: -0.02em;
}

.gate h1 b {
  font-weight: 800;
}

.gate form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.field label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-soft);
}

input[type="password"],
input[type="text"],
textarea {
  min-height: var(--tap);
  padding: 12px 14px;
  background: var(--ground-sunk);
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
  color: var(--ink);
}

input:focus,
textarea:focus {
  border-color: var(--signal-edge);
  outline: none;
  box-shadow: 0 0 0 3px var(--signal-sunk);
}

.error {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--warn);
}

/* --- motion --------------------------------------------------------------- */

.swap-in {
  animation: rise 220ms ease-out;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.htmx-request .btn-ghost {
  color: var(--ink-faint);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* Roomier once there is room, but the phone is the design target. */
@media (min-width: 640px) {
  body {
    font-size: 18px;
  }

  .controls {
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
  }

  .controls .btn-primary {
    min-width: 220px;
  }
}

/* The crawling bar is an animation, so reduced motion kills it. Fill the
   track instead of leaving a stub parked at 40%, which reads as stuck. */
@media (prefers-reduced-motion: reduce) {
  .scoring-bar span {
    width: 100%;
    opacity: 0.55;
  }
}

/* --- progress --------------------------------------------------------------
   One series per chart, so no categorical palette exists: the line wears the
   ink token and amber stays reserved for the active filter and the newest
   point. Five dimensions are five small multiples rather than five lines in
   one frame, which is what keeps it that way.
--------------------------------------------------------------------------- */

.streak {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin: 4px 0 20px;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.streak b {
  font-size: 2.3rem;
  font-weight: 200;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.filters {
  margin: 0 0 10px;
}

.segmented-wrap {
  flex-wrap: wrap;
}

.segmented-wrap button {
  min-width: auto;
  padding: 0 12px;
}

.chart-block {
  position: relative;
  padding: 18px 0 4px;
  border-top: 1px solid var(--line-soft);
}

.chart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.chart-head h2,
.recent h2 {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-faint);
}

.chart-latest {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.chart-latest b {
  font-size: 1.5rem;
  font-weight: 300;
  color: var(--ink);
}

.up { color: #7fb283; }
.down { color: var(--warn); }

.chart {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  touch-action: pan-y;
}

/* Recessive grid and axes -- they orient, they do not compete. */
.grid {
  stroke: var(--line-soft);
  stroke-width: 1;
}

.axis {
  fill: var(--ink-faint);
  font-size: 10px;
  font-family: var(--font);
}

.chart-line {
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.chart-dot {
  fill: var(--ink-soft);
  /* A surface ring so overlapping marks stay countable. */
  stroke: var(--ground);
  stroke-width: 2;
}

/* The newest point is the "current" one, so it earns the signal colour. */
.chart-dot.is-last {
  fill: var(--signal);
}

.crosshair {
  stroke: var(--signal-edge);
  stroke-width: 1;
  opacity: 0;
}

.crosshair.is-on {
  opacity: 1;
}

.tooltip {
  position: absolute;
  top: 26px;
  transform: translateX(-50%);
  padding: 5px 9px;
  background: var(--ground-lift);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  pointer-events: none;
  white-space: nowrap;
}

.chart-note {
  margin: 10px 0 0;
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--ink-faint);
}

.seam {
  display: block;
  margin-top: 6px;
  font-weight: 600;
  color: var(--warn);
}

/* --- small multiples ------------------------------------------------------ */

.facets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 4px 18px;
  padding: 20px 0;
  border-top: 1px solid var(--line-soft);
}

.facet {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 10px;
  padding: 8px 0;
}

.facet-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 0 2px;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

.facet-head b {
  font-weight: 700;
  color: var(--ink);
}

.spark {
  width: 100%;
  height: auto;
  overflow: visible;
}

.spark-line {
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.spark-dot {
  fill: var(--signal);
  stroke: var(--ground);
  stroke-width: 1.5;
}

.facet-trend {
  font-size: 0.76rem;
  color: var(--ink-faint);
  text-align: right;
}

/* --- table view ----------------------------------------------------------- */

.table-view {
  padding: 4px 0 18px;
}

.table-view summary {
  padding: 10px 0;
  min-height: var(--tap);
  font-size: 0.8rem;
  color: var(--ink-faint);
  cursor: pointer;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
}

.data-table th {
  padding: 6px 4px;
  text-align: left;
  font-weight: 600;
  color: var(--ink-faint);
  border-bottom: 1px solid var(--line);
}

.data-table td {
  padding: 8px 4px;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line-soft);
}

.data-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.data-table a {
  color: var(--ink);
  text-decoration: none;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  padding: 30px 0;
}

.recent {
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}

/* --- setup ---------------------------------------------------------------- */

.setup {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding-top: 10px;
}

.setup-group {
  margin: 0;
  padding: 0;
  border: 0;
}

.setup-group legend {
  padding: 0;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-faint);
}

.setup-hint {
  margin: 6px 0 12px;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.choice-list,
.radio-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.radio-row {
  flex-direction: row;
  gap: 18px;
}

.choice {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: var(--tap);
  font-size: 0.95rem;
  cursor: pointer;
}

.choice input {
  width: 19px;
  height: 19px;
  accent-color: var(--signal);
}
