/* ==========================================================================
   MTG COMMANDER DISCOVERY — BASE STYLES & COMPONENTS
   ==========================================================================
   Requires tokens.css loaded first, and the same Google Fonts <link> the
   three sibling sites use (this file intentionally does NOT @import them —
   an @import blocks the render tree and throws away the preconnect win;
   put this in <head> instead, exactly as the source sites do):

     <link rel="preconnect" href="https://fonts.googleapis.com">
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">

   Section map:
     1. Reset & element defaults
     2. Typography primitives (eyebrow, masthead, headings)
     3. Focus & utility
     4. Top nav (4-mode switcher)                    — NEW
     5. Buttons
     6. Inputs, search, combobox                      — combobox is NEW
     7. Chips, segmented control, filter sidebar       — segmented is NEW
     8. Range slider                                   — NEW
     9. Badges / tags / pills
    10. Card portrait + skeleton                       — NEW
    11. Nav arrows / prev-next bar                      — NEW
    12. Favorite toggle                                 — NEW
    13. Color-identity pips                             — NEW
    14. Results grid / list
    15. Login / auth form                               — NEW
    16. Data table                                      — NEW
    17. Toast                                           — NEW
    18. Generic card / panel primitives (from source)
    19. Application shell + shared commander view
    20. Responsive
    21. Commander view breakpoints
   ==========================================================================

   MOBILE-FIRST. The commander view (§19, §21) is the screen the owner
   actually uses, on an iPad and a phone. Its base rules are the phone layout;
   the breakpoints add to it rather than the other way round. Before changing
   any height in §19's `.commander-layout` block, read the layout contract at
   the top of it -- those numbers are what stop the Next button moving out
   from under the pointer on every card advance. */

/* ── 1. Reset & element defaults ─────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Bug A, half two: on a desktop browser with classic (space-taking)
     scrollbars, a page that crosses the viewport height gains a scrollbar and
     LOSES ~15px of width, which re-wraps everything and shifts the layout
     sideways -- then loses the scrollbar on the next card and shifts back.
     Reserving the gutter unconditionally makes the page width a constant.
     Ignored by overlay-scrollbar platforms (iOS/iPadOS), where it is a no-op
     because there was never a shift to fix. */
  scrollbar-gutter: stable;
}
html, body { margin: 0; padding: 0; }

body {
  background:
    radial-gradient(ellipse 80% 50% at 50% 0%, rgba(67, 56, 202, 0.04), transparent 60%),
    radial-gradient(ellipse 60% 40% at 100% 100%, rgba(234, 88, 12, 0.03), transparent 60%),
    var(--ink);
  color: var(--parchment);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-base);
  min-height: 100vh;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, picture, svg { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }
h1, h2, h3, h4, h5, h6, p, figure, blockquote { margin: 0; }

/* Default link treatment. NOTE — accessibility fix vs. the source sites:
   the three originals default text-links to --gold-dim (4.17–4.47:1, only
   clears the 3:1 "large text" bar) and darken to --gold on hover. That is
   backwards for AA on small link text, so this file starts at the safe
   color (--gold, 7.4–7.9:1) and lets hover move to --gold-dim — hover is a
   transient reinforcement paired with an underline change, not the only
   cue, so the dip there is an accepted, minor trade-off. See
   research/04-design-system.md §5. */
a {
  color: var(--gold);
  text-decoration: none;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}
a:hover { color: var(--gold-dim); }
a:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* Dotted-underline link, for inline references (rule citations, source
   links) — lifted directly from .modal-rule-source a / .modal-rule-list dt a */
.link-dotted {
  color: var(--gold); /* set explicitly, not inherited from `a`, so this also works on
                          a plain <button> (e.g. the oracle-text "show more" toggle) */
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  border-bottom: 1px dotted var(--gold);
  padding-bottom: 1px;
}
.link-dotted:hover { color: var(--gold-dim); border-bottom-color: var(--gold-dim); }

code, kbd, pre { font-family: var(--font-mono); }
code { color: var(--gold-dim); font-size: 0.92em; }
kbd {
  font-family: var(--font-mono);
  background: var(--ink-3);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  color: var(--parchment-dim);
  font-size: var(--fs-2xs);
}
hr { border: none; border-top: 1px solid var(--rule); margin: var(--space-11) 0; }

/* ── 2. Typography primitives (verbatim idiom from the source sites) ──── */

.eyebrow {
  font-size: var(--fs-sm);
  letter-spacing: var(--ls-eyebrow-lg);
  color: var(--gold);
  text-transform: uppercase;
  margin-bottom: var(--space-8);
}
.masthead {
  padding: var(--space-16) var(--space-14) var(--space-11);
  text-align: center;
  border-bottom: 1px solid var(--rule);
  position: relative;
}
.masthead::before, .masthead::after {
  content: "✦";
  color: var(--gold-dim);
  font-size: 14px;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.masthead::before { left: var(--space-14); }
.masthead::after { right: var(--space-14); }
h1.title, .title {
  font-weight: var(--fw-extrabold);
  font-size: var(--fs-hero);
  letter-spacing: var(--ls-wide);
  margin: 0;
  color: var(--parchment);
  text-shadow: 0 0 30px rgba(67, 56, 202, 0.06);
}
.title-rule {
  display: inline-block;
  width: 80px; height: 1px;
  background: linear-gradient(to right, transparent, var(--gold), transparent);
  margin: var(--space-10) 0;
}
.subtitle {
  font-style: italic;
  color: var(--parchment-dim);
  font-size: var(--fs-md);
  margin: 0 auto;
  max-width: 720px;
}
.source-note {
  color: var(--parchment-mute);
  font-size: var(--fs-sm-alt);
  margin-top: var(--space-8);
  font-family: var(--font-mono);
  letter-spacing: var(--ls-tight);
}
.version-pill {
  display: inline-block;
  margin-top: var(--space-7);
  padding: 3px 10px;
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-eyebrow);
  background: rgba(67, 56, 202, 0.10);
  border: 1px solid var(--gold-dim);
  color: var(--gold);
  text-transform: uppercase;
  border-radius: var(--radius-hairline);
}

