/* =====================================================================
   Shot Solver — visual system

   Two themes, both deliberate: "night" for low light and screen comfort,
   "day" for direct sunlight, where a field tool actually has to be legible.
   Numbers are set in a tabular monospace so digits line up in columns and
   a changing value does not make the layout twitch.
   ===================================================================== */

:root {
  --font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 14px;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.3);
  --shadow-2: 0 6px 24px -8px rgb(0 0 0 / 0.55);

  /* ---- night (default) ---- */
  --bg: #0c1014;
  --bg-raised: #141a20;
  --bg-sunken: #080b0e;
  --bg-input: #1b232b;
  --border: #26313b;
  --border-strong: #384754;
  --text: #e4ebf1;
  --text-dim: #9aacba;
  --text-faint: #64798a;
  --accent: #ffb020;
  --accent-dim: #6b4b12;
  --accent-text: #1a1206;
  --cool: #4db8ff;
  --good: #4ade80;
  --warn: #fbbf24;
  --bad: #f87171;
  --grid: #1e2831;

  /* ---- data visualization ----
   * Categorical slots 1-3 and the sequential hue come from a validated
   * palette, re-checked against THIS app's chart surface rather than assumed
   * to transfer. Both modes pass the lightness band, chroma floor, adjacent
   * CVD separation (worst deutan dE 9.4 dark / 9.2 light, target >= 8) and the
   * normal-vision floor. Light mode warns on contrast for slots 2 and 3, which
   * is why every categorical bar carries a visible name and value label — the
   * relief that a contrast warning obliges, not an optional nicety.
   *
   * Slots are assigned in fixed order and never cycled. Status colors are
   * reserved for status and never reused as a series.
   */
  --series-1: #3987e5;   /* blue   */
  --series-2: #d95926;   /* orange */
  --series-3: #199e70;   /* aqua   */
  --seq: #3987e5;        /* single hue for magnitude */
  --seq-soft: #184f95;
}

:root[data-theme='day'] {
  --bg: #f2f4f6;
  --bg-raised: #ffffff;
  --bg-sunken: #e6eaee;
  --bg-input: #ffffff;
  --border: #c8d1d9;
  --border-strong: #93a2af;
  --text: #10171d;
  --text-dim: #445563;
  --text-faint: #6b7c8a;
  --accent: #a15c00;
  --accent-dim: #ffe4b3;
  --accent-text: #ffffff;
  --cool: #0a5fa4;
  --good: #12703a;
  --warn: #8a5800;
  --bad: #b3261e;
  --grid: #d5dde4;
  --shadow-2: 0 6px 20px -10px rgb(0 0 0 / 0.25);

  /* Selected for the light surface, not an automatic flip of the dark values. */
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --seq: #2a78d6;
  --seq-soft: #86b6ef;
}

* { box-sizing: border-box; }

/*
 * The user agent's `[hidden] { display: none }` loses to any author rule that
 * sets `display`, so an author-level restatement is required or every hidden
 * flex/grid container stays visible.
 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

body { min-height: 100vh; }

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.25; }
p { margin: 0 0 var(--sp-3); }
a { color: var(--cool); }

/* ------------------------------- header ------------------------------ */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  background: color-mix(in srgb, var(--bg-raised) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

.brand { display: flex; align-items: baseline; gap: var(--sp-2); margin-right: auto; }
.brand h1 { font-size: 1.05rem; letter-spacing: 0.02em; }
.brand .mark { color: var(--accent); font-size: 1.2rem; line-height: 1; }
.brand .sub { color: var(--text-faint); font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; }

.seg {
  display: inline-flex;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.seg button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.8rem;
  padding: 5px 11px;
  cursor: pointer;
  white-space: nowrap;
}
.seg button + button { border-left: 1px solid var(--border); }
.seg button[aria-pressed='true'] { background: var(--accent); color: var(--accent-text); font-weight: 600; }
.seg button:hover:not([aria-pressed='true']) { background: var(--bg-input); color: var(--text); }

.seg-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin-right: 2px; align-self: center; }
/*
 * On a phone the header was three rows of controls before any content. The
 * "UNITS" and "ANGLE" captions are the cheapest thing to drop: Imperial/Metric
 * and MIL/MOA/Clicks say what they are without being introduced.
 */
@media (max-width: 560px) { .seg-label { display: none; } }