/* ── 3. Focus & utility ─────────────────────────────────────────────── */

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

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

/* Hide, but KEEP THE SPACE. The counterpart to the global [hidden] rule
   further down: `[hidden]` removes an element from the flow, which is exactly
   what you must not do to anything sharing a column with the commander view's
   reserved-height blocks (an "expand oracle text" toggle that only some cards
   need, an EDHREC link that 7 commanders don't have). visibility:hidden also
   takes the element out of the tab order, so nothing focusable hides here. */
.is-invisible { visibility: hidden; }

/* ── 4. Top nav — the four-mode switcher (NEW) ─────────────────────────
   Same sticky-blur-hairline recipe as the source sites' `.controls`, with
   a tab-style active indicator added (a gold underline, echoing .title-rule) */

.top-nav {
  position: sticky;
  top: 0;
  z-index: var(--z-controls);
  display: flex;
  align-items: center;
  gap: var(--space-11);
  padding: var(--space-6) var(--space-14);
  background: linear-gradient(to bottom, var(--ink) 70%, rgba(246, 247, 249, 0.94));
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--rule);
}
.top-nav-brand {
  display: flex; align-items: center; gap: var(--space-4);
  text-decoration: none; color: var(--parchment); flex: none;
}
.top-nav-brand:hover { color: var(--parchment); }
.top-nav-mark { color: var(--gold-dim); font-size: 15px; }
.top-nav-name {
  font-weight: var(--fw-extrabold);
  font-size: var(--fs-lg);
  letter-spacing: var(--ls-snug);
}
.top-nav-name-accent { color: var(--gold); }
.top-nav-modes {
  display: flex; gap: var(--space-3); flex-wrap: wrap;
}
.top-nav-link {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font: var(--fw-semibold) var(--fs-xs) var(--font-sans);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--parchment-dim);
  text-decoration: none;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-sm);
  border-bottom: 2px solid transparent;
  transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}
.top-nav-link:hover { color: var(--parchment); background: var(--ink-3); }
.top-nav-link.is-active { color: var(--gold); border-bottom-color: var(--gold); }
.top-nav-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.top-nav-count {
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: var(--fw-bold);
  background: var(--ink-3); border: 1px solid var(--rule-strong); color: var(--parchment-mute);
  padding: 1px 5px; border-radius: var(--radius-sm);
}
.top-nav-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-6); flex: none; }

/* ── 5. Buttons ─────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-3);
  background: var(--ink-3);
  border: 1px solid var(--rule-strong);
  color: var(--parchment-dim);
  font: var(--fw-semibold) var(--fs-xs) var(--font-sans);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  padding: var(--space-5) var(--space-7);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
  white-space: nowrap;
}
.btn:hover:not(:disabled) { border-color: var(--gold-dim); color: var(--gold); }
.btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.btn:disabled { opacity: 0.3; cursor: default; }

.btn-primary { background: var(--gold); border-color: var(--gold); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--gold-dim); border-color: var(--gold-dim); color: #fff; }

.btn-ghost { background: transparent; border-color: var(--gold-dim); color: var(--gold); }
.btn-ghost:hover:not(:disabled) { background: var(--gold); border-color: var(--gold); color: #fff; }

.btn-danger { color: var(--c-action); border-color: color-mix(in srgb, var(--c-action) 45%, transparent); }
.btn-danger:hover:not(:disabled) { background: var(--c-action); border-color: var(--c-action); color: #fff; }

.btn-icon { width: 40px; height: 40px; padding: 0; flex: none; font-size: 16px; }
.btn-block { width: 100%; }

/* ── 6. Inputs, search, combobox ───────────────────────────────────── */

.field-label {
  display: block;
  font: var(--fw-bold) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--parchment-mute);
  margin-bottom: var(--space-3);
}

.input {
  width: 100%;
  background: var(--ink-2);
  border: 1px solid var(--rule-strong);
  color: var(--parchment);
  font: var(--fw-regular) var(--fs-body) var(--font-sans);
  padding: var(--space-5) var(--space-7);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color 0.2s;
}
.input:focus { border-color: var(--gold); }
.input::placeholder { color: var(--parchment-mute); font-style: italic; }
.input:disabled { opacity: 0.5; cursor: not-allowed; }
.input.has-error { border-color: var(--c-action); }

.search-field {
  position: relative;
  /* This used to read `flex: 1 1 280px`, which is only correct in a ROW flex
     parent. Its real parent in the filter sidebar is `.filter-group`, which is
     `flex-direction: column`, so flex-basis resolved against the BLOCK axis and
     made the field 280px TALL around a 41px input — stranding the ⌕ glyph
     (positioned at top: 50%) in ~240px of dead space. Measured in-browser at
     390px and 834px by the search agent. Sizing the inline axis explicitly is
     right in both directions, and min() keeps it from overflowing a narrow row. */
  flex: 1 1 auto;
  min-width: min(280px, 100%);
}
.search-field::before {
  content: "⌕";
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  color: var(--gold-dim); font-size: 18px; pointer-events: none;
}
.search-field .input { padding-left: 38px; }

/* Searchable combobox — creature-type / tag picker (NEW; no equivalent
   existed in the source sites, built from the .input + .card left-border
   idioms already established there) */
.combobox { position: relative; }
.combobox-listbox {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--ink-2);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-popover);
  max-height: 280px;
  overflow-y: auto;
  z-index: var(--z-dropdown);
  padding: var(--space-3);
  display: flex; flex-direction: column; gap: 2px;
}
.combobox-option {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
  cursor: pointer;
  font-size: var(--fs-base-tight);
  color: var(--parchment);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.combobox-option:hover, .combobox-option.is-focused { background: var(--ink-3); }
.combobox-option[aria-selected="true"] {
  border-left-color: var(--gold);
  background: color-mix(in srgb, var(--gold) 7%, var(--ink-2));
  color: var(--gold);
  font-weight: var(--fw-semibold);
}
.combobox-option-count { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--parchment-mute); }
.combobox-empty { padding: var(--space-8); text-align: center; color: var(--parchment-mute); font-style: italic; font-size: var(--fs-base); }