/*
 * The header wraps, and on a narrow phone it was wrapping to 218px — a quarter
 * of the screen spent on unit toggles before any ballistics. Tighter padding,
 * a smaller gap and slimmer segment buttons bring it back to roughly one third
 * of that without shrinking any tap target below the 40px the grouped segments
 * are already documented as using.
 */
@media (max-width: 460px) {
  .topbar { padding: 8px 10px; gap: 8px; row-gap: 6px; }
  .brand { gap: 6px; }
  .brand .sub { display: none; }
  .brand h1 { font-size: 0.95rem; }
  .topbar .inline { gap: 6px; row-gap: 6px; }
  .seg button { padding: 5px 9px; font-size: 0.75rem; }
}

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

.shell {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  gap: var(--sp-4);
  padding: var(--sp-4);
  max-width: 1680px;
  margin: 0 auto;
  align-items: start;
}

@media (max-width: 1000px) {
  /*
   * minmax(0, 1fr), not 1fr. Bare `1fr` means `minmax(auto, 1fr)`, and that
   * `auto` minimum lets a wide child — a range-card table, a long select —
   * push the column past the viewport and give the whole page a horizontal
   * scrollbar. The panel used to hide this with `overflow: hidden`, which also
   * broke sticky positioning; clamping the track is the correct fix for both.
   */
  .shell { grid-template-columns: minmax(0, 1fr); }
  /*
   * Stacked, the sidebar form comes first in the DOM — so on a phone the
   * Advanced view put forty-five fields above the tab bar and the answer. The
   * navigation and the firing solution go first; the inputs sit under them.
   */
  .shell > .panel { order: 1; }
  .shell > .inputs { order: 2; }
}

.panel {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  /*
   * `overflow: hidden` here would silently break `position: sticky` on
   * everything inside — the sticky solution readout among them — because a
   * clipping ancestor is not a valid containing block for it. `clip` on the
   * inline axis alone keeps the rounded corners tidy without capturing the
   * vertical scroll the sticky element needs.
   */
  overflow-x: clip;
}

.inputs { display: flex; flex-direction: column; gap: var(--sp-3); position: sticky; top: 68px; max-height: calc(100vh - 84px); overflow-y: auto; scrollbar-width: thin; }
@media (max-width: 1000px) { .inputs { position: static; max-height: none; } }

/*
 * The Quick tab carries its own inputs inside a tab panel rather than in the
 * sticky sidebar, so it must not inherit the sidebar's sticky positioning or
 * its own scroll region — a scroll box inside a scroll box on a phone is
 * miserable. Two columns on a wide screen, one on a narrow one.
 */
.inputs.quick {
  position: static;
  max-height: none;
  overflow: visible;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  align-items: start;
  margin-bottom: var(--sp-4);
}

/* With the Quick tab up there is no sidebar, so the output takes the width. */
.shell[data-layout='quick'] { grid-template-columns: minmax(0, 1fr); }

/* A segmented control that fills its field rather than hugging its labels. */
.seg.fill { display: flex; width: 100%; }
.seg.fill button { flex: 1 1 0; text-align: center; padding: 7px 4px; }
/* The recommended-choice marker must stay legible once the button is selected:
   a faint text token on the accent fill is not. */
.seg button[aria-pressed='true'] .faint { color: inherit; }

/*
 * The Quick tab's "what this settled without asking" list. Cards rather than a
 * three-column table: the third column is a paragraph, and a paragraph in a
 * table cell at 390 px either wraps to a ribbon or forces the whole table into
 * a horizontal scroll. Neither is readable on a phone.
 */
/* ------------------------- polish & motion --------------------------- */

/*
 * Motion is used for one thing only: to make a state change legible, so a
 * repainted panel or a toggled control does not simply blink into a new shape.
 * Nothing moves for decoration, nothing moves more than 120ms, and anybody who
 * has asked their system for less motion gets none of it.
 */
@media (prefers-reduced-motion: no-preference) {
  .seg button, .btn, .info, [role='tab'], details.card > summary, .list-item,
  .sticky-solution, .stat, .decision {
    transition: background-color 0.12s ease, color 0.12s ease,
                border-color 0.12s ease, opacity 0.12s ease;
  }
  #quickOut, #advancedOut { animation: settle 0.14s ease-out; }
  @keyframes settle { from { opacity: 0.7; } to { opacity: 1; } }
}