/* ── 7. Chips, segmented control, filter sidebar ───────────────────── */

.chip-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.chip-toggle {
  background: var(--ink-2);
  border: 1px solid var(--rule-strong);
  color: var(--parchment-dim);
  font: var(--fw-semibold) var(--fs-xs) var(--font-sans);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  padding: var(--space-4) var(--space-7);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
}
.chip-toggle:hover { color: var(--parchment); border-color: var(--gold-dim); }
.chip-toggle.is-active { background: var(--gold); border-color: var(--gold); color: #fff; }
.chip-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* 3-way comparator (Exactly / Includes / Fits within) — NEW, needed for
   the color-identity filter's match mode (research/02-filter-spec.md §2.1) */
.segmented { display: inline-flex; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm); overflow: hidden; }
.segmented-option {
  background: var(--ink-2);
  color: var(--parchment-dim);
  font: var(--fw-semibold) var(--fs-xs) var(--font-sans);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  padding: var(--space-4) var(--space-7);
  cursor: pointer;
  border-right: 1px solid var(--rule-strong);
  transition: all var(--transition-fast);
}
.segmented-option:last-child { border-right: none; }
.segmented-option:hover { background: var(--ink-3); color: var(--parchment); }
.segmented-option.is-active { background: var(--gold); color: #fff; }
.segmented-option:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

.filter-sidebar {
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  padding: var(--space-9);
  display: flex; flex-direction: column; gap: var(--space-11);
}
.filter-group { display: flex; flex-direction: column; gap: var(--space-5); }
.filter-group-label {
  display: flex; align-items: baseline; justify-content: space-between;
  font: var(--fw-bold) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--parchment-mute);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--rule);
}
.filter-group-clear {
  font-family: var(--font-mono); font-size: var(--fs-2xs); letter-spacing: 0;
  text-transform: none; color: var(--gold); background: none; border: none; cursor: pointer;
}
.filter-group-clear:hover { color: var(--gold-dim); }
.filter-count {
  font: var(--fw-semibold) var(--fs-2xs) var(--font-mono);
  color: var(--parchment-mute); margin-top: var(--space-2);
}

/* ── 8. Range slider (NEW) ─────────────────────────────────────────── */

.range-field { display: flex; flex-direction: column; gap: var(--space-4); }
.range-track-labels {
  display: flex; justify-content: space-between;
  font: var(--fw-semibold) var(--fs-2xs) var(--font-mono);
  color: var(--parchment-mute); letter-spacing: var(--ls-wider);
}
.range-slider { position: relative; height: 20px; display: flex; align-items: center; }

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: var(--radius-full);
  background: linear-gradient(
    to right,
    var(--gold) 0%, var(--gold) var(--range-fill, 50%),
    var(--rule-strong) var(--range-fill, 50%), var(--rule-strong) 100%
  );
  outline: none;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--ink-2);
  border: 2px solid var(--gold);
  cursor: pointer;
  box-shadow: var(--shadow-art);
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}
input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); }
input[type="range"]::-moz-range-thumb {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--ink-2);
  border: 2px solid var(--gold);
  cursor: pointer;
  box-shadow: var(--shadow-art);
}
input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--radius-full); background: var(--rule-strong); }
input[type="range"]::-moz-range-progress { height: 4px; border-radius: var(--radius-full); background: var(--gold); }
input[type="range"]:focus-visible { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--gold); outline-offset: 2px; }
input[type="range"]:focus-visible::-moz-range-thumb { outline: 2px solid var(--gold); outline-offset: 2px; }
input[type="range"]:disabled { opacity: 0.4; cursor: default; }

/* Dual-handle range (e.g. CMC 2–5): stack two native <input type=range> in
   one .range-dual wrapper; JS keeps --range-min/--range-max (percentages)
   in sync with each thumb's position to draw the fill bar between them. */
.range-dual { position: relative; height: 20px; }
.range-dual input[type="range"] {
  position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
  background: transparent; pointer-events: none; margin: 0;
}
.range-dual input[type="range"]::-webkit-slider-thumb { pointer-events: auto; }
.range-dual input[type="range"]::-moz-range-thumb { pointer-events: auto; }
.range-dual-track {
  position: absolute; left: 0; right: 0; top: 50%; height: 4px; transform: translateY(-50%);
  border-radius: var(--radius-full); background: var(--rule-strong);
}
.range-dual-fill {
  position: absolute; top: 50%; height: 4px; transform: translateY(-50%);
  border-radius: var(--radius-full); background: var(--gold);
  left: var(--range-min, 0%);
  right: calc(100% - var(--range-max, 100%));
}

/* ── 9. Badges / tags / pills ───────────────────────────────────────── */

.tag {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font: var(--fw-medium) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--parchment-mute);
  background: var(--ink-3);
  border: 1px solid var(--rule);
  padding: 2px 7px;
  border-radius: var(--radius-sm);
}
.pill {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font: var(--fw-bold) var(--fs-xs) var(--font-mono);
  padding: 3px 10px;
  border-radius: var(--radius-sm);
  letter-spacing: var(--ls-wide);
}
.pill-accent {
  color: var(--gold);
  background: color-mix(in srgb, var(--gold) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--gold) 35%, transparent);
}
/* Category-colored badge — set --cat on an ancestor (or inline) to recolor.
   Direct port of the source sites' .band-cr / .lvl-num formula. */
.pill-cat {
  color: var(--cat, var(--gold));
  background: color-mix(in srgb, var(--cat, var(--gold)) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--cat, var(--gold)) 35%, transparent);
}

/* ── 10. Card portrait + skeleton (NEW) ─────────────────────────────
   Scryfall art is ~488×680 and hotlinked, so this always reserves the
   correct aspect ratio up front (no layout shift) and shows a shimmering
   placeholder — never a blank box — while the image loads. */

.card-portrait {
  position: relative;
  width: 100%;
  aspect-ratio: 488 / 680;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--ink-3);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow-art);
}
.card-portrait-img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  opacity: 1;
  transition: opacity var(--transition-base);
}
.card-portrait.is-loading .card-portrait-img { opacity: 0; }