/*
 * Finger-sized hit areas without finger-sized icons.
 *
 * The glossary buttons are deliberately 15px circles — a 44px one next to every
 * label would shout over the labels. So the visible dot stays small and an
 * invisible pseudo-element extends the touch target around it. Same trick for
 * the tabs, which need height a phone can hit without growing the bar.
 */
.info { position: relative; }
.info::after {
  content: '';
  position: absolute;
  inset: -15px;
  border-radius: 50%;
}
[role='tab'] { min-height: 44px; }
.seg button { min-height: 40px; }
.seg.fill button { min-height: 44px; }
.btn { min-height: 44px; }
/*
 * The grouped header segments stay at 40. They sit in a two-row header that
 * should not grow a third time, they are wide so the horizontal target is
 * generous, and a mis-tap between adjacent segments is visible and instantly
 * correctable — unlike a mis-tap on a submit button.
 */
.btn.tiny { min-height: 34px; }
details.card > summary { min-height: 46px; }

/* The tab bar scrolls on a phone; make that feel deliberate rather than lost. */
.tabs {
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs [role='tab'] { scroll-snap-align: start; }

/*
 * The compact readout that sticks under the tab bar once the full dials scroll
 * away, so the number you are changing is never off screen while you change it.
 */
.sticky-solution {
  position: sticky;
  /*
   * The header is itself sticky at top: 0, so a strip that also sticks at 0
   * slides underneath it and becomes untappable. `--header-h` is measured at
   * runtime, because the header wraps to two rows on a phone and one on a wide
   * screen — a hard-coded offset would be wrong on one of them.
   */
  top: calc(var(--header-h, 64px) + 6px);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  margin-bottom: var(--sp-2);
  padding: 9px var(--sp-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--bg-raised) 92%, transparent);
  backdrop-filter: blur(8px);
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.sticky-solution:hover { background: var(--bg-raised); }
.sticky-solution[hidden] { display: none !important; }
.ss-part { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.ss-k { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-faint); }
.ss-v { font-family: var(--font-mono); font-size: 0.95rem; font-weight: 600; color: var(--accent); }
.ss-range { margin-left: auto; font-family: var(--font-mono); font-size: 0.75rem; color: var(--text-dim); }
.ss-top { color: var(--text-faint); font-size: 0.8rem; }

/*
 * The answer comes before the questions.
 *
 * Quick keeps its inputs inside the solver panel, which put five cards of form
 * above the firing solution — so the thing you opened the app for was the last
 * thing on screen. The DOM order stays as it is (the inputs must not be inside
 * a repainted region), and the panel reorders visually instead.
 */
#panel-solve { display: flex; flex-direction: column; }
#panel-solve > .mode-switch { order: 0; }
#panel-solve > #quickLoad { order: 1; margin-bottom: var(--sp-3); }
#panel-solve > #quickOut,
#panel-solve > #advancedOut { order: 2; }
#panel-solve > #quickInputs { order: 3; margin-top: var(--sp-4); margin-bottom: 0; }

/*
 * The load picker serves both modes. Stacked, the big Advanced form sits
 * below the output, which put its Bullet & load card about 2000 px down the
 * page — nobody picks a cartridge they have to scroll two screens to find.
 * Both forms bind the same state paths, so this is one more control on the
 * same path, not a second source of truth.
 *
 * Side by side there is no such problem: the form is already beside the
 * answer, and two cartridge dropdowns visible at once reads as two settings.
 */
@media (min-width: 1001px) {
  .shell[data-layout='full'] #quickLoad { display: none; }
}

/* The Quick / Advanced switch at the top of the solver tab. */
.mode-switch { margin-bottom: var(--sp-3); }
.mode-switch .seg.fill { max-width: 320px; }
.mode-switch .hint { margin: 6px 0 0; }

.decisions { display: grid; gap: var(--sp-2); }
@media (min-width: 760px) { .decisions { grid-template-columns: 1fr 1fr; } }
.decision {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-3);
}
.decision-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; }
.decision-what { font-weight: 600; font-size: 0.88rem; }
.decision-size { font-family: var(--font-mono); font-size: 0.82rem; color: var(--text-dim); white-space: nowrap; }
.decision p { margin: 6px 0 0; font-size: 0.8rem; color: var(--text-dim); line-height: 1.55; }
.decision p .badge { vertical-align: baseline; }

/* ------------------------------ cards -------------------------------- */