.card-portrait-skeleton {
  position: absolute; inset: 0;
  background: linear-gradient(100deg, var(--ink-3) 30%, var(--ink) 50%, var(--ink-3) 70%);
  background-size: 200% 100%;
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}
.card-portrait.is-loaded .card-portrait-skeleton,
.card-portrait.is-error .card-portrait-skeleton { display: none; }
@keyframes skeleton-sweep {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .card-portrait-skeleton { animation: none; background: var(--ink-3); }
}

.card-portrait-noart {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-4);
  color: var(--parchment-mute);
  background: var(--ink-3);
}
.card-portrait-noart .cpn-letter { font: var(--fw-bold) 64px var(--font-mono); line-height: 1; }
.card-portrait-noart .cpn-label {
  font: var(--fw-semibold) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
.card-portrait-error {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-5);
  background: var(--ink-3);
  color: var(--parchment-mute);
  padding: var(--space-8);
  text-align: center;
  font-size: var(--fs-base-tight);
}

/* ── 11. Nav arrows / prev-next bar (NEW) ───────────────────────────── */

.nav-bar {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-8);
  padding-top: var(--space-9);
  margin-top: var(--space-9);
  border-top: 1px solid var(--rule);
  flex-wrap: wrap;
}
.nav-bar .btn { flex: 0 1 auto; }
.nav-bar-hint {
  display: flex; gap: var(--space-6); justify-content: center; flex-wrap: wrap;
  font: var(--fw-medium) var(--fs-2xs) var(--font-mono);
  color: var(--parchment-mute);
  letter-spacing: var(--ls-wide);
  margin-top: var(--space-4);
  width: 100%;
}

/* ── 12. Favorite toggle (NEW) ───────────────────────────────────────
   A drawn ribbon/bookmark (inline SVG, stroke-only when off, filled when
   on) rather than a star — reads unambiguously as "save this" without
   borrowing any Scryfall/WotC iconography. */

.favorite-toggle {
  display: inline-flex; align-items: center; gap: var(--space-4);
  background: var(--ink-2);
  border: 1px solid var(--rule-strong);
  color: var(--parchment-dim);
  font: var(--fw-semibold) var(--fs-xs) var(--font-sans);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  padding: var(--space-5) var(--space-7);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
}
.favorite-toggle:hover { border-color: var(--gold-dim); color: var(--gold); }
.favorite-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.favorite-mark {
  flex: none;
  color: var(--parchment-mute);
  transition: color var(--transition-fast);
}
.favorite-mark path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linejoin: round;
  transition: fill var(--transition-fast);
}
.favorite-toggle:hover .favorite-mark { color: var(--gold-dim); }

.favorite-toggle[aria-pressed="true"] {
  border-color: var(--gold);
  color: var(--gold);
  background: color-mix(in srgb, var(--gold) 8%, var(--ink-2));
}
.favorite-toggle[aria-pressed="true"] .favorite-mark { color: var(--gold); }
.favorite-toggle[aria-pressed="true"] .favorite-mark path { fill: currentColor; }

.favorite-toggle.is-animating .favorite-mark { animation: favorite-pop var(--transition-pop); }
@keyframes favorite-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.35); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .favorite-toggle.is-animating .favorite-mark { animation: none; }
}

.favorite-toggle.is-icon-only { padding: var(--space-4); }
.favorite-toggle.is-icon-only .favorite-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* Overlay placement on a grid result card's portrait */
.card-portrait .favorite-toggle.is-overlay {
  position: absolute; top: var(--space-4); right: var(--space-4);
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(4px);
}

/* ── 13. Color-identity pips (NEW) ──────────────────────────────────
   Deliberately NOT Scryfall's circular pastel mana symbols: these are
   flat rounded-square letter chips (W/U/B/R/G/C) using the site's own
   "colored text + tinted bg + tinted border" badge formula (see .pill-cat
   above / .band-cr in the source). See research/04-design-system.md §2
   for the full contrast-audited palette and rationale. */

.identity-row { display: inline-flex; gap: var(--space-2); align-items: center; }
.identity-pip {
  --pip-color: var(--mana-c);
  width: 22px; height: 22px;
  border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  font: var(--fw-bold) 11px var(--font-mono);
  color: var(--pip-color);
  background: color-mix(in srgb, var(--pip-color) 10%, var(--ink-2));
  border: 1px solid color-mix(in srgb, var(--pip-color) 40%, transparent);
  flex: none;
}
.identity-pip[data-color="W"] { --pip-color: var(--mana-w); }
.identity-pip[data-color="U"] { --pip-color: var(--mana-u); }
.identity-pip[data-color="B"] { --pip-color: var(--mana-b); }
.identity-pip[data-color="R"] { --pip-color: var(--mana-r); }
.identity-pip[data-color="G"] { --pip-color: var(--mana-g); }
.identity-pip[data-color="C"], .identity-pip.is-colorless { --pip-color: var(--mana-c); }
.identity-pip.is-inactive {
  --pip-color: var(--parchment-mute);
  opacity: 0.35;
  background: var(--ink-3);
  border-style: dashed;
}
.identity-row.is-lg .identity-pip { width: 28px; height: 28px; font-size: 13px; border-radius: var(--radius-md); }
.identity-row.is-sm .identity-pip { width: 16px; height: 16px; font-size: 8.5px; border-radius: var(--radius-hairline); }
/* Mana-cost rows also carry generic ("{3}") and hybrid ("{G/W}") symbols, which
   are 1-3 glyphs wide -- a fixed 22px square clips them. Only the cost variant
   grows; the colour-identity row stays a strict grid of squares. */
.identity-row.is-cost .identity-pip { width: auto; min-width: 22px; padding: 0 4px; }

/* Compact alternative for tight grid tiles — a 3px gradient rule instead
   of five separate chips. Give each present color a flex segment. */
.identity-bar {
  height: 3px;
  border-radius: var(--radius-full);
  display: flex;
  overflow: hidden;
  background: var(--rule);
}
.identity-bar span { flex: 1; }
.identity-bar span[data-color="W"] { background: var(--mana-w); }
.identity-bar span[data-color="U"] { background: var(--mana-u); }
.identity-bar span[data-color="B"] { background: var(--mana-b); }
.identity-bar span[data-color="R"] { background: var(--mana-r); }
.identity-bar span[data-color="G"] { background: var(--mana-g); }
.identity-bar span[data-color="C"] { background: var(--mana-c); }

/* ── 14. Results grid / list ────────────────────────────────────────── */

.results-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-7);
}
.result-card {
  position: relative;
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.result-card:hover { transform: translateY(-3px); border-color: var(--gold); box-shadow: var(--shadow-art-hover); }
.result-card:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.result-card .card-portrait { border-radius: 0; border: none; border-bottom: 1px solid var(--rule); box-shadow: none; }
.result-card-body { padding: var(--space-6) var(--space-7) var(--space-7); display: flex; flex-direction: column; gap: var(--space-3); }
.result-card-name { font: var(--fw-bold) var(--fs-base-tight) var(--font-sans); color: var(--parchment); line-height: var(--lh-snug); }
.result-card-type {
  font: var(--fw-medium) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--parchment-mute);
}
.result-card-meta { display: flex; align-items: center; justify-content: space-between; margin-top: var(--space-2); }

.results-list { display: flex; flex-direction: column; gap: var(--space-4); }
.result-row {
  display: flex; gap: var(--space-7); align-items: center;
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--gold-dim);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.result-row:hover { border-color: var(--gold-dim); background: var(--ink-3); }
.result-row:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.result-row-thumb { width: 56px; aspect-ratio: 488 / 680; border-radius: var(--radius-sm); overflow: hidden; flex: none; background: var(--ink-3); }
.result-row-thumb img { width: 100%; height: 100%; object-fit: cover; }
.result-row-body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: var(--space-2); }
.result-row-name { font: var(--fw-bold) var(--fs-base-tight) var(--font-sans); color: var(--parchment); }
.result-row-type {
  font: var(--fw-medium) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--parchment-mute);
}
.result-row-actions { display: flex; align-items: center; gap: var(--space-5); flex: none; }

.empty-state {
  text-align: center;
  padding: var(--space-18) 0;
  color: var(--parchment-mute);
  font-style: italic;
  font-size: var(--fs-md);
  grid-column: 1 / -1;
}

/* ── 15. Login / auth form (NEW) ────────────────────────────────────── */

.auth-card {
  max-width: 380px;
  margin: 0 auto;
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  padding: var(--space-13) var(--space-11) var(--space-11);
  box-shadow: var(--shadow-card-hover);
}
.auth-card .eyebrow { text-align: center; }
.auth-card h1 {
  text-align: center;
  font: var(--fw-extrabold) 26px var(--font-sans);
  color: var(--parchment);
  margin: 0 0 var(--space-11);
}
.form-group { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-8); }
.form-error { font-size: var(--fs-sm); color: var(--c-action); font-style: italic; }
.form-group.has-error .input { border-color: var(--c-action); }
.form-footer { margin-top: var(--space-9); text-align: center; font-size: var(--fs-base-tight); color: var(--parchment-mute); }
.form-footer a { color: var(--gold); }
.form-footer a:hover { color: var(--gold-dim); }
.form-divider {
  display: flex; align-items: center; gap: var(--space-6);
  margin: var(--space-9) 0;
  color: var(--parchment-mute);
  font: var(--fw-medium) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
.form-divider::before, .form-divider::after { content: ""; flex: 1; height: 1px; background: var(--rule); }

/* ── 16. Data table (NEW) ───────────────────────────────────────────── */

.data-table-wrap { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--fs-base-tight); }
.data-table th {
  text-align: left;
  font: var(--fw-bold) var(--fs-2xs) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--parchment-mute);
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--rule-strong);
  white-space: nowrap;
}
.data-table td {
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--rule);
  color: var(--parchment);
}
.data-table tbody tr { transition: background var(--transition-fast); }
.data-table tbody tr:hover { background: var(--ink-3); }
.data-table td.num, .data-table th.num { text-align: right; font-family: var(--font-mono); }

/* ── 17. Toast (NEW) ────────────────────────────────────────────────── */

.toast {
  position: fixed; left: 50%; bottom: var(--space-14);
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex; align-items: center; gap: var(--space-7);
  background: var(--parchment);
  color: var(--ink);
  padding: var(--space-6) var(--space-8);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-modal);
  font-size: var(--fs-base-tight);
  animation: toast-in var(--transition-pop);
}
.toast-action {
  color: var(--gold-on-dark);
  font-weight: var(--fw-bold);
  background: none; border: none; cursor: pointer;
  text-decoration: underline;
}
.toast-action:hover { color: #fff; }
@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* ── 18. Generic card / panel primitives (from source) ─────────────── */

.panel {
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  padding: var(--space-9);
}
.panel-accent { border-left: 3px solid var(--gold-dim); }

.card {
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--c-set);
  padding: var(--space-8) var(--space-9);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}
.card:hover { border-color: var(--gold-dim); transform: translateX(2px); box-shadow: var(--shadow-card-hover); }
.card:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ── 19. Application shell (NEW — added when the app shell was built on
   top of this system; everything here follows the same token/idiom rules
   as sections 1–18). Used by main.js, components/*, and views/*. The
   browse/search agents building top300/surf/random/search may reuse the
   mode-bar / progress-rail / btn-row / dialog primitives directly instead
   of inventing their own — see components/README.md. ───────────────── */

/* Page container — the one place page-level max-width/padding is set, so
   every view gets the same rhythm without repeating it. Applied to the
   router's outlet element once, in index.html. */
.app-main { max-width: 1100px; margin: 0 auto; padding: var(--space-13) var(--space-14) var(--space-18); }
.app-main:focus { outline: none; } /* focused programmatically after each route change, not by the user tabbing */

/* Generic flex button/action row — end-of-list states, form footers,
   anywhere a handful of .btn's need to sit in a wrapping row. */
.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: center; }

/* Per-mode context bar: "TOP 300 · #47 of 300 · ★ 12 saved" etc. Content is
   mode-specific and owned by each view; this only supplies the shared
   layout/typography so all four modes look like one product. */