details.card { background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--r-md); }
details.card > summary {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 10px var(--sp-3);
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-dim);
  list-style: none;
}
details.card > summary::-webkit-details-marker { display: none; }
details.card > summary::before {
  content: '';
  width: 0; height: 0;
  border-left: 5px solid var(--text-faint);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s;
}
details.card[open] > summary::before { transform: rotate(90deg); }
details.card[open] > summary { color: var(--text); border-bottom: 1px solid var(--border); }
details.card > summary .badge { margin-left: auto; }
.card-body { padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-3); }

/*
 * The walkthrough. One row until it is opened — a beginner's aid that pushes
 * the answer off the screen is a worse first impression than none at all.
 */
details.walkthrough { margin-bottom: var(--sp-3); }
details.walkthrough > summary { text-transform: none; letter-spacing: 0; font-size: 0.84rem; }
details.walkthrough > summary .badge { text-transform: none; letter-spacing: 0; font-weight: 500; }
.walklist { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding-left: 0; list-style: none; counter-reset: walk; }
.walklist > li { display: flex; gap: var(--sp-2); align-items: baseline; }
.walklist > li::before {
  counter-increment: walk;
  content: counter(walk) '.';
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
/*
 * A plain block label, NOT the flex `.check` used elsewhere: flex turns every
 * element child into a flex item, so the bold phrase and the link inside these
 * sentences were being laid out as separate boxes and the words came out in
 * the wrong order.
 */
.walklist .check { display: block; line-height: 1.45; }
.walklist .check input { margin-right: 8px; vertical-align: -2px; }
.walklist .check:has(input:checked) { color: var(--text-faint); text-decoration: line-through; }

/* A card nested inside a card: quieter, so the nesting reads as one level. */
details.card.sub { background: transparent; border-style: dashed; }
details.card.sub > summary { padding: 8px var(--sp-2); font-size: 0.72rem; }
details.card.sub > summary .badge { text-transform: none; letter-spacing: 0; font-weight: 500; }
details.card.sub[open] > summary { border-bottom-style: dashed; }
details.card.sub > .card-body { padding: var(--sp-2); }

/*
 * Explainers — the reasoning, one tap from the answer.
 *
 * Same chrome as the input cards on purpose: a reader who has learned that a
 * chevron and a badge means "there is more inside this" should not have to
 * learn it twice. The summary carries the number the section would have led
 * with, so a closed one still answers its own question.
 */
.explainers { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-4); }
/*
 * Title on one line, its headline figure on the next, both aligned past the
 * chevron. A badge squeezed onto the end of the title fought it for width and
 * left four differently-ragged summaries; two predictable rows read as a list.
 */
details.explain > summary {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 3px var(--sp-2);
}
details.explain > summary .d-title { grid-column: 2; }
details.explain > summary .badge { grid-column: 2; justify-self: start; margin-left: 0; }
details.explain > summary .badge {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}
.explain-body { padding: var(--sp-3); }
.explain-body > :first-child { margin-top: 0; }
.explain-body > :last-child { margin-bottom: 0; }
/* Inside one, a section title is redundant — the summary is the title. */
.explain-body .section-title { margin-top: var(--sp-4); }

/* ------------------------------ fields ------------------------------- */

.field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.field > label { font-size: 0.72rem; color: var(--text-dim); display: flex; align-items: center; gap: 4px; }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--sp-2); }

input[type='number'], input[type='text'], input[type='email'], input[type='password'], select, textarea {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--font-mono);
  /*
   * 16px, not 0.9rem.
   *
   * iOS Safari zooms the whole page when you focus an input whose font-size is
   * under 16px, and then leaves you zoomed in — you have to pinch back out
   * after every field. On a phone-first app that is the single most irritating
   * thing a stylesheet can do, and the fix costs nothing but a slightly larger
   * number. The tap target comes out at 44px too, which is the other thing a
   * finger needs.
   */
  font-size: 16px;
  padding: 10px 10px;
  min-height: 44px;
  min-width: 0;
}
select { font-family: var(--font-ui); }
textarea { min-height: 0; font-size: 16px; }

input:focus-visible, select:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* Spinners eat width on a phone and the keypad is better; keep them on hover-capable pointers only. */
input[type='number']::-webkit-inner-spin-button { opacity: 0.35; }
@media (hover: none) {
  input[type='number']::-webkit-inner-spin-button,
  input[type='number']::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
}