.mode-bar {
  display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap;
  padding-bottom: var(--space-6); margin-bottom: var(--space-9);
  border-bottom: 1px solid var(--rule);
}
.mode-bar-label {
  font: var(--fw-bold) var(--fs-xs) var(--font-mono);
  letter-spacing: var(--ls-label-lg); text-transform: uppercase; color: var(--gold);
}
.mode-bar-meta {
  font: var(--fw-medium) var(--fs-sm) var(--font-mono);
  color: var(--parchment-mute); margin-left: auto;
}

/* Thin bounded-progress rail (Top 300 rank, Surf All position). */
.progress-rail { height: 4px; border-radius: var(--radius-full); background: var(--ink-3); overflow: hidden; margin-top: var(--space-4); width: 100%; }
.progress-rail-fill { height: 100%; background: var(--gold-dim); border-radius: var(--radius-full); transition: width var(--transition-base); }

/* Generic modal dialog — ported from the source sites' own .modal-overlay/
   .modal idiom (see research/04-design-system.md §1.11). Used here for the
   "?" shortcuts overlay; available to any view that needs a lightweight
   dialog (e.g. Surf All's "Go to #" jump box) instead of hand-rolling one. */
.dialog-overlay {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: rgba(31, 41, 55, 0.42);
  backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: var(--space-14) var(--space-11);
  overflow-y: auto;
  animation: dialog-fade-in var(--transition-base);
}
/* Global [hidden] safety net.
   The UA stylesheet's [hidden]{display:none} loses to ANY author rule that sets
   display -- e.g. `.toast { display: flex }` -- so `el.hidden = true` silently
   fails to hide such elements. This is what left the favorite/unfavorite toast
   stuck on screen until a page reload. One rule fixes the whole class of bug.
   Anything that genuinely needs to override this must set display in a rule
   that also carries !important, which nothing here does. */
[hidden] { display: none !important; }

.dialog-overlay[hidden] { display: none; }
@keyframes dialog-fade-in { from { opacity: 0; } to { opacity: 1; } }
.dialog {
  background: var(--ink-2);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--gold-dim);
  max-width: 480px; width: 100%;
  padding: var(--space-11);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-modal);
  position: relative;
  margin: auto 0;
  animation: dialog-slide-up var(--transition-pop);
}
@keyframes dialog-slide-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
.dialog-title {
  font: var(--fw-extrabold) var(--fs-xl) var(--font-sans);
  color: var(--parchment); margin: 0 0 var(--space-9);
  padding-right: var(--space-13);
}
.dialog-close {
  position: absolute; top: var(--space-6); right: var(--space-6);
  width: 32px; height: 32px; border-radius: var(--radius-sm);
  background: transparent; border: 1px solid var(--rule-strong); color: var(--parchment-dim);
  display: flex; align-items: center; justify-content: center; font-size: 16px; cursor: pointer;
  transition: all var(--transition-fast);
}
.dialog-close:hover { border-color: var(--gold-dim); color: var(--gold); background: var(--ink-3); }
.dialog-close:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

/* Key + description list, used by the shortcuts overlay. */
.kbd-list { display: flex; flex-direction: column; }
.kbd-list-row { display: flex; align-items: baseline; gap: var(--space-7); padding: var(--space-4) 0; border-bottom: 1px dashed var(--rule); }
.kbd-list-row:last-child { border-bottom: none; }
.kbd-list-keys { flex: 0 0 150px; display: flex; gap: var(--space-2); flex-wrap: wrap; }
.kbd-list-desc { margin: 0; color: var(--parchment-dim); font-size: var(--fs-base-tight); }

/* ── Shared commander view — the single most-reused piece of the app ──────
   MOBILE-FIRST, and stable-by-construction. The rules below are the fix for
   the "scroll bar keeps jumping up and down / the Next button moves out from
   under the pointer" bug, so read this before changing any height here.

   THE RULE: every box between the top of the view and the Prev/Favorite/Next
   row is sized from CONSTANTS (the token scale and the viewport), never from
   the commander's content. Content-sized boxes are all below the controls.

     .commander-layout            grid: head / focus / details
       .commander-head            FIXED height: 2-line name box + 1-line type
       .commander-focus           the block the user aims at
         .commander-stage         card art -- width from the viewport, height
                                  from the 488:680 aspect-ratio
         .nav-bar.commander-nav   FIXED height, never wraps
       .commander-details         everything variable, each block with its own
                                  reserved min-height so the panels below the
                                  fold hold their positions too

   Measured against the real payload (3,411 commanders): names run 3-59 chars,
   type lines up to 49, oracle text 0-780 chars, themes 0-5, synergy 0-5, and
   7 commanders have no EDHREC record at all. Every one of those renders with
   the head, the card and the nav row at identical y-offsets. */

.commander-layout {
  /* -- Geometry constants. Not one of these depends on the commander. -- */
  --cv-gap: var(--space-6);
  --cv-name-fs: clamp(19px, 5.2vw, 30px);
  --cv-name-lines: 2;      /* 59-char double-faced names fit in 2 at every width */
  --cv-type-h: 15px;       /* one line of .result-card-type (9px mono) */
  --cv-oracle-lines: 4;
  --cv-fav-w: 118px;       /* fixed, so "Favorite" -> "Favorited" moves nothing */
  --cv-syn-line: 20px;
  --cv-tags-h: 46px;       /* 2 rows of .tag. The widest real themes row in the
                              payload ("Planeswalkers / Multicolor Matters /
                              +1/+1 Counters / Tokens / Jegantha Companion")
                              measures ~575px, which is 2 rows at >=360px and
                              3 below that -- see the <380px block in §21. */
  --cv-synergy-h: calc(var(--cv-syn-line) * 5 + var(--space-2) * 4);
  --cv-sticky-top: calc(64px + var(--space-6)); /* clears the sticky top nav */

  /* Vertical space the card may NOT use, so that name + card + controls all
     land inside the first screen: top nav + page padding + the view's own
     mode header + .commander-head + two gaps + the nav row + slack.
     Tuned per breakpoint below; measured, see styles/preview-mobile.html. */
  --cv-chrome: 300px;
  --cv-card-cap: 100%;
  --cv-card-min: 180px;   /* below this the art stops being worth looking at */

  /* 0.7176 = 488/680. Height-bound on tall/short screens, width-bound on
     phones -- min() picks whichever actually fits, with no media query. */
  --cv-card-w: min(100%, var(--cv-card-cap),
                   max(var(--cv-card-min), calc((100vh - var(--cv-chrome)) * 0.7176)));

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "head" "focus" "details";
  row-gap: var(--cv-gap);
  align-items: start;
}
/* svh, not dvh: dvh re-sizes the card every time mobile Safari's URL bar
   slides away, which is the same jump under a different name. svh is the
   worst case, so the card always fits and never resizes mid-scroll. */
@supports (height: 100svh) {
  .commander-layout {
    --cv-card-w: min(100%, var(--cv-card-cap),
                     max(var(--cv-card-min), calc((100svh - var(--cv-chrome)) * 0.7176)));
  }
}
.commander-layout[hidden] { display: none; }

/* -- head -- */
.commander-head { grid-area: head; display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.commander-name-box {
  height: calc(var(--cv-name-fs) * var(--lh-tight) * var(--cv-name-lines));
  display: flex; align-items: flex-end; /* short names sit against the type line */
  overflow: hidden;
}
.commander-name {
  font-family: var(--font-sans);
  font-weight: var(--fw-extrabold);
  font-size: var(--cv-name-fs);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-snug);
  color: var(--parchment);
  margin: 0;
  overflow-wrap: anywhere; /* "A // B" double-faced names have no short words */
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--cv-name-lines); line-clamp: var(--cv-name-lines);
  overflow: hidden;
}
.commander-type {
  height: var(--cv-type-h); line-height: var(--cv-type-h);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* -- focus: card + controls, moved as one unit by the desktop rule below -- */
.commander-focus { grid-area: focus; display: flex; flex-direction: column; gap: var(--cv-gap); min-width: 0; }
.commander-stage { display: flex; justify-content: center; }
.commander-stage .card-portrait { width: var(--cv-card-w); flex: 0 0 auto; }

.nav-bar.commander-nav {
  gap: var(--space-4);
  padding-top: 0; margin-top: 0; border-top: none;
  flex-wrap: nowrap; /* four buttons that never reflow onto a second row */
}
.commander-nav .btn, .commander-nav .favorite-toggle {
  min-height: 44px;              /* iOS/Android minimum comfortable touch target */
  padding-left: var(--space-6); padding-right: var(--space-6);
}
.commander-nav .commander-nav-prev,
.commander-nav .commander-nav-next { flex: 1 1 0; min-width: 0; max-width: 200px; }
.commander-nav .commander-nav-home { flex: 0 0 auto; }
.commander-nav .commander-nav-fav { flex: 0 0 var(--cv-fav-w); width: var(--cv-fav-w); justify-content: center; }
.commander-nav-glyph { font-size: 13px; line-height: 1; }
.commander-nav-hash { font-family: var(--font-mono); }
.commander-nav .nav-bar-hint { display: none; } /* keyboard hints are desktop-only */

/* -- details: everything content-sized, all of it below the controls -- */
.commander-details { grid-area: details; min-width: 0; display: flex; flex-direction: column; gap: var(--space-5); }
.commander-cost { min-height: 22px; }
.commander-identity { min-height: 28px; }

/* Oracle text — clamped to 4 lines until expanded, per the UX spec, AND
   floored at 4 lines so a one-line commander and a six-line one leave the
   EDHREC panel below them in the same place. */
.oracle-text {
  font-size: var(--fs-body); line-height: var(--lh-relaxed); color: var(--parchment);
  margin: var(--space-3) 0 0;
  white-space: pre-line; /* respects \n between printed lines of rules text */
  min-height: calc(var(--fs-body) * var(--lh-relaxed) * var(--cv-oracle-lines));
}
.oracle-text[data-expanded="false"] {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--cv-oracle-lines); line-clamp: var(--cv-oracle-lines);
  overflow: hidden;
}
.oracle-toggle { align-self: flex-start; font-size: var(--fs-sm); }

/* EDHREC enrichment panel — deliberately plain: a couple of label:value
   rows and a numbered list, never EDHREC's own pill-with-big-count or
   multi-section-cascade treatments (see design-system anti-goals §7).
   Every sub-block is always present, with "no data" copy in place of missing
   fields, so the 141 commanders with no themes / 80 with no synergy / 7 with
   no EDHREC page at all do not change the panel's height. */
.enrichment-panel { padding-top: var(--space-8); border-top: 1px solid var(--rule); display: flex; flex-direction: column; gap: var(--space-6); }
/* One line, always. Left to wrap, "No EDHREC page for this card." took a
   second line at 320px while "Rank #12 · 4,010 decks" took one — 20px of
   difference in total page height between two commanders, which is the same
   bug as A wearing a hat (it makes the scroll position jump on advance). */
.enrichment-head { display: flex; align-items: center; gap: var(--space-5); flex-wrap: nowrap; height: 20px; font-size: var(--fs-base-tight); color: var(--parchment); }
.enrichment-head .enrichment-label { flex: none; }
.enrichment-stats { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.enrichment-block { display: flex; flex-direction: column; gap: var(--space-3); }
.enrichment-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; font-size: var(--fs-base-tight); color: var(--parchment); }
.enrichment-label { font: var(--fw-bold) var(--fs-2xs) var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--parchment-mute); }
.enrichment-empty { color: var(--parchment-mute); font-style: italic; font-size: var(--fs-base-tight); margin: 0; }
.enrichment-tags { display: flex; flex-wrap: wrap; align-content: flex-start; gap: var(--space-3); min-height: var(--cv-tags-h); }
a.tag-link { color: var(--parchment-mute); text-decoration: none; }
a.tag-link:hover { color: var(--gold); border-color: var(--gold-dim); background: color-mix(in srgb, var(--gold) 8%, var(--ink-3)); }
a.tag-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.synergy-list {
  margin: 0; padding-left: 18px; color: var(--parchment); font-size: var(--fs-base-tight);
  display: flex; flex-direction: column; gap: var(--space-2);
  min-height: var(--cv-synergy-h);
}
/* One line each, ellipsised rather than wrapped: five entries are always
   exactly five rows tall. The longest name in the payload
   ("Central Elevator // Promising Stairs", 36 chars) fits at every width
   this ships at, so the ellipsis is a guarantee, not a routine truncation. */