.unit-suffix { font-size: 0.68rem; color: var(--text-faint); font-family: var(--font-mono); }

.check { display: flex; align-items: center; gap: var(--sp-2); font-size: 0.82rem; color: var(--text-dim); cursor: pointer; }
.check input { accent-color: var(--accent); width: 15px; height: 15px; }

button.btn {
  appearance: none;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  padding: 6px 12px;
  cursor: pointer;
}
button.btn:hover { border-color: var(--accent); color: var(--accent); }
button.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); font-weight: 600; }
button.btn.primary:hover { filter: brightness(1.1); color: var(--accent-text); }
button.btn.tiny { padding: 3px 8px; font-size: 0.72rem; }
.btn-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* info affordance — every technical term is clickable */
.info {
  appearance: none;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-faint);
  border-radius: 50%;
  width: 15px; height: 15px;
  font-size: 10px;
  line-height: 1;
  padding: 0;
  cursor: help;
  flex: 0 0 auto;
  font-family: var(--font-ui);
}
.info:hover { color: var(--accent); border-color: var(--accent); }

/* ------------------------------- tabs -------------------------------- */

.tabs { display: flex; gap: 2px; overflow-x: auto; border-bottom: 1px solid var(--border); background: var(--bg-sunken); scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 500;
  padding: 10px var(--sp-4);
  cursor: pointer;
  white-space: nowrap;
}
.tabs button[aria-selected='true'] { color: var(--accent); border-bottom-color: var(--accent); background: var(--bg-raised); }
.tabs button:hover:not([aria-selected='true']) { color: var(--text); }

.tabpanel { padding: var(--sp-4); }
.tabpanel[hidden] { display: none; }

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

.hero { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
@media (max-width: 560px) { .hero { grid-template-columns: 1fr; } }

.dial {
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
}
.dial.windage { border-left-color: var(--cool); }
.dial .k { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-faint); }
.dial .v {
  font-family: var(--font-mono);
  font-size: clamp(2.2rem, 7vw, 3.4rem);
  font-weight: 700;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.dial .u { font-size: 0.9rem; color: var(--text-dim); font-family: var(--font-ui); font-weight: 500; margin-left: 4px; }
.dial .aux { font-family: var(--font-mono); font-size: 0.76rem; color: var(--text-dim); margin-top: 2px; }
.dial .dir { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); }
.dial.windage .dir { color: var(--cool); }

/* ------------------------------ stat grid ---------------------------- */

/*
 * The grid lines are drawn with outlines on the cells rather than by letting a
 * darker container color show through 1px gaps. Outlines take no layout space,
 * so adjacent ones overlap into crisp single lines, and — the reason for doing
 * it this way — a partly-filled final row shows the panel background instead of
 * a large block of grid color.
 */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 1px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-top: var(--sp-3);
}
.stat {
  background: var(--bg-raised);
  padding: var(--sp-2) var(--sp-3);
  outline: 1px solid var(--border);
}
.stat .k { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-faint); display: flex; align-items: center; gap: 3px; }
.stat .v { font-family: var(--font-mono); font-size: 1.02rem; font-variant-numeric: tabular-nums; margin-top: 1px; }
.stat .v small { font-size: 0.66rem; color: var(--text-faint); font-family: var(--font-ui); }
.stat.em .v { color: var(--accent); }
.stat.cool .v { color: var(--cool); }

.section-title {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-faint);
  margin: var(--sp-5) 0 var(--sp-2);
  display: flex; align-items: center; gap: var(--sp-2);
}
.section-title::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.section-title:first-child { margin-top: 0; }

/* ------------------------------ notices ------------------------------ */