.synergy-list li { line-height: var(--cv-syn-line); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.synergy-list li.synergy-empty { list-style: none; color: var(--parchment-mute); font-style: italic; }
.synergy-link {
  color: var(--parchment);
  text-decoration: underline; text-decoration-style: dotted;
  text-decoration-color: var(--rule-strong); text-underline-offset: 2px;
}
.synergy-link:hover { color: var(--gold); text-decoration-color: var(--gold); }

.enrichment-outbound { align-self: flex-start; font-size: var(--fs-base-tight); }

/* ── 20. Responsive ─────────────────────────────────────────────────── */

/* Base (desktop) two-column layout. This rule MUST live in the stylesheet, not inline:
   an inline grid-template-columns beats the max-width:900px collapse below regardless of
   viewport, which silently breaks the mobile layout. */
.layout-with-sidebar {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: var(--space-11);
  align-items: start;
}

@media (max-width: 900px) {
  .layout-with-sidebar { grid-template-columns: 1fr; }
  .filter-sidebar { position: static; }
}

/* The sticky nav is pure chrome, and on a handset it was eating 165px --
   nearly a fifth of the screen -- because the brand, the five modes and the
   session button each wrapped onto their own row. Measured, then tightened to
   two rows on a phone and kept to ONE row down to 744px. Every pixel saved
   here goes straight into the commander art below it. */
@media (max-width: 1023px) {
  .top-nav { padding-left: var(--space-10); padding-right: var(--space-10); gap: var(--space-6); }
  .top-nav-link { padding: var(--space-4) var(--space-5); }
}

@media (max-width: 720px) {
  .masthead { padding-left: var(--space-10); padding-right: var(--space-10); }
  .masthead::before, .masthead::after { display: none; }
  .results-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--space-5); }
  .top-nav { flex-wrap: wrap; gap: var(--space-5) var(--space-6); padding: var(--space-4) var(--space-8); }
  /* Two rows, not three: brand and session share row 1, the mode tabs get
     row 2 to themselves. `order` is what puts the full-width tab strip last
     even though nav.js emits it in the middle. */
  .top-nav-modes { order: 3; width: 100%; }
  .top-nav-actions { margin-left: auto; }
  .top-nav-name { font-size: var(--fs-base); }
  .top-nav-actions .btn { padding: var(--space-3) var(--space-6); }
  .auth-card { padding: var(--space-11) var(--space-8); }
  /* Reclaim page gutter for the card. Every pixel spent here is a pixel the
     commander art does not get on a phone. */
  .app-main { padding: var(--space-8) var(--space-8) var(--space-14); }
}

@media (max-width: 480px) {
  .results-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
  .top-nav { gap: var(--space-3) var(--space-5); }
  .top-nav-modes { justify-content: space-between; gap: var(--space-1); }
  .top-nav-link {
    flex: 1; justify-content: center; padding: var(--space-4) var(--space-1);
    font-size: 9px; letter-spacing: var(--ls-snug); white-space: nowrap;
  }
  .nav-bar { gap: var(--space-4); }
  .nav-bar .btn:not(.btn-icon) span.btn-label-full { display: none; }
}

/* ── 21. Commander view breakpoints ──────────────────────────────────────
   Three stops, one DOM. The single-column phone layout IS the base (above);
   these only retune the constants that decide how much screen the card gets,
   and fold the details column alongside once there is room for both.

   Measured in a real browser at 320x568, 360x640, 390x844, 744x1133,
   768x1024, 834x1112, 1024x768, 1194x834 and 1440x900. At every one of them
   the name box, the card and the nav row sit at a single y-offset across all
   the payload's extremes, and the controls land inside the first screen. */

/* Very narrow phones: with the optional "#1" control shown there are four
   buttons, and they stop fitting side by side at ~360px. The favorite
   collapses to its ribbon and the "#1" caption drops to a bare glyph. */
@media (max-width: 379px) {
  .commander-layout { --cv-fav-w: 48px; --cv-chrome: 310px; --cv-tags-h: 72px; }
  .commander-nav .commander-nav-fav .favorite-label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
  .commander-nav-hash { display: none; }
}

/* Tablet portrait (and large phones in landscape): still one column with the
   card dominating, but capped so it stops short of absurd on an 834pt iPad. */
@media (min-width: 640px) and (max-width: 1023px) {
  .commander-layout {
    --cv-gap: var(--space-9);
    --cv-card-cap: 540px;
    --cv-chrome: 330px;
  }
  .commander-details { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6) var(--space-11); align-items: start; }
  .commander-cost { grid-column: 1; }
  .commander-identity { grid-column: 2; justify-self: end; }
  .oracle-text, .oracle-toggle, .enrichment-panel { grid-column: 1 / -1; }
}

/* Desktop / iPad landscape: the same three blocks, with the focus column
   (card + controls) on the left and the details alongside. Sticky, so the
   controls follow you as far as the grid allows while you read the details --
   its travel is (grid height - focus height), which is small when the details
   are short and grows exactly when you need it (expanded oracle text). */
@media (min-width: 1024px) {
  .commander-layout {
    --cv-gap: var(--space-9);
    --cv-card-cap: 340px;
    --cv-chrome: 220px;
    --cv-name-fs: 30px;
    grid-template-columns: minmax(240px, 340px) minmax(0, 1fr);
    grid-template-areas:
      "focus head"
      "focus details";
    grid-template-rows: auto 1fr;
    column-gap: var(--space-13);
  }
  .commander-focus { position: sticky; top: var(--cv-sticky-top); align-self: start; }
  .commander-nav.nav-bar { flex-wrap: wrap; }
  .commander-nav .nav-bar-hint { display: flex; margin-top: var(--space-2); }
}