.notices { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.notice {
  display: flex; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  font-size: 0.82rem;
  border: 1px solid;
  line-height: 1.45;
}
.notice .ico { flex: 0 0 auto; font-weight: 700; font-family: var(--font-mono); }
/*
 * Severity classes are prefixed. They used to be bare `.error` / `.warning` /
 * `.info`, and `.info` collided with the glossary definition button of the same
 * name — which is a 15px circle with `flex: 0 0 auto`. Every info-severity
 * notice was silently being squashed into a 26px box. Prefixing removes the
 * whole class of collision rather than fighting it with specificity.
 */
.notice.sev-error { border-color: var(--bad); background: color-mix(in srgb, var(--bad) 12%, transparent); }
.notice.sev-error .ico { color: var(--bad); }
.notice.sev-warning { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.notice.sev-warning .ico { color: var(--warn); }
.notice.sev-info { border-color: var(--border-strong); background: var(--bg-sunken); color: var(--text-dim); }
.notice.sev-info .ico { color: var(--cool); }
/* The message must take the remaining width, not size to its own content. */
.notice > span:not(.ico) { flex: 1 1 auto; min-width: 0; }

.badge {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  color: var(--text-dim);
  font-weight: 600;
  white-space: nowrap;
}
.badge.good { border-color: var(--good); color: var(--good); }
.badge.warn { border-color: var(--warn); color: var(--warn); }
.badge.bad { border-color: var(--bad); color: var(--bad); }
.badge.accent { border-color: var(--accent); color: var(--accent); }

/* ------------------------------- tables ------------------------------ */

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-md); }
table { border-collapse: collapse; width: 100%; font-family: var(--font-mono); font-size: 0.8rem; font-variant-numeric: tabular-nums; }
thead th {
  position: sticky; top: 0;
  background: var(--bg-sunken);
  color: var(--text-dim);
  font-family: var(--font-ui);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
  text-align: right;
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
thead th:first-child { text-align: left; }
tbody td { padding: 5px 10px; text-align: right; border-bottom: 1px solid var(--grid); white-space: nowrap; }
tbody td:first-child { text-align: left; color: var(--text-dim); }
tbody tr:hover td { background: var(--bg-sunken); }
tbody tr.mark td { background: color-mix(in srgb, var(--accent) 13%, transparent); }
tbody tr.mark td:first-child { color: var(--accent); font-weight: 700; }
tbody tr.subsonic td { color: var(--text-faint); }
tbody tr.transonic td:first-child::after { content: ' ~'; color: var(--warn); }
table.plain { font-family: var(--font-ui); font-size: 0.84rem; }
table.plain td:first-child { color: var(--text); }
table.plain td, table.plain th { text-align: left; }
table.plain td.num, table.plain th.num { text-align: right; font-family: var(--font-mono); }

/*
 * On a narrow screen the range card is six columns of numbers that only just
 * miss fitting. Tightening the horizontal padding is what makes it fit rather
 * than scroll sideways — worth it here, because the Quick tab exists to be read
 * at arm's length without fiddling.
 */
@media (max-width: 460px) {
  #quickOut thead th { padding-left: 6px; padding-right: 6px; }
  #quickOut tbody td { padding-left: 6px; padding-right: 6px; }
}

/* ------------------------------- chart ------------------------------- */

.chart-wrap { border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-sunken); padding: var(--sp-2); }
svg.chart { display: block; width: 100%; height: auto; }
svg.chart text { font-family: var(--font-mono); font-size: 10px; fill: var(--text-faint); }
svg.chart .axis { stroke: var(--border-strong); stroke-width: 1; }
svg.chart .gridline { stroke: var(--grid); stroke-width: 1; }
svg.chart .zeroline { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
svg.chart .trace { fill: none; stroke-width: 2; stroke-linejoin: round; }
svg.chart .trace.path { stroke: var(--accent); }
svg.chart .trace.wind { stroke: var(--cool); }
svg.chart .trace.vel { stroke: var(--good); }
/* Bars: thin marks, rounded data-end, anchored to the baseline. */
svg.chart .bar { fill: var(--seq); }
svg.chart .bar.status { fill: var(--bad); }
svg.chart .bar.s1 { fill: var(--series-1); }
svg.chart .bar.s2 { fill: var(--series-2); }
svg.chart .bar.s3 { fill: var(--series-3); }
svg.chart .bar-label { fill: var(--text); font-size: 11px; }
svg.chart .bar-value { fill: var(--text-dim); font-size: 11px; font-variant-numeric: tabular-nums; }
svg.chart .baseline { stroke: var(--border-strong); stroke-width: 1; }
svg.chart .band { fill: var(--warn); opacity: 0.1; }
.legend { display: flex; gap: var(--sp-4); flex-wrap: wrap; font-size: 0.74rem; color: var(--text-dim); margin-top: var(--sp-2); }
.legend span { display: inline-flex; align-items: center; gap: 5px; }
.legend i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }

/* ---------------------------- library list --------------------------- */

.list { display: flex; flex-direction: column; gap: 1px; background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; }
.list-item { outline: 1px solid var(--border); }
.list-item {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-2);
  align-items: center;
  background: var(--bg-raised);
  padding: var(--sp-2) var(--sp-3);
  cursor: pointer;
  border: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  width: 100%;
}
.list-item:hover { background: var(--bg-sunken); }
.list-item .t { font-size: 0.86rem; font-weight: 500; }
.list-item .d { font-size: 0.72rem; color: var(--text-faint); font-family: var(--font-mono); margin-top: 1px; }
.list-item .r { display: flex; gap: var(--sp-2); align-items: center; }
.list-item .bc { font-family: var(--font-mono); font-size: 0.8rem; color: var(--accent); }

/* --------------------------- glossary entry -------------------------- */

.term { border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-3); background: var(--bg-raised); }
.term h4 { font-size: 0.95rem; display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.term .cat { font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-faint); }
.term .short { color: var(--text); margin: var(--sp-2) 0; font-weight: 500; }
.term .long { color: var(--text-dim); font-size: 0.86rem; }
.term .long p { margin: 0 0 0.7em; }
.term .long p:last-child { margin-bottom: 0; }
.term .formula { font-family: var(--font-mono); font-size: 0.8rem; background: var(--bg-sunken); border-left: 2px solid var(--accent); padding: 6px 10px; border-radius: var(--r-sm); margin: var(--sp-2) 0; overflow-x: auto; }
.term .pitfall { font-size: 0.82rem; border-left: 2px solid var(--warn); padding: 4px 10px; margin-top: var(--sp-2); color: var(--text-dim); }
.term .pitfall b { color: var(--warn); font-weight: 600; }
.term .rel { display: flex; gap: 6px; flex-wrap: wrap; margin-top: var(--sp-2); }
.term .rel button { appearance: none; background: var(--bg-sunken); border: 1px solid var(--border); color: var(--text-dim); font: inherit; font-size: 0.7rem; padding: 2px 8px; border-radius: 999px; cursor: pointer; }
.term .rel button:hover { border-color: var(--accent); color: var(--accent); }

/* ----------------------------- definition drawer --------------------- */

dialog.drawer {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  max-height: 78vh;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--bg-raised);
  color: var(--text);
  padding: 0;
  box-shadow: var(--shadow-2);
}
dialog.drawer::backdrop { background: rgb(0 0 0 / 0.55); }
.drawer-head { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg-raised); }
.drawer-head h3 { font-size: 0.9rem; margin-right: auto; }
.explain-body { padding: var(--sp-4); overflow-y: auto; }

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

.step { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3); align-items: start; }
.step-n {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--accent-text);
  background: var(--accent);
  width: 26px; height: 26px;
  border-radius: 50%;
  display: grid; place-items: center;
  flex: 0 0 auto;
}
.step-b { min-width: 0; }
.step-b h4 { font-size: 0.95rem; margin-bottom: 4px; }
.step-b p { margin: 0 0 6px; font-size: 0.88rem; }
.step-b .do { color: var(--text); }
.step-b .why { color: var(--text-dim); }
.step-b .why b { color: var(--cool); font-weight: 600; }
.step-b .watch { color: var(--text-dim); border-left: 2px solid var(--warn); padding-left: 10px; }
.step-b .watch b { color: var(--warn); font-weight: 600; }
.step-b p:last-child { margin-bottom: 0; }

/* An inline button that reads as a link, for cross-references in prose. */
button.linkish {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--cool);
  text-decoration: underline;
  cursor: pointer;
}
button.linkish:hover { color: var(--accent); }

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

.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.small { font-size: 0.8rem; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
.hint { font-size: 0.74rem; color: var(--text-faint); line-height: 1.45; }
.pos { color: var(--good); }
.neg { color: var(--bad); }
.spacer { flex: 1; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--sp-3); }
.stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.inline { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* ------------------------------- print ------------------------------- */

@media print {
  :root { --bg: #fff; --bg-raised: #fff; --bg-sunken: #fff; --text: #000; --text-dim: #333; --text-faint: #666; --border: #999; --grid: #ccc; --accent: #000; }
  .topbar, .inputs, .tabs, .no-print, dialog { display: none !important; }
  .shell { grid-template-columns: 1fr; padding: 0; }
  .panel { border: 0; }
  .tabpanel[hidden] { display: block !important; }
  table { font-size: 9pt; }
  thead th { background: #eee !important; }
  body { font-size: 10pt; }
}
