/* ============================================================================
   Responsive breakpoints
   ----------------------------------------------------------------------------
   Two semantic mobile breakpoints. Custom-property names are documentation
   only — CSS doesn't allow custom properties inside @media conditions, so
   the literal pixel values appear in every @media rule below.

   --bp-tablet: 768px   "tablet portrait and below" — phone + tablet portrait.
                        Card grids collapse to single column, filter rows
                        stack, popouts cap at 90vw, tap targets enforced 44px.
                        Used in @media (max-width: 768px) blocks — currently
                        five (header/inventory, analytics, consistency, health,
                        bracket-v2) plus the 44px tap-target rule at EOF.

   --bp-phone:  480px   Reserved for true-phone overrides (tighter spacing,
                        smaller heroes, bottom-sheet-style modal hooks).
                        Not yet wired up — Stage 4+ work.

   Desktop tier kept as-is:
   --bp-desktop-narrow: 980px   First-pass shrink (laptop → tablet). Used
                                only at line 504 for inventory-card grid.

   Game tracker portrait: @media (orientation: portrait) at line ~1944.
                          Tablet-landscape-first by explicit design; the
                          44px tap-target enforcement does NOT apply to
                          tracker buttons (see exception list in the rule).
   ============================================================================ */

/* ============================================================================
   Design tokens (v3.27.7 foundation pass)
   ----------------------------------------------------------------------------
   Two parallel concerns live here:

   1. The WORKING token set the v3.x stylesheet has been built on for years —
      ``--bg``, ``--panel``, ``--panel-2``, ``--border``, ``--text``, ``--muted``,
      ``--link``, ``--accent``, ``--accent-2``, ``--danger``, plus the
      breakpoint trio. These tokens stay at their current values. The
      v3.27.7 foundation pass reconciled (not replaced) them: appearance
      across the app is unchanged outside the explicit bug-fix sites.

   2. A NEW Cartarch brand palette (off-white / blue / near-black / red /
      green / gold / dark-navy bg) prefixed ``--brand-*``. Forward-looking,
      consumed by the later v3.27.x aesthetic restyling release. NOT yet
      referenced by any CSS rule — adding the tokens here so the brand
      vocabulary exists when the restyle work begins.

   Two additional v3.27.7 cleanups, both folded into Part 3 of the
   foundation pass: the previously-undefined ``--bg-card`` /
   ``--text-muted`` references documented in current-status.md Known
   Problems were repointed to ``--panel`` / ``--muted`` respectively (the
   End Game modal's transparent-background bug is the one the user
   actually sees fixed); and three additional undefined tokens the v3.27.7
   inventory caught (``--surface2``, ``--surface3``, ``--surface-muted``)
   were repointed to ``--panel-2`` / ``--panel`` / ``--panel-2``
   respectively. See style.css Part 3 fix sites inline.

   Tokens NOT in :root that legitimately exist as CSS custom properties:
   ``--player-color`` (set per-seat by game_detail.html JS for the v3.26.1
   commander art panel work), ``--commander-art-url-primary`` /
   ``--commander-art-url-secondary`` (same), ``--current-player-color``
   (set on turn change by game_detail.html render()). Dynamic JS-set
   props; do NOT belong in :root.
   ============================================================================ */
:root {
  /* --- Working palette (v3.28.1 — repointed to the warm-charcoal Folio target) ---
     Token NAMES kept verbatim per the v3.28.1 spec — every existing CSS rule
     referencing ``var(--bg)`` / ``var(--panel)`` / etc. inherits the new
     values without a per-rule rewrite. v3.28.1 is the second token-value
     repoint of the v3.27.7 foundation (v3.27.15 was the first, to navy).
     Folio palette source: the v3.28.0-finalized design package's :root block
     in foundation.jsx; the Folio package's own token names (--cream / --brass
     / --oxblood / etc.) are NOT imported — production keeps its names. */
  --bg: #1A130C;       /* was #081321 navy; Folio --bg, warm charcoal */
  --panel: #251C12;    /* was #0f1a2e; Folio --panel */
  --panel-2: #2E2418;  /* was #13243d; Folio --panel-2 */
  --panel-3: #3A2C1D;  /* was #1a2f4d; Folio --leather (tier-3 surface; v3.27.15 added this token vestigially, v3.28.1 consumed it) */
  --border: rgba(181, 138, 71, 0.22);  /* was #21385a; Folio --border (translucent brass at 22% — semi-transparent border idiom matches the Folio package) */
  --border-soft: rgba(181, 138, 71, 0.10);  /* NEW (v3.28.2) — softer inner divider, used by the Chronicle page's editorial dividers */

  /* --- Working text scale (v3.28.1 — repointed to warm cream) --- */
  --text: #EDE1C4;     /* was #f5f1d7; Folio --cream (anchor value per spec) */
  --text-dim: #D6C9A8; /* was #aeb9cc; Folio --cream-2 (mid-tier body) */
  --muted: #A99772;    /* was #6b7a92; Folio --cream-3 (muted captions / secondary) */
  --link: #D4A85F;     /* was #a9c8ff; Folio --brass-2 (Folio links read as brass, not blue) */

  /* --- Working accents + danger (v3.28.1 — repointed to brass + oxblood) --- */
  --accent: #B58A47;   /* was #2f6df6 blue; Folio --brass (primary accent) */
  --accent-2: #D4A85F; /* was #ffd57a; Folio --brass-2 (highlight/active) */
  --danger: #8A2E22;   /* was #ff5d57; Folio --oxblood (anchor value per spec) */

  /* --- Cartarch brand palette (defined v3.27.7, repointed in v3.27.15 and v3.28.1) ---
     Source: v3.28.0-finalized Folio design package.
     Awkward names — kept per the v3.28.1 spec's "names preserved" rule:
       --brand-blue holds brass (no longer blue; 0 consumers — tech debt)
       --brand-bg-navy holds warm charcoal (no longer navy; 0 consumers — tech debt)
     These two carry misleading names post-v3.28.1; preserved per spec for
     potential future cleanup. The repoint is by VALUE only. */
  --brand-off-white: #EDE1C4;  /* was #f5f1d7; aligned with --text */
  --brand-blue: #B58A47;       /* was #1e5aa8; now holds brass (name misleading; 0 refs) */
  --brand-near-black: #211810; /* was #1c1c1e; aligned with Folio bg-2 for warm tone */
  --brand-red: #8A2E22;        /* was #c73d3d; → Folio oxblood (editorial red) */
  --brand-green: #6A8253;      /* was #2e7d46; → Folio sage (editorial green) */
  --brand-gold: #B58A47;       /* was #c8a15a; → Folio brass (primary accent) */
  --brand-gold-soft: rgba(181, 138, 71, 0.14);  /* was rgba(200,161,90,0.14); brass base, same alpha */
  --brand-bg-navy: #1A130C;    /* was #081321; now holds warm charcoal (name misleading; 0 refs) */

  /* --- Radius scale (v3.27.7, forward-looking; values seen in current CSS) ---
     The stylesheet currently inlines radius values (6px, 10px, 12px, 14px,
     16px, 18px, 20px, 999px) across ~40 rules. Capturing the common ones
     as tokens for the aesthetic-restyling release to consolidate around. */
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 18px;
  --radius-pill: 999px;

  /* --- Shadow scale (v3.27.7, forward-looking) ---
     One shadow value seen in current CSS (.panel at line 282). Capturing
     a small scale for consistency. */
  --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.18);
  --shadow: 0 10px 28px rgba(0, 0, 0, 0.22);

  /* --- Spacing scale (v3.27.7, forward-looking) ---
     Stylesheet currently inlines rem values throughout. Documented common
     steps for the aesthetic-restyling release to consolidate around. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;

  /* --- Editorial display face (v3.29.4) ---
     Self-hosted Source Serif 4 for the "The X." titling register added in
     v3.29.4 (landing, sets, tokens). Distinct from --font-family-base
     (Montserrat — body/UI) and the Newsreader stack used by pagehead-title
     since v3.28.1. The transitional/old-style proportions give the editorial
     register the "printed-page" voice the Folio design language asks for.
     Consumed by .editorial-display + the v3.29.4 .the-x-title rules. */
  --font-display:
    "Source Serif 4", "Source Serif Pro", Georgia, "Times New Roman", serif;

  /* --- Breakpoints (v3.x, unchanged) ---
     Documentation only — CSS doesn't allow custom properties inside @media
     conditions. See the big top-of-file comment for the full @media usage
     map. */
  --bp-tablet: 768px;
  --bp-phone: 480px;
  --bp-desktop-narrow: 980px;
}

/* ============================================================================
   === Mobile Fundamentals ===
   ----------------------------------------------------------------------------
   Global rules that prevent common mobile failure modes everywhere, so
   individual pages don't need to opt in. Anything that could affect desktop
   layout is scoped to @media (max-width: 768px) per the project's
   "no desktop changes" constraint.

   What's here:
     1. box-sizing: border-box on every element (including pseudo-elements)
     2. img scaling so card thumbs can't overflow narrow phones
     3. <= 768px: html/body overflow-x: hidden + max-width: 100vw
        (hammer to prevent rogue elements from horizontal-scrolling the
        whole page; .table-wrap retains its own overflow-x: auto and is
        unaffected because it's its own scroll context)
     4. <= 768px: 16px minimum font-size on form controls so iOS Safari
        doesn't auto-zoom on focus
     5. <= 768px: overflow-wrap: anywhere on common text containers so a
        single unbroken token (long Scryfall ID, oracle-text URL, etc.)
        doesn't force horizontal scroll
     6. <= 768px: 44px min-height on link-styled elements in nav, filter
        rows, hero rows, and pagination (button coverage is handled by
        the EOF tap-target block; this rule adds anchor coverage)

   What's NOT here (already wired elsewhere; do not duplicate):
     - .bottom-nav { padding-bottom: env(safe-area-inset-bottom) }
       lives in the mobile bottom-tab block at ~line 3058
     - Global <button> 44px floor lives in the EOF tap-target block
   ============================================================================ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* ============================================================================
   Scrollbar-gutter — v3.27.16 (regression fix from v3.27.15)
   ----------------------------------------------------------------------------
   The v3.27.15 aesthetic pass introduced ``body { min-height: 100vh;
   position: relative; }`` plus the ``body::before`` / ``body::after``
   fixed-positioned background-texture layers. On pages tall enough to
   need a vertical scrollbar (Import, Collection, Sets, Tokens, Locations,
   Audit, Admin all observed), a thin horizontal scrollbar appeared as a
   regression; pages short enough to fit without vertical scroll
   (Dashboard, Pending, Watchlist, Drawers, Decks, Games, Account) were
   unaffected. Symptom fingerprint: horizontal scrollbar present **iff**
   vertical scrollbar present.

   Root cause — scrollbar-gutter reflow. When the body grows tall enough
   to require a vertical scrollbar, the browser reserves ~15-17px of
   layout width for the scrollbar gutter, shrinking the available viewport
   width. The v3.27.15 changes (especially the fixed-positioned pseudo-
   elements that paint at viewport bounds + body becoming a positioning
   context with ``min-height: 100vh``) interact with that reflow in a way
   the pre-v3.27.15 stylesheet did not — content sized assuming the
   pre-scrollbar viewport ends up ~17px over the new (reduced) width,
   triggering horizontal scroll.

   Fix — ``scrollbar-gutter: stable`` on ``html``. Reserves the gutter
   space whether or not the scrollbar is visible, so viewport width is
   constant in both states and no reflow happens when the scrollbar
   appears. Standardized property (Chrome 94+, Firefox 97+, Safari 18.2+);
   older browsers ignore it gracefully — they revert to the pre-v3.27.15
   behavior (still has the regression on long pages, but no worse than
   the regression itself). The reserved gutter shows as a faint inset on
   non-scrollbar pages; visually acceptable per the v3.27.15 calm-finish
   direction.

   Why on ``html`` rather than ``body``: the property only takes effect
   on the scrolling element, which is ``html`` in modern browsers (the
   ``body`` no longer scrolls by default — it's ``<html>``).
   ============================================================================ */
html {
  scrollbar-gutter: stable;
}

img {
  max-width: 100%;
  height: auto;
}

@media (max-width: 768px) {
  html,
  body {
    overflow-x: hidden;
    max-width: 100vw;
  }

  /* iOS Safari auto-zooms on focus when input font-size < 16px. The 16px
     floor is the documented fix and applies regardless of the surrounding
     :root font scale. */
  input,
  textarea,
  select {
    font-size: 16px;
  }

  /* Break long unbroken tokens (Scryfall IDs, URLs, deck-export pastes)
     inside text containers so they wrap instead of forcing horizontal
     scroll. Limited to text-bearing containers — applying to .hero-row /
     .filter-row would break their flex layouts. */
  .panel p,
  .panel li,
  .combo-description,
  .combo-prereq,
  .dead-cards-list li,
  .synergy-card-list li {
    overflow-wrap: anywhere;
  }

  /* Tap-target floor for link-styled interactive elements. <button>
     coverage lives in the EOF tap-target block (with tracker exclusions);
     this rule adds the <a> coverage missing from that block. */
  nav a,
  .filter-row a,
  .hero-row a,
  .pagination a,
  .pagination button {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}
/* === end Mobile Fundamentals === */

/* ============================================================================
   Typography — self-hosted (v3.27.15)
   ----------------------------------------------------------------------------
   Two faces:
     - Montserrat — UI / body face. Weights 400 / 500 / 600 / 700 / 800 served
       from a single variable woff2 (the v18 Latin subset Google Fonts now
       publishes as variable; each @font-face below references the same file
       and the browser picks the right weight from the variable axis).
     - Montserrat Alternates — used ONLY for the brand wordmark / sidebar
       brand name. Weights 600 and 700, each a distinct file. Do NOT set
       Alternates as body text — it reads poorly at small sizes.

   Files self-hosted under /static/fonts/ — no runtime CDN dependency
   (per v3.27.15 spec invariant 3: no request-path external calls).
   font-display: swap so fallback (system-ui) renders immediately and
   Montserrat takes over when the woff2 loads. SIL OFL — redistribution
   is fine.
   ============================================================================ */
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/montserrat-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/montserrat-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/montserrat-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/montserrat-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/static/fonts/montserrat-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat Alternates";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/montserrat-alternates-600-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Montserrat Alternates";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/montserrat-alternates-700-latin.woff2") format("woff2");
}

/* --- Newsreader (v3.28.1) — the Folio editorial face -----------------------
   Self-hosted variable woff2 (Latin subset) — 2 files cover regular + italic
   weights 200..800. Newsreader is used ONLY for editorial display surfaces
   (page H1 titles, italic display copy, stat-figure values). Montserrat
   stays as the body/UI face. The Folio design package uses Newsreader italic
   heavily for the editorial register; production opts in selectively to
   avoid setting a serif on body text (the same judgment v3.27.15 applied to
   Montserrat Alternates). SIL OFL — redistribution is fine.
   ============================================================================ */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/static/fonts/newsreader-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  src: url("/static/fonts/newsreader-italic-variable-latin.woff2") format("woff2");
}

/* --- Source Serif 4 (v3.29.4) — the editorial display face ----------------
   Self-hosted variable woff2 (Latin subset) — 2 files cover regular + italic
   weights 400..700. Source Serif 4 is used ONLY for the editorial display
   register added in v3.29.4: page titles in the "The X." titling voice
   ("The Sets.", "The Tokens.", "The ruler of your collection."), chapter
   eyebrows, and the § motif. Newsreader stays the working editorial italic
   face for pagehead-titles + body display copy (the v3.28.1 register).
   Montserrat stays the body/UI face. SIL OFL — redistribution is fine.

   Variable woff2 (weights 400..700) for upright + italic; same shape as the
   v3.28.1 Newsreader blocks. Exposed via --font-display below; consumed by
   `.editorial-display` and the v3.29.4 landing/sets/tokens editorial rules. */
@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/source-serif-4-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Source Serif 4";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/source-serif-4-italic-variable-latin.woff2") format("woff2");
}

body {
  font-family:
    "Montserrat",
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif;
  margin: 0;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  position: relative;
}

/* ============================================================================
   Background texture (v3.27.15 — "calmer texture")
   ----------------------------------------------------------------------------
   Two fixed pointer-events:none layers behind app content. Per v3.27.7's
   stated direction (reduced effects, calmer texture):
     - body::before: faint SVG fractal-noise overlay so the navy isn't
       dead-flat. SVG via data: URI (no extra HTTP fetch). Opacity 0.5
       on the layer, 0.025 inside the SVG rect — the multiplied effect
       is barely visible but kills the flat-LCD look.
     - body::after: soft radial brand-color washes — repointed in v3.28.1
       from navy + gold (the v3.27.15 treatment) to brass + brass-2 (the
       Folio treatment). The dominant top-left wash is now brass at 0.10
       alpha (slightly softer than v3.27.15's 0.16 alpha because the
       warm-charcoal ground is lower-contrast and a heavier brass wash
       reads as muddy); the top-right wash is brass-2 at 0.06 alpha
       (mildly intensified for the same reason). Same gradient geometry.
   z-index: 0 on both layers; .app-shell already establishes its own
   stacking context so content stays above naturally.
   ============================================================================ */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.025'/%3E%3C/svg%3E");
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 520px at 14% -8%, rgba(181, 138, 71, 0.10), transparent 70%),
    radial-gradient(700px 480px at 102% 0%, rgba(212, 168, 95, 0.06), transparent 62%);
}
.app-shell {
  position: relative;
  z-index: 1;
}

/* ============================================================================
   App shell (v3.27.8)
   ----------------------------------------------------------------------------
   Two-column grid: left sidebar + main pane. Desktop ≥768px shows the
   sidebar; <768px hides it and the existing .bottom-nav handles mobile
   nav (unchanged from prior releases). The slim .app-topbar sits above
   the page content inside the main pane and carries the version pill
   + logout control (relocated from the floating top-right per spec).
   ============================================================================ */
/* v3.27.17 correction — the .app-shell layout was a two-column grid
   pre-correction; the brand lived inside the sidebar so the topbar was
   constrained to the main column. The correction makes the topbar a
   full-width row at the top of the grid, spanning both columns, so the
   brand stays visible at ≤768px when the sidebar hides. Grid layout: a
   topbar row above a sidebar+main row, using grid-template-areas so the
   row-span on the topbar is named, not implicit. The 1fr main row
   absorbs whatever vertical space remains after the auto-sized topbar,
   keeping min-height:100vh behavior unchanged. */
.app-shell {
  display: grid;
  grid-template-columns: 236px 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "topbar topbar"
    "sidebar main";
  min-height: 100vh;
}
/* Anon variant (login / register / privacy / terms — no current_user):
   .app-sidebar does not render. Drop to a single column for the
   sidebar/main row; the topbar row still spans the full width via the
   collapsed grid-template-areas. */
.app-shell-anon {
  grid-template-columns: 1fr;
  grid-template-areas:
    "topbar"
    "main";
}
.app-shell > .app-topbar {
  grid-area: topbar;
}
.app-shell > .app-sidebar {
  grid-area: sidebar;
}
.app-shell > .app-main {
  grid-area: main;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.app-sidebar {
  background: linear-gradient(180deg, var(--bg) 0%, var(--bg) 100%);
  border-right: 1px solid var(--border);
  /* v3.27.17 correction — sidebar no longer carries the brand block;
     padding-top is the only padding adjustment needed (was 1.25rem to
     account for the brand sitting above the first nav group). The
     first nav group now sits directly under the topbar separator;
     padding-top stays tight to keep the nav visually aligned with
     the topbar baseline. */
  padding: 1.25rem 0.75rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.nav-group {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.nav-label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 0.65rem 0.35rem;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius-sm);
  border-left: 2px solid transparent;
  color: var(--text-dim);
  font-size: 0.86rem;
  font-weight: 500;
  text-decoration: none;
  transition:
    background 0.15s ease,
    color 0.15s ease;
}
.nav-item:hover {
  background: var(--panel-2);
  color: var(--text);
}
.nav-item.active {
  background: var(--brand-gold-soft);
  color: var(--text);
  border-left-color: var(--brand-gold);
  font-weight: 600;
}
/* v3.27.15 — Lucide nav icons. 24x24 viewBox declared on the SVG;
   rendered at ~17px to match the mockup's visual weight. stroke=currentColor
   on every path so the icon picks up the nav item's color (inactive
   --text-dim, hover --text, active --text). Active state lifts opacity
   slightly and shifts the icon stroke to gold for the visual tie-in. */
.nav-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  opacity: 0.85;
}
.nav-item:hover .nav-icon {
  opacity: 1;
}
.nav-item.active .nav-icon {
  opacity: 1;
  color: var(--brand-gold);
}

/* v3.29.3 — Donate link (sidebar bottom + mobile More mirror).
   Tasteful gold-accented treatment using the existing --brand-gold /
   --brand-gold-soft tokens — reads as distinct from the plain nav
   items above without competing for navigation attention. NOT
   wired into active-state logic (external link, no in-app route).
   Pinned at the bottom of the sidebar via margin-top:auto on the
   parent .sidebar-nav flex column. */
.nav-donate {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin: 0.75rem 0 0;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--brand-gold-soft);
  background: transparent;
  color: var(--brand-gold);
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: none;
  transition:
    background 0.15s ease,
    border-color 0.15s ease,
    color 0.15s ease;
}
.nav-donate:hover {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--text);
}
.nav-donate .nav-icon {
  color: var(--brand-gold);
  opacity: 1;
}

/* v3.29.5 — sticky viewport-height sidebar. Replaces the v3.29.3 in-flow
   bottom-pin (`.sidebar-nav { flex: 1 1 auto }` + `.sidebar-nav > .nav-donate
   { margin-top: auto }`) which only surfaced Donate at the bottom of the
   sidebar's natural flow — meaning it was below the fold on long pages.
   The new structure makes the sidebar itself a sticky 100vh column with
   the nav groups in an internally-scrolling middle region and Donate in a
   non-scrolling footer, so Donate is in view on every page regardless of
   page length.

   Topbar also becomes sticky on desktop so the sidebar sits below it
   without geometric gap (the sidebar's `top: 72px` matches the topbar's
   72px height; `height: calc(100vh - 72px)` is exactly the available
   vertical space below the topbar). Scoped to ≥769px — at ≤768px the
   sidebar is hidden via `.app-sidebar { display: none }` and the topbar
   stays non-sticky there (the mobile path is explicitly untouched per
   the v3.29.5 spec out-of-scope clause). Sticky (not fixed) keeps the
   sidebar inside the existing `236px 1fr` grid track — no manual width
   math, no reflow of .app-main, the v3.27.8 .page-shell width-trap fix
   continues to hold. */
.sidebar-nav-scroll {
  /* Structural rule: applies at all widths, but only renders visibly at
     desktop where the sidebar is displayed. The .nav-group children
     keep their previous 1.1rem rhythm via this wrapper's gap. */
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
@media (min-width: 769px) {
  /* The topbar sticks at viewport-top so the sidebar can offset by its
     height without a gap. Background is explicit so the sidebar (z-index
     stacks below it) cannot bleed through during scroll-into-position
     transitions. */
  .app-topbar {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--bg);
  }
  /* The sidebar itself is the sticky 100vh-72px column. align-self: start
     prevents the grid from stretching it to fill its track height.
     overflow: hidden contains the internal scroll wrapper cleanly. */
  .app-sidebar {
    position: sticky;
    top: 72px;
    height: calc(100vh - 72px);
    align-self: start;
    overflow: hidden;
  }
  .sidebar-nav {
    /* Fills the sidebar so its flex children (the scroll wrapper + the
       Donate footer) can lay out against a known height. The base
       `display: flex; flex-direction: column; gap: 1.1rem` rule above
       still applies; this only adds the height. */
    height: 100%;
  }
  .sidebar-nav-scroll {
    /* The flex-growing middle region. `min-height: 0` is LOAD-BEARING —
       without it a flex child refuses to shrink below its content
       height and the internal scroll never engages on a short
       viewport. `overflow-y: auto` engages the scroll only when the
       five nav groups don't fit. */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Thin brass-tinted scrollbar — readable but unobtrusive against
       the warm-charcoal sidebar background. Firefox uses the scrollbar-
       width + scrollbar-color properties; WebKit reads the
       ::-webkit-scrollbar* rules below. */
    scrollbar-width: thin;
    scrollbar-color: var(--brand-gold-soft) transparent;
  }
  .sidebar-nav-scroll::-webkit-scrollbar {
    width: 8px;
  }
  .sidebar-nav-scroll::-webkit-scrollbar-thumb {
    background: var(--brand-gold-soft);
    border-radius: 4px;
  }
  .sidebar-nav-scroll::-webkit-scrollbar-track {
    background: transparent;
  }
  /* Donate is the non-scrolling footer. flex: 0 0 auto means it never
     compresses; the scroll wrapper above takes any remaining vertical
     space. */
  .sidebar-nav > .nav-donate {
    flex: 0 0 auto;
  }
}

/* v3.27.17 correction — full-width brand topbar.
   - Spans both columns via grid-area: topbar (declared above on
     .app-shell > .app-topbar).
   - Height nudged taller than the v3.27.8 slim variant so the horizontal
     logo (5:1 viewBox, 2000×400 — has a "the ruler of your collection"
     tagline baked into the bottom register) is legible. 72px is inside
     the spec's 68–76px range; height set as a literal here, NOT a new
     CSS token, per the v3.27.17 correction spec.
   - justify-content: space-between puts the brand left and the controls
     right with the gap absorbed by the flex algorithm — no fixed gap
     because the available width varies wildly across viewports.
   - border-bottom preserved from the pre-correction slim topbar to
     visually separate from the sidebar/main row below. */
.app-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 72px;
  padding: 0.5rem 1.5rem;
  border-bottom: 1px solid var(--border);
}
.topbar-brand {
  /* Anchor wrapping the brand <img>. Matches the wordmark height
     budget within the 72px bar — 52px image height gives breathing
     room above and below. min-width:0 lets flex actually shrink the
     image on narrow phones rather than overflowing the bar. */
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  min-width: 0;
}
.topbar-brand-logo {
  /* Height-constrained to the topbar; width:auto preserves the 5:1
     aspect ratio. max-width caps the on-screen size at wide viewports
     so the logo doesn't grow into the controls; on narrow phones the
     same max-width plus min-width:0 on .topbar-brand lets flex shrink
     the image without colliding with the right-side controls. The
     full tagline ("— the ruler of your collection") is part of the
     SVG itself and stays visible at every width.

     v3.27.18 — desktop cap unchanged; phone widths get a narrower
     viewport-relative cap via the ≤480px media query below so the
     logo steps DOWN at narrow widths rather than holding intrinsic
     size and colliding with the controls. */
  height: 52px;
  width: auto;
  max-width: min(60vw, 320px);
  display: block;
}
.topbar-controls {
  /* Right cluster — version pill + (authed) logout. Stays as a flex
     row so the two items keep their existing visual rhythm.

     v3.27.18 — gained ``min-width: 0`` so the cluster can actually
     shrink under flex distribution. Without it the cluster's
     min-width defaulted to ``auto`` (min-content), which the long
     "Logout (DisplayName)" string set to a value larger than the
     topbar could grant at phone widths, forcing a brand collision. */
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
}

@media (max-width: 768px) {
  .app-shell {
    /* Mobile: sidebar hides; the topbar row stays full-width on top of
       the (now lone) main column. Grid collapses to single-column
       sidebar/main row, but the topbar row's grid-template-areas
       continues to work with the single column. */
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "main";
  }
  .app-sidebar {
    display: none;
  }
  .app-topbar {
    /* v3.27.18 — restored horizontal padding from the v3.27.17 mobile
       value (1rem) to 1.25rem so the crest has visible breathing room
       at the left edge. The SVG's bounding box is tight; 1rem made
       the crest read as edge-glued at phone widths. */
    padding: 0.5rem 1.25rem;
  }
}
@media (max-width: 480px) {
  /* v3.27.18 — phone-width sweep of the post-v3.27.17 topbar
     collision (the bug this patch fixes):
     - Step the logo cap DOWN so it can't claim more than half the
       bar at phone widths. 50vw + 200px ceiling means at 360px the
       logo is at most 180px wide; at 480px at most 200px.
     - Hide the parenthesized display-name suffix on the logout
       button (rendered as .nav-logout-suffix in base.html); the
       button still reads "Logout" so the action remains identified.
     - Continue hiding the version pill — same v3.27.17 behaviour;
       brand + logout always stay, pill is the only droppable item.
     The 480px breakpoint is wider than v3.27.17's 420px because the
     collision class extends a bit higher than the pre-existing
     pill-drop class — the controls' min-content with the suffix is
     larger than the pill alone. */
  .topbar-brand-logo {
    max-width: min(50vw, 200px);
  }
  .nav-logout-suffix {
    display: none;
  }
  .app-topbar .version-pill {
    display: none;
  }
}
@media (max-width: 420px) {
  /* Reserved breakpoint slot — v3.27.17 originally dropped the
     version pill here, but v3.27.18 promotes that drop to ≤480px
     alongside the logout-suffix drop and the logo-cap step-down.
     The rule that lives here now is empty; kept for future
     phone-width-only refinements. */
}

/* ============================================================================
   Dashboard homepage (v3.27.8 layout)
   ----------------------------------------------------------------------------
   Two states gated by show_onboarding in home.html:
   - Populated: .pagehead + .section-label + .action-grid of .action-card
   - Empty: .welcome-panel + .firststep-panel + .next-steps of .next-step
   The stat-row + recent-activity / storage-snapshot panels from the v3.27.8
   mockup are intentionally NOT here yet — v3.27.9 dashboard-data-tiles
   owns them and has its own data-aggregation prereqs.
   ============================================================================ */
.pagehead {
  margin-bottom: 0.25rem;
}
.pagehead-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-gold);
  margin-bottom: 0.3rem;
}
.pagehead-title {
  /* v3.28.1 — Newsreader italic for the Folio editorial register
     (the design package's .ds-pagehead h1 + .dsh-masthead h1 are both
     Newsreader italic). Nested <b>/<strong> renders upright to mirror
     the design package's "italic body + upright emphasis" idiom. */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: clamp(1.6rem, 2.7vw, 2rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.05;
  color: var(--text);
  margin: 0;
  /* v3.27.18 — long single-word display names ("CoruscantSunrise" etc.)
     would otherwise extend past the viewport on narrow phones since
     the dashboard heading interpolates the display name without any
     break opportunity. overflow-wrap: anywhere lets the browser break
     inside a long word as a last resort (after whitespace breaks),
     keeping the heading inside the viewport without altering layout
     for short names. */
  overflow-wrap: anywhere;
}
.pagehead-title b,
.pagehead-title strong {
  font-style: normal;
  font-weight: 500;
}
.pagehead-tagline {
  /* v3.28.1 — Newsreader italic for editorial taglines (the design
     package's .ds-pagehead .lede uses Newsreader italic at this size). */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  color: var(--text-dim);
  font-size: 0.98rem;
  line-height: 1.45;
  margin-top: 0.35rem;
}

.section-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 1rem 0 0.5rem;
}

.action-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  /* v3.27.18 — at ≤768px this grid drops to a single column via the
     existing pre-v3.27.18 @media rule near line 1075 (which also
     targets .next-steps). 1-column-at-768px was the right call and
     stays. Calling it out here so future maintainers don't add
     parallel breakpoints. */
}
.action-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  text-decoration: none;
  color: var(--text);
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
}
.action-card:hover {
  border-color: var(--border);
  background: var(--panel-2);
}
.action-card .action-tag {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}
.action-card h3 {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
}
.action-card p {
  font-size: 0.81rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
  flex: 1;
}
.action-card .action-cta {
  align-self: flex-start;
  margin-top: 0.25rem;
  font-size: 0.81rem;
  font-weight: 600;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.9rem;
}
.action-card-highlight {
  border-color: var(--brand-gold);
  background: linear-gradient(180deg, var(--brand-gold-soft), var(--panel));
}
.action-card-highlight .action-cta {
  background: var(--brand-gold);
  color: #1a1205;
  border-color: var(--brand-gold);
}

/* ============================================================================
   Dashboard tiles (v3.27.10)
   ----------------------------------------------------------------------------
   Three tiles wired into the home.html populated state, sitting below the
   Quick Actions grid. Token-reuse only — no parallel tokens, no new visual
   language (aesthetic restyling is v3.27.13's job).

   Tiles 1 + 2 (Collection Value, Sets Collected) follow the action-card
   surface contract (--panel background, --border border, --radius corners)
   but emphasize a single large number instead of a description block. Tile
   3 (Recent Activity) is wider in content but matches the same surface.
   ========================================================================= */
.dashboard-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr 2fr;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
@media (max-width: 1024px) {
  .dashboard-tiles {
    grid-template-columns: 1fr 1fr;
  }
  .dash-tile-activity {
    grid-column: 1 / -1;
  }
}
@media (max-width: 640px) {
  .dashboard-tiles {
    grid-template-columns: 1fr;
  }
  .dash-tile-activity {
    grid-column: auto;
  }
}
.dash-tile {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  text-decoration: none;
  color: var(--text);
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
}
.dash-tile:hover {
  border-color: var(--border);
  background: var(--panel-2);
}
.dash-tile-eyebrow {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.dash-tile-number {
  /* v3.28.1 — Newsreader italic for the editorial stat-figure register
     (the design package's CollectionMetric .v is Newsreader italic 500). */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 2.25rem;
  font-weight: 500;
  line-height: 1;
  color: var(--text);
}
.dash-tile-sublabel {
  font-size: 0.85rem;
  color: var(--muted);
}
.dash-tile-pending {
  font-size: 0.8rem;
  color: var(--muted);
  border-top: 1px dashed var(--border);
  padding-top: 0.5rem;
  margin-top: 0.2rem;
}
.dash-tile-progress {
  height: 6px;
  background: var(--panel-2);
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin-top: 0.35rem;
}
.dash-tile-progress-fill {
  height: 100%;
  background: var(--brand-gold);
  border-radius: var(--radius-sm);
  transition: width 0.4s ease;
}
.dash-tile-activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.dash-tile-activity-list li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.82rem;
  line-height: 1.4;
}
.dash-tile-activity-type {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
  flex-shrink: 0;
  min-width: 5.5rem;
}
.dash-tile-activity-card {
  color: var(--text);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-tile-activity-when {
  color: var(--muted);
  font-size: 0.75rem;
  flex-shrink: 0;
}
.dash-tile-empty {
  font-size: 0.85rem;
  color: var(--muted);
  font-style: italic;
}

/* ============================================================================
   Watchlist (v3.27.12)
   ----------------------------------------------------------------------------
   Card-detail watch toggles + watchlist page type badges. Token-reuse
   only — no parallel tokens, no new visual language. Aesthetic restyling
   is v3.27.13's job.
   ========================================================================= */
.card-watch-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.watch-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 0.85rem;
  font-size: 0.85rem;
  border-radius: var(--radius-sm);
}
.watch-btn-on {
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
  color: var(--text);
}
.watch-btn-on:hover {
  border-color: var(--brand-gold);
}
.watch-type-badge {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.22rem 0.55rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.watch-type-printing {
  color: var(--text);
  background: var(--panel-2);
}
.watch-type-name {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--brand-gold);
}

/* ============================================================================
   Empty-state homepage (v3.27.8)
   ----------------------------------------------------------------------------
   Shown when show_onboarding is True for the home route. Replaces the
   populated dashboard with a single clear first step + numbered "what
   comes next" path — first-run UX that was missing.
   ============================================================================ */
.welcome-panel {
  background: linear-gradient(135deg, var(--panel-2), var(--panel));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.75rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  position: relative;
  overflow: hidden;
}
.welcome-panel::after {
  content: "";
  position: absolute;
  right: -40px;
  top: -40px;
  width: 220px;
  height: 220px;
  background: radial-gradient(
    circle,
    rgba(200, 161, 90, 0.13),
    transparent 65%
  );
  pointer-events: none;
}
.welcome-eyebrow {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-gold);
}
.welcome-title {
  /* v3.28.1 — Newsreader italic to match the editorial register on
     the populated-dashboard .pagehead-title. */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: clamp(1.7rem, 3.2vw, 2rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.05;
  margin: 0;
}
.welcome-title b,
.welcome-title strong {
  font-style: normal;
  font-weight: 500;
}
.welcome-tagline {
  /* v3.28.1 — Newsreader italic, matching .pagehead-tagline register. */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  color: var(--text-dim);
  font-size: 1rem;
  line-height: 1.55;
  max-width: 38rem;
}

.firststep-panel {
  background: linear-gradient(180deg, var(--brand-gold-soft), var(--panel));
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
}
.firststep-num {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--brand-gold);
  color: #1a1205;
  font-weight: 800;
  font-size: 1.15rem;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.firststep-body {
  flex: 1;
}
.firststep-body h3 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 0.15rem;
}
.firststep-body p {
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
}
.firststep-go {
  font-size: 0.86rem;
  font-weight: 700;
  background: var(--brand-gold);
  color: #1a1205;
  border-radius: var(--radius-sm);
  padding: 0.7rem 1.2rem;
  text-decoration: none;
  flex-shrink: 0;
}

.next-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}
.next-step {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  opacity: 0.7;
}
.next-step-num {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.next-step h4 {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0;
}
.next-step p {
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
}

@media (max-width: 768px) {
  .action-grid,
  .next-steps {
    grid-template-columns: 1fr;
  }
  .firststep-panel {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
  }
  .firststep-num {
    align-self: center;
  }
  .firststep-go {
    text-align: center;
  }
}

/* ============================================================================
   .page-shell wrapper (post-v3.27.8 — the .site-header* CSS family removed across v3.27.15 + v3.27.16)
   ----------------------------------------------------------------------------
   v3.27.8 retired the horizontal top header in favor of the .app-shell /
   .app-sidebar / .app-topbar layout. The .site-header / .header-shell /
   .header-left / .header-right CSS rules went dormant at that point. The
   v3.27.15 aesthetic pass struck the main desktop block but left two
   smaller @media blocks behind (lines ~1589-1612 carrying .header-shell /
   .header-right / .header-tagline mobile overrides, and lines ~4744-4762
   carrying .site-header nav / .site-header .header-right /
   .site-header .brand-wordmark mobile-hide overrides). v3.27.16 finished
   the deletion. DOM had not referenced any of these since v3.27.8, so
   removal had no visual effect.

   ``.page-shell`` is the surviving wrapper: every interior page (rendered
   inside .app-main) uses it as the content container. v3.27.8 fix history:
   the original rule was ``width: min(96vw, 1800px)``, viewport-relative,
   sized for the pre-v3.27.8 no-sidebar layout where this wrapper had the
   full viewport to fill. Post-v3.27.8, .page-shell lives inside .app-main
   (the 1fr column of a 236px 1fr grid), so its container is viewport−236px
   on the authenticated shell. 96vw of the FULL viewport then bled past
   the container — at 1280px viewport the wrapper computed 1229px in a
   1044px container, clipping ~185px off the right edge. Decks page's
   Edit/Delete column landed in that overflow region. ``width: 100%`` +
   max-width fills the container correctly on both authed (viewport−236px)
   and anon (full viewport) shells; the 1800px cap still applies on
   ultrawide displays; margin:0 auto centers when narrower than max-width.
   ============================================================================ */
.page-shell {
  width: 100%;
  max-width: 1800px;
  margin: 0 auto;
  padding-left: 1rem;
  padding-right: 1rem;
}

.version-pill {
  /* v3.27.15 — token-driven: was hardcoded var(--text) text + rgba(127,176,255)
     border + rgba(10,20,36) background; now uses --text-dim + --border +
     --panel surface tokens so the pill picks up the navy palette
     automatically. */
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.65rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  /* v3.27.8 fix — safeguard against text wrap. The dev-environment value
     is git-short-hash-prefixed (e.g. "dev-ce9cbd0"), longer than the prod
     version-string value ("v3.27.7"); under any unexpected width-squeeze
     the multi-segment text could wrap to two lines. nowrap forces it to
     stay on one line and overflow visibly rather than break. */
  white-space: nowrap;
}

.page-shell {
  padding-top: 1.4rem;
  padding-bottom: 2rem;
  display: grid;
  gap: 1rem;
}
/* Grid items default to min-width: auto which sizes to their min-content. A
   wide child (e.g. `.table-wrap table { min-width: 900px }`) would propagate
   up through the grid item and blow the section past the viewport on mobile,
   making .table-wrap's overflow-x: auto useless because the clipping ancestor
   is already wider than the screen. Forcing min-width: 0 here lets sections
   shrink to viewport width and lets .table-wrap actually scroll internally. */
.page-shell > * {
  min-width: 0;
}

.brand-logo-link {
  display: flex;
  align-items: center;
  text-decoration: none;
}
.brand-wordmark {
  height: 70px;
  width: auto;
  display: block;
}
.mana-pip {
  width: 24px;
  height: 24px;
  vertical-align: middle;
  margin-left: 2px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7));
}
.mana-pip-row {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 0.5rem;
  vertical-align: middle;
}
.eyebrow {
  /* v3.27.15 — repointed from hardcoded var(--brand-gold) (light blue) to gold to
     match the dashboard/eyebrow aesthetic that's the v3.27.15 visual
     reference. Used on multiple pages (login/register/forgot-password/
     reset-password panel-topline, card-detail hero, set-detail) as the
     small "PAGE SECTION LABEL" affordance.
     v3.28.3 — switched to inline-flex so the editorial_eyebrow() macro's
     24×1px hairline (`<span class="ln">`) sits inline with the text.
     Eyebrows without a `.ln` child (legacy callers, modals, etc.) still
     render correctly — flex with one child is layout-equivalent to a
     plain block. */
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--brand-gold);
  margin-bottom: 0.2rem;
}
/* v3.28.3 — the editorial 24×1px brass hairline that precedes the eyebrow
   text on the editorial_eyebrow() macro. Translates the design package's
   `.ds-pagehead .crumb .ln { width: 24px; height: 1px; background: var(--brass-2); }`
   from layout.jsx. Brass-2 (production --brand-gold) for the hairline tone. */
.eyebrow .ln {
  width: 24px;
  height: 1px;
  background: var(--brand-gold);
  flex: 0 0 auto;
}
/* v3.27.15 — auth-page brand-name surface picks up Montserrat Alternates.
   The "Cartarch" text inside .eyebrow on login/register/forgot/reset uses
   the brand face; everything else using .eyebrow on non-auth pages stays
   on Montserrat (body font). One-line override targeting only the auth
   bracket: a .eyebrow inside a .panel that has a sibling .panel-title
   reading "Sign In" / "Register" / "Forgot password" / "Reset password"
   doesn't have a clean selector — keeping the override at the document
   level lets every existing "Cartarch" eyebrow use Alternates. The text
   is identical on every auth page; the visual lift is small but matches
   the brand-face treatment in the sidebar. */
.auth-brand,
.eyebrow.brand-name {
  font-family: "Montserrat Alternates", "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.18em;
}
.site-title {
  margin: 0;
  font-size: 1.3rem;
  line-height: 1.15;
}
/* v3.27.16 — .header-tagline removed (was a leftover from the retired
   v3.x horizontal site-header DOM; no template referenced it post-v3.27.8). */
.muted {
  color: var(--muted);
}

nav {
  margin-top: 0.85rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
nav a {
  color: var(--link);
  text-decoration: none;
  font-weight: 500;
}
nav a:hover {
  color: white;
}

a {
  color: var(--link);
}

.panel {
  background: linear-gradient(
    180deg,
    rgba(58, 44, 29, 0.96) 0%,
    rgba(46, 36, 24, 0.96) 100%
  );
  border: 1px solid rgba(181, 138, 71, 0.32);
  border-radius: 20px;
  padding: 1.15rem 1.2rem;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
}
.hero-panel.compact-hero {
  padding-top: 1rem;
  padding-bottom: 1rem;
}
.panel-topline {
  color: var(--brand-gold);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.78rem;
  font-weight: 700;
  margin-bottom: 0.35rem;
}
.panel-title {
  margin: 0;
  font-size: 1.9rem;
  line-height: 1.05;
}
/* v3.28.1 — page-level panel titles (h2.panel-title inside .page-hero
   containers) get the Folio editorial register (italic Newsreader),
   matching .pagehead-title. h3.panel-title (sub-section headers like
   "Upload CSV" inside the page) stays Montserrat so the editorial register
   reads as a one-time page-entry signal, not a blanket serif sweep that
   would visually equalize every level. The <b>/<strong> upright override
   mirrors .pagehead-title's "italic body + upright emphasis" idiom. */
h2.panel-title {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-weight: 500;
  letter-spacing: -0.01em;
}
h2.panel-title b,
h2.panel-title strong {
  font-style: normal;
  font-weight: 500;
}
.hero-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: start;
  flex-wrap: wrap;
}
.hero-stats-inline {
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
  color: var(--text);
  font-weight: 700;
}

button {
  padding: 0.58rem 0.9rem;
  border: none;
  background: var(--accent);
  color: white;
  border-radius: 10px;
  cursor: pointer;
  font-weight: 600;
}
input,
select,
textarea {
  padding: 0.55rem 0.7rem;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
}
textarea {
  min-height: 90px;
}
.stack-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.stack-form label {
  font-size: 0.875rem;
  color: var(--muted);
  margin-bottom: -0.2rem;
}
.inline-form {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}
.compact-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 0.5rem;
  margin-top: 0.75rem;
}
.compact-form-grid button {
  white-space: nowrap;
}
.filter-row {
  display: flex;
  gap: 0.55rem;
  flex-wrap: wrap;
  align-items: center;
}
.filter-row input,
.filter-row select {
  min-width: 140px;
}

.stat-grid {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

/* Tighter variant used on /pending where the 3 stat cards (Pending Cards,
   Drawers Affected, Total Copies) hold short integer values. Default
   .stat-grid drops to 1 column too eagerly at the 480-768 tier because
   180px doesn't fit twice; compact variant keeps a 3-column layout down
   to 480px so the action buttons stay closer to the fold on phones. */
.stat-grid-compact {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 480px) {
  .stat-grid-compact {
    grid-template-columns: 1fr;
  }
}
.stat-card {
  background: rgba(26, 19, 12, 0.35);
  border: 1px solid rgba(181, 138, 71, 0.25);
  border-radius: 16px;
  padding: 0.85rem 1rem;
}
.stat-card.compact {
  padding: 0.75rem 0.9rem;
}
.stat-label {
  color: var(--muted);
  font-size: 0.92rem;
  margin-bottom: 0.3rem;
}
.stat-number,
.stat-value {
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1;
}
.collection-stats-grid {
  margin-top: 0.9rem;
}
/* v3.27.10: pending sub-stat. Headline aggregates are placed-only across
   every cross-page surface; the in-flight slice surfaces here as a small
   muted line with a gold-hover affordance pointing to /pending. Same
   visual contract as the dashboard tile's pending sub-stat. */
.pending-substat {
  margin-top: var(--space-2);
  font-size: 0.85rem;
  color: var(--muted);
}
.pending-substat a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px dashed var(--border);
  padding-bottom: 1px;
}
.pending-substat a:hover {
  color: var(--brand-gold);
  border-bottom-color: var(--brand-gold);
}
.drawer-pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
  margin-top: 0.95rem;
}
.drawer-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.52rem 0.8rem;
  border-radius: 999px;
  border: 1px solid rgba(181, 138, 71, 0.32);
  background: rgba(26, 19, 12, 0.38);
  color: var(--text);
  min-height: 44px;
}

.card-grid-section {
  display: grid;
  gap: 1rem;
}

.card-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.card-tile {
  width: 150px;
  border-radius: var(--radius-sm);
  padding: 6px;
  border: 1px solid transparent;
  transition:
    border-color 0.15s ease,
    background 0.15s ease,
    transform 0.15s ease;
}
/* v3.27.15 — Cards on /sets/{set_code} are now wrapped in
   /cards/{card_id} links when the local cards table carries the row
   (folded in from the deferred v3.27.13 finding). The .card-tile-link
   variant inherits color, removes the underline, and adds a subtle
   hover affordance so the interactive distinction is visible without
   re-styling card-name or card-meta independently. */
.card-tile-link {
  display: block;
  color: inherit;
  text-decoration: none;
}
.card-tile-link:hover {
  border-color: var(--border);
  background: var(--panel-2);
  transform: translateY(-1px);
}

.card-tile img {
  width: 100%;
  border-radius: var(--radius-sm);
}

.card-name {
  font-size: 12px;
  margin-top: 4px;
}

.inventory-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

.inventory-card {
  background: linear-gradient(
    180deg,
    rgba(37, 28, 18, 0.94) 0%,
    rgba(46, 36, 24, 0.94) 100%
  );
  border: 1px solid rgba(70, 95, 140, 0.4);
  border-radius: 18px;
  padding: 0.95rem;
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 1rem;
  min-height: 230px;
}

.inventory-card-compact {
  min-height: 200px;
}
.inventory-card-media {
  display: flex;
  align-items: start;
}
.inventory-thumb {
  width: 138px;
  aspect-ratio: 0.716 / 1;
  object-fit: cover;
  border-radius: 12px;
  border: 1px solid rgba(104, 131, 181, 0.42);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.28);
  display: block;
  background: var(--bg);
}
.inventory-thumb.placeholder {
  display: block;
}
.inventory-card-body {
  min-width: 0;
}
.inventory-card-title {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
  line-height: 1.05;
}
.inventory-card-title a {
  color: var(--text);
  text-decoration: none;
}
.inventory-card-title a:hover {
  color: white;
}
.inventory-subtitle {
  color: var(--muted);
  font-size: 0.96rem;
  margin-bottom: 0.65rem;
}
.inventory-meta,
.inventory-location {
  color: var(--text);
  font-size: 0.97rem;
  margin-bottom: 0.45rem;
}
.inventory-total {
  color: var(--accent-2);
  font-size: 1rem;
  font-weight: 700;
  margin-top: 0.45rem;
}

.pending-hero {
  display: grid;
  gap: 1rem;
}
.pending-actions {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-top: 0.75rem;
  border-top: 1px solid rgba(181, 138, 71, 0.28);
}
.pending-group details > summary {
  cursor: pointer;
  list-style: none;
  font-size: 1.05rem;
  font-weight: 700;
  padding: 0.25rem 0;
}
.pending-group details > summary::-webkit-details-marker {
  display: none;
}
.pending-list {
  display: grid;
  gap: 0.9rem;
  margin-top: 0.9rem;
}
.pending-item {
  display: grid;
  grid-template-columns: 96px 1fr auto;
  gap: 1rem;
  align-items: center;
}
.pending-item.cross-drawer {
  border: 1px solid var(--accent-2);
  box-shadow: 0 0 0 1px rgba(255, 213, 122, 0.25);
}
.pending-thumb-wrap {
  width: 96px;
}
.pending-thumb {
  width: 84px;
  border-radius: 8px;
  border: 1px solid #374151;
  display: block;
}
.pending-thumb.placeholder {
  height: 117px;
  background: var(--panel-2);
}
.pending-name {
  font-size: 1.02rem;
  font-weight: 700;
  margin-bottom: 0.28rem;
}
.pending-meta {
  color: var(--text-dim);
  margin-bottom: 0.28rem;
}
.pending-location {
  display: grid;
  gap: 0.2rem;
  color: var(--text);
}
.pending-location-from {
  color: var(--accent -2);
  font-size: 0.92rem;
  font-weight: 700;
}
.pending-location-to {
  color: var(--text);
  font-size: 0.95rem;
}
.pending-action {
  justify-self: end;
}

.danger-button {
  background: var(--danger);
  color: #fff;
}
.ghost-button {
  background: transparent;
  border: 1px solid #4b5563;
  color: #e5e7eb;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
}
th,
td {
  border-bottom: 1px solid rgba(181, 138, 71, 0.25);
  padding: 0.65rem 0.5rem;
  vertical-align: top;
  text-align: left;
}

.table-wrap {
  overflow-x: auto;
  width: 100%;
}

.table-wrap table {
  min-width: 900px;
}

/* ============================================================================
   Wide-table responsive treatment (v3.27.15, deferred from v3.27.8)
   ----------------------------------------------------------------------------
   v3.27.8 left wide interior tables falling back to raw horizontal scroll
   inside ``.table-wrap``. v3.27.15 layers column-priority hiding on top:
   as the viewport narrows, low-priority columns drop entirely (via the
   ``.col-priority-low`` / ``.col-priority-mid`` classes applied to
   specific <th>/<td> cells in each wide-table template), so the columns
   that remain stay readable without horizontal scroll being the primary
   interaction.

   Two priority tiers:
     - .col-priority-low   hidden at ≤980px (laptop-narrow / tablet)
     - .col-priority-mid   hidden at ≤768px (tablet portrait / phone)

   Both classes apply to the matching <th> AND the corresponding <td> in
   every row. <colgroup><col> would be cleaner but doesn't carry
   display:none across browsers reliably; per-cell class is the safe shape.

   ``.table-wrap`` horizontal scroll stays as the final-fallback floor for
   tables intrinsically wider than the viewport even after column drops.
   Uniform across pages — no per-page card layouts, no per-page interaction
   model.
   ============================================================================ */
@media (max-width: 980px) {
  th.col-priority-low,
  td.col-priority-low {
    display: none !important;
  }
}
@media (max-width: 768px) {
  th.col-priority-mid,
  td.col-priority-mid {
    display: none !important;
  }
}

@media (max-width: 980px) {
  .inventory-grid {
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  }
  .inventory-card {
    grid-template-columns: 110px 1fr;
  }
  .inventory-thumb {
    width: 100px;
  }
  .compact-form-grid {
    grid-template-columns: 1fr 1fr;
  }
  .compact-form-grid button {
    grid-column: 1 / -1;
  }
}

@media (max-width: 768px) {
  /* v3.27.16 — .header-shell / .header-right / .header-tagline rules
     removed from this @media block (dead DOM since v3.27.8 retired the
     horizontal top header). .page-shell padding override + .site-title
     font-size + the .inventory-card mobile rules below are LIVE and stay. */
  .page-shell {
    padding-left: 1rem;
    padding-right: 1rem;
  }
  .site-title {
    font-size: 1.05rem;
  }

  .inventory-card {
    grid-template-columns: 1fr;
  }
  .inventory-card-media {
    justify-content: center;
  }
  .inventory-thumb {
    width: 170px;
  }
  .pending-item {
    grid-template-columns: 72px 1fr;
  }
  .pending-action {
    grid-column: 2;
    justify-self: start;
  }
  .pending-thumb {
    width: 64px;
  }
}

.page-hero {
  display: block;
}

.feature-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.feature-card {
  display: block;
  text-decoration: none;
  color: var(--text);
  background: linear-gradient(
    180deg,
    rgba(37, 28, 18, 0.94) 0%,
    rgba(46, 36, 24, 0.94) 100%
  );
  border: 1px solid rgba(70, 95, 140, 0.4);
  border-radius: 18px;
  padding: 1rem 1.05rem;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
  transition:
    transform 140ms ease,
    border-color 140ms ease,
    box-shadow 140ms ease;
}

.feature-card:hover {
  transform: translateY(-2px);
  border-color: rgba(127, 176, 255, 0.6);
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.24);
}

.feature-kicker {
  color: var(--brand-gold);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.72rem;
  font-weight: 700;
  margin-bottom: 0.35rem;
}

.feature-title {
  font-size: 1.08rem;
  font-weight: 700;
  margin-bottom: 0.35rem;
  color: #f5f8ff;
}

.feature-card p {
  margin: 0;
  color: var(--muted);
  line-height: 1.45;
}
.progress-bar {
  width: 100%;
  height: 16px;
  background: #222;
  border-radius: 8px;
  margin: 10px 0;
}

.progress-fill {
  height: 100%;
  background: #2f9e44;
}

.rarity-breakdown {
  margin-top: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.rarity-row {
  display: grid;
  grid-template-columns: 90px 1fr 90px;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.85rem;
}
.rarity-label {
  font-weight: 600;
  letter-spacing: 0.02em;
}
.rarity-label-common {
  color: #a0a3a7;
}
.rarity-label-uncommon {
  color: #c0c4c8;
}
.rarity-label-rare {
  color: #d4af37;
}
.rarity-label-mythic {
  color: #f08020;
}
.rarity-label-special {
  color: #b070d0;
}
.rarity-label-bonus {
  color: #e070b0;
}
.rarity-bar {
  width: 100%;
  height: 8px;
  background: #1a1c1f;
  border-radius: 4px;
  overflow: hidden;
}
.rarity-bar-fill {
  height: 100%;
}
.rarity-bar-common {
  background: #8e9296;
}
.rarity-bar-uncommon {
  background: #c0c4c8;
}
.rarity-bar-rare {
  background: #d4af37;
}
.rarity-bar-mythic {
  background: #f08020;
}
.rarity-bar-special {
  background: #b070d0;
}
.rarity-bar-bonus {
  background: #e070b0;
}
.rarity-count {
  text-align: right;
  color: #aab;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 480px) {
  .rarity-row {
    grid-template-columns: 70px 1fr 70px;
    gap: 0.4rem;
    font-size: 0.78rem;
  }
}

.filter-tabs {
  display: flex;
  gap: 10px;
  margin-bottom: 15px;
}

.filter-tabs a {
  padding: 6px 10px;
  background: #333;
  border-radius: 6px;
  text-decoration: none;
}

.card-meta {
  font-size: 11px;
  opacity: 0.7;
}
.set-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

.set-dashboard-card {
  display: block;
  padding: 1rem;
  border-radius: 14px;
  background: var(--panel);
  border: 1px solid var(--border);
  text-decoration: none;
  color: var(--text);
}

.set-dashboard-card:hover {
  border-color: var(--accent);
}

.set-code {
  font-size: 1.15rem;
  font-weight: 800;
}

.set-name {
  color: var(--muted);
  margin-top: 0.2rem;
}

.set-progress-meta {
  margin-top: 0.8rem;
  font-size: 0.9rem;
}
.deck-create-form {
  display: grid;
  grid-template-columns: 200px 180px 1fr auto;
  gap: 0.6rem;
  align-items: stretch;
  margin: 1rem 0 2rem 0;
}

.deck-create-form input,
.deck-create-form select,
.deck-create-form button {
  height: 42px;
}
.deck-create-form textarea {
  min-height: 42px;
  height: 42px;
  max-height: 42px;
  resize: none;
}
.deck-item-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.deck-item-actions input {
  flex: 1 1 120px;
  min-width: 0;
}

.deck-item-actions button {
  width: 100%;
}
.deck-card,
.deck-card * {
  box-sizing: border-box;
}

.return-form,
.deck-item-actions {
  width: 100%;
  max-width: 100%;
}

.deck-item-actions input,
.deck-item-actions button {
  min-width: 0;
  max-width: 100%;
}
.deck-card .inventory-card-body {
  min-width: 0;
  overflow: hidden;
}
.return-details {
  display: inline-block;
}
.return-summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: inline-block;
  padding: 0.35rem 0.75rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.88rem;
  color: var(--text);
}
.return-summary::-webkit-details-marker {
  display: none;
}
.return-details[open] > .return-summary {
  opacity: 0.6;
}

.btn-like {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: inline-block;
  padding: 0.35rem 0.75rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.88rem;
  color: var(--text);
  white-space: nowrap;
}
.btn-like::-webkit-details-marker {
  display: none;
}

.inline-details {
  position: relative;
}
.inline-details .edit-popout {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 200;
  /* var(--surface) was undefined since v3.10.6 — popout has been
     functionally transparent. Same Known-Problem class as --bg-card /
     --text-muted (current-status.md). Swapped to the defined --panel
     token (matches v3.25.2 .fp-modal-inner, v3.26.0 .notes-modal-inner). */
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem;
  min-width: 260px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
/* Inline-create row (import preview "+ Create new deck/location"):
   the trigger buttons sit on the LEFT of their container, so the default
   `right: 0` anchor would push the 260px popout off-screen to the left at
   non-fullscreen browser widths above the mobile-popover breakpoint (768px).
   Anchor left so the popout extends rightward into available space. */
.inline-create-row .edit-popout {
  right: auto;
  left: 0;
}

.card-actions-drawer {
  margin-top: 0.6rem;
}
.card-actions-drawer > summary {
  cursor: pointer;
  list-style: none;
  color: var(--muted);
  font-size: 0.88rem;
  user-select: none;
}
.card-actions-drawer > summary::-webkit-details-marker {
  display: none;
}
.card-actions-drawer > summary::after {
  content: " ▾";
}
.card-actions-drawer[open] > summary::after {
  content: " ▴";
}
.card-actions-body {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin-top: 0.5rem;
}
.card-actions-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.nav-user {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.9rem;
}

.nav-logout-form {
  display: inline;
  margin: 0;
}

.nav-logout-button {
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text);
  padding: 0.4rem 0.75rem;
  cursor: pointer;
}

.nav-logout-button:hover {
  background: var(--panel-2);
}

.controls-panel {
  display: block;
}

/* Add-card panel on deck detail (v3.16.18) */
.add-card-panel {
  padding: 1rem 1.4rem;
}
.add-card-form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.add-card-label {
  font-weight: 600;
  margin-bottom: -0.2rem;
}
.add-card-input-row {
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
}
.add-card-input-row input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
}
.add-card-input-row input[type="number"] {
  width: 80px;
  flex: 0 0 auto;
  text-align: center;
}
.add-card-finish-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: center;
}
.add-card-finish-label {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
}
.add-card-results {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.35rem;
  max-height: 480px;
  overflow-y: auto;
  /* Cards with many reprints (Sol Ring ~80, basics ~hundreds) will scroll
     within this region; short-tail cards just render their actual count. */
}
.add-card-result {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
  min-height: 56px;
  width: 100%;
}
.add-card-result:hover,
.add-card-result:focus-visible {
  background: rgba(74, 144, 226, 0.15);
  border-color: #4a90e2;
  outline: none;
}
.add-card-result img {
  width: 38px;
  height: auto;
  border-radius: 3px;
  flex: 0 0 auto;
}
.add-card-result-noimg {
  width: 38px;
  height: 53px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 3px;
  flex: 0 0 auto;
}
.add-card-result-meta {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  overflow: hidden;
}
.add-card-result-meta strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.add-card-result-meta .muted {
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.add-card-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
}
.add-card-submit {
  min-height: 44px;
  padding: 0.5rem 1.5rem;
  font-weight: 600;
}
.add-card-submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
@media (max-width: 768px) {
  .add-card-input-row {
    flex-direction: column;
  }
  .add-card-input-row input[type="number"] {
    width: 100%;
  }
  .add-card-submit {
    width: 100%;
  }
}

/* Deck analytics panel */
.analytics-panel {
  padding: 1.1rem 1.4rem;
}
.analytics-grid {
  display: grid;
  grid-template-columns: 190px 1fr 160px;
  gap: 0 2.5rem;
  align-items: start;
}
.analytics-grid.no-pips {
  grid-template-columns: 190px 1fr;
}
.analytics-section {
  padding-right: 2.5rem;
  border-right: 1px solid rgba(181, 138, 71, 0.2);
}
.analytics-section:last-child {
  padding-right: 0;
  border-right: none;
}
.analytics-section-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: var(--brand-gold);
  margin-bottom: 0.5rem;
}
.analytics-avg-cmc {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1;
  margin-bottom: 0.85rem;
}
.analytics-avg-cmc-unit {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--muted);
}
.analytics-curve {
  display: flex;
  align-items: flex-end;
  gap: 5px;
  height: 108px;
}
.curve-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  height: 100%;
  justify-content: flex-end;
}
.curve-bar {
  width: 100%;
  border-radius: 3px 3px 0 0;
  min-height: 2px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.curve-bar-ramp-fill {
  background: linear-gradient(180deg, #42c46a 0%, #238a46 100%);
}
.curve-bar-spell-fill {
  background: linear-gradient(180deg, #5a9dff 0%, var(--accent) 100%);
}
.curve-count {
  font-size: 0.68rem;
  color: var(--muted);
  height: 1rem;
  line-height: 1rem;
}
.curve-label {
  font-size: 0.7rem;
  color: rgba(156, 169, 199, 0.7);
  margin-top: 4px;
}
.curve-insights {
  display: flex;
  flex-direction: column;
  gap: 0.22rem;
  margin-top: 0.6rem;
}
.curve-insight {
  font-size: 0.75rem;
  color: var(--muted);
}
.curve-insight strong {
  color: var(--text);
}
.curve-insight-low {
  color: #42c46a;
}
.curve-insight-moderate {
  color: #d4a840;
}
.curve-insight-high {
  color: #e86850;
}
.curve-insight-detail {
  color: var(--muted);
  font-size: 0.7rem;
}
.curve-legend {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.4rem;
}
.curve-legend-ramp {
  font-size: 0.7rem;
  color: #42c46a;
}
.curve-legend-spell {
  font-size: 0.7rem;
  color: #5a9dff;
}
.analytics-row {
  display: grid;
  grid-template-columns: 88px 1fr 2.2rem;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.42rem;
}
.arow-label {
  font-size: 0.85rem;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.arow-label-pip {
  display: flex;
  align-items: center;
}
.arow-bar-wrap {
  background: rgba(255, 255, 255, 0.06);
  border-radius: 4px;
  height: 10px;
  overflow: hidden;
}
.arow-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 4px;
}
.arow-bar-w {
  background: linear-gradient(90deg, #c8b47a, #e8d8a0);
}
.arow-bar-u {
  background: linear-gradient(90deg, #2d74c4, #5aa0e8);
}
.arow-bar-b {
  background: linear-gradient(90deg, #7a4db0, #a87de0);
}
.arow-bar-r {
  background: linear-gradient(90deg, #c43a2a, #e86850);
}
.arow-bar-g {
  background: linear-gradient(90deg, #238a46, #42c46a);
}
.arow-count {
  font-size: 0.8rem;
  color: var(--muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 768px) {
  .analytics-grid,
  .analytics-grid.no-pips {
    grid-template-columns: 1fr;
    gap: 1.25rem 0;
  }
  .analytics-section {
    padding-right: 0;
    border-right: none;
    border-bottom: 1px solid rgba(181, 138, 71, 0.2);
    padding-bottom: 1.25rem;
  }
  .analytics-section:last-child {
    border-bottom: none;
    padding-bottom: 0;
  }
  /* Mana-curve bar labels are functional data, not decoration. Bump them
     from ~11px to ~13.6px on mobile so the 0-6+ axis and per-bar counts
     are legible at arm's length. */
  .curve-label,
  .curve-count {
    font-size: 0.85rem;
  }
  .curve-count {
    height: auto;
    line-height: 1.2;
  }
}
.token-image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 1rem;
  margin-top: 0.75rem;
}
.token-card {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.token-card-img {
  width: 100%;
  border-radius: 8px;
  aspect-ratio: 63 / 88;
  object-fit: cover;
}
.token-card-placeholder {
  background: rgba(255, 255, 255, 0.05);
  border: 1px dashed rgba(181, 138, 71, 0.3);
}
.token-card-name {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
}
.token-card-type {
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.3;
}

/* Consistency score */
.consistency-row {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  margin-bottom: 0.25rem;
}
.consistency-badge {
  font-size: 1.8rem;
  font-weight: 800;
  line-height: 1;
  min-width: 3rem;
  text-align: center;
}
.cs-ok {
  color: #42c46a;
}
.cs-warn {
  color: #d4a840;
}
.cs-low {
  color: #e86850;
}
.consistency-info {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 140px;
}
.consistency-label {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}
.consistency-descriptor {
  font-size: 0.75rem;
  color: var(--muted);
}
.consistency-breakdown {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex: 1;
}
.cs-sub {
  display: grid;
  grid-template-columns: 58px 1fr 36px;
  align-items: center;
  gap: 0.4rem;
}
.cs-sub-label {
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}
.cs-sub-bar-wrap {
  background: rgba(255, 255, 255, 0.06);
  border-radius: 3px;
  height: 6px;
  overflow: hidden;
}
.cs-sub-bar {
  height: 100%;
  border-radius: 3px;
}
.cs-sub-bar.cs-ok {
  background: #42c46a;
}
.cs-sub-bar.cs-warn {
  background: #d4a840;
}
.cs-sub-bar.cs-low {
  background: #e86850;
}
.cs-sub-val {
  font-size: 0.68rem;
  color: var(--muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.health-divider {
  border: none;
  border-top: 1px solid rgba(181, 138, 71, 0.2);
  margin: 0.85rem 0;
}
@media (max-width: 768px) {
  .consistency-row {
    flex-wrap: wrap;
  }
  .consistency-breakdown {
    min-width: 100%;
  }
}

/* Deck health panel */
.health-grid {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 0 2.5rem;
  align-items: start;
}
.health-section-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: var(--brand-gold);
  margin-bottom: 0.55rem;
}
.health-pip-note {
  margin-bottom: 0.5rem;
}
.health-row {
  display: grid;
  grid-template-columns: 100px 1fr 72px auto;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.45rem;
}
.health-pip-row {
  grid-template-columns: 24px 1fr 120px;
}
.health-label {
  font-size: 0.85rem;
  color: var(--text);
  white-space: nowrap;
}
.health-bar-wrap {
  background: rgba(255, 255, 255, 0.06);
  border-radius: 4px;
  height: 10px;
  overflow: hidden;
}
.health-bar {
  height: 100%;
  border-radius: 4px;
  transition: width 0.3s ease;
}
.health-bar-ok {
  background: linear-gradient(90deg, #238a46, #42c46a);
}
.health-bar-warn {
  background: linear-gradient(90deg, #a07820, #d4a840);
}
.health-bar-low {
  background: linear-gradient(90deg, #8b2020, #c43a2a);
}
.health-count {
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.health-count-ok {
  color: #42c46a;
}
.health-count-warn {
  color: #d4a840;
}
.health-count-low {
  color: #e86850;
}
.health-cards-link {
  font-size: 0.78rem;
  color: var(--accent);
  white-space: nowrap;
  text-decoration: none;
  padding: 0.15rem 0.45rem;
  border-radius: 4px;
  border: 1px solid rgba(181, 138, 71, 0.3);
}
.health-cards-link:hover {
  background: rgba(181, 138, 71, 0.15);
}
.health-cards-link.active {
  background: rgba(181, 138, 71, 0.2);
  color: var(--text);
}
.health-cards-empty {
  color: var(--muted);
  font-size: 0.78rem;
}
.health-filter-badge {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--accent);
}
.health-filter-clear {
  font-size: 0.78rem;
  color: var(--muted);
  text-decoration: none;
}
.health-filter-clear:hover {
  color: var(--text);
}
.health-pips {
  border-left: 1px solid rgba(181, 138, 71, 0.2);
  padding-left: 2.5rem;
}
.health-pip-stat {
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.pip-sym {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-size: 0.65rem;
  font-weight: 800;
  color: #fff;
}
.pip-sym-w {
  background: #c8b47a;
  color: #333;
}
.pip-sym-u {
  background: #2d74c4;
}
.pip-sym-b {
  background: #7a4db0;
}
.pip-sym-r {
  background: #c43a2a;
}
.pip-sym-g {
  background: #238a46;
}
@media (max-width: 768px) {
  .health-grid {
    grid-template-columns: 1fr;
    gap: 1.25rem 0;
  }
  .health-pips {
    border-left: none;
    padding-left: 0;
    border-top: 1px solid rgba(181, 138, 71, 0.2);
    padding-top: 1.25rem;
  }
}

/* Card role tags */
.card-tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.35rem;
}
.card-tag {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  color: #fff;
}
.card-tag-ramp {
  background: #238a46;
}
.card-tag-draw {
  background: #2d74c4;
}
.card-tag-removal {
  background: #8b2020;
}
.card-tag-wipe {
  background: #a07820;
}
.card-tag-tutor {
  background: #6a2db0;
}
.card-tag-protection {
  background: #1a7a7a;
}
.card-tag-engine {
  background: #c46a20;
}
.card-tag-synergy {
  background: #b04080;
}
.card-tag-threat {
  background: #c43a2a;
}
.card-tag-hate {
  background: #4a5060;
}

/* Tag source / confidence indicators (v3.22.0).
   User-confirmed tags read at full opacity with solid borders — the default.
   Auto-tagged tags get a dashed inner border + slight opacity drop so they
   read as "system-suggested, not yet confirmed" at a glance without
   adding decorative iconography. Tooltips on the badges (set in the
   template) give the full source/confidence story on hover.
   Session 2+ will start producing `low`-confidence tags; that case
   subdues further. */
.card-tag.tag-source-user {
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.card-tag.tag-source-auto {
  border: 1px dashed rgba(255, 255, 255, 0.35);
  opacity: 0.78;
}
.card-tag.tag-conf-low {
  opacity: 0.55;
}

.legality-badge {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.45rem;
  border-radius: 3px;
  text-transform: uppercase;
}
.legality-banned {
  background: #7a1a1a;
  color: #ffd0d0;
}
.legality-restricted {
  background: #7a4a0a;
  color: #ffe0b0;
}
.legality-not-legal {
  background: #5a4a00;
  color: #ffe880;
}
/* v3.26.2 — per-location sorter-mode badge on the locations table.
   Mirrors the .legality-badge visual pattern (inline pill, dark-theme
   palette). One color per mode: green = managed (sorter active), amber =
   manual (hands-off), blue = sink (source-only catch-all), gray =
   ignored (invisible to sorter). */
.location-mode-badge {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.45rem;
  border-radius: 3px;
  text-transform: uppercase;
}
.location-mode-managed {
  background: #2d6e2d;
  color: #d0ffd0;
}
.location-mode-manual {
  background: #7a5a1a;
  color: #ffe0b0;
}
.location-mode-sink {
  background: #1a4a7a;
  color: #b0d0ff;
}
.location-mode-ignored {
  background: #444;
  color: #ccc;
}
.tag-editor-details {
  margin-top: 0.3rem;
}
.tag-editor-summary {
  font-size: 0.78rem;
  color: var(--accent);
  cursor: pointer;
  list-style: none;
}
.tag-editor-summary::-webkit-details-marker {
  display: none;
}
.tag-editor-form {
  margin-top: 0.35rem;
}
.tag-editor-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.2rem 0.5rem;
  margin-bottom: 0.4rem;
}
.tag-checkbox {
  font-size: 0.75rem;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.25rem;
  cursor: pointer;
}
.tag-suggested {
  color: #d4a840;
}
.tag-save-btn {
  font-size: 0.78rem;
  padding: 0.2rem 0.6rem;
}

.btn-danger-small {
  background: var(--danger);
  color: #fff;
  padding: 0.3rem 0.65rem;
  font-size: 0.85rem;
  border-radius: 8px;
}

.finish-badge {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 700;
  border: 1px solid rgba(181, 138, 71, 0.5);
}

/* Physical-language badge (v3.18.0). Shown next to the finish badge in
   the inventory_card macro when item.language is set and is not "en".
   Amber tint to distinguish from finish at a glance. */
.language-badge {
  display: inline-block;
  padding: 0.15rem 0.45rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
  background: rgba(245, 158, 11, 0.18);
  border: 1px solid rgba(245, 158, 11, 0.55);
  color: #f59e0b;
  letter-spacing: 0.04em;
  margin-left: 0.2rem;
}

/* Proxy badge (v3.19.0). Shown when InventoryRow.is_proxy is True.
   Muted gray so the badge reads as "physical-attribute note" rather
   than "warning" — proxies aren't problems, just a category. */
.proxy-badge {
  display: inline-block;
  padding: 0.15rem 0.45rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
  background: rgba(120, 120, 130, 0.18);
  border: 1px solid rgba(120, 120, 130, 0.55);
  color: #9ca3af;
  letter-spacing: 0.04em;
  margin-left: 0.2rem;
}

.warning-text {
  color: #b45309;
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 0.2rem;
}

/* Reconciliation table — duplicates-only view (v3.16.22).
   When `has_deck_only_dupes` is set, the table-wrap gets
   .reconcile-dupes-only and fresh (no-match) rows are display:none'd.
   They stay in the DOM so their hidden reconcile_action / new_qty
   fields submit at the right array indices — the commit handler walks
   parsed_rows by position. */
.reconcile-dupes-only .reconcile-row-fresh {
  display: none;
}
.reconcile-row-deck-only {
  background: rgba(244, 158, 76, 0.08);
}
.reconcile-row-deck-only td:first-child {
  border-left: 3px solid rgba(244, 158, 76, 0.6);
}

/* Win Conditions / Combo panel */
.combo-section-label {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin-bottom: 0.6rem;
}
.combo-section-label-gap {
  margin-top: 1.5rem;
}
.combo-item {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem 1rem;
  margin-bottom: 0.6rem;
  background: var(--panel-2);
}
.combo-item-almost {
  border-color: rgba(181, 138, 71, 0.3);
  opacity: 0.9;
}
.combo-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-bottom: 0.45rem;
}
.combo-card-name {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.2rem 0.55rem;
  font-size: 0.85rem;
  font-weight: 600;
}
.combo-card-missing {
  background: rgba(180, 83, 9, 0.12);
  border-color: rgba(180, 83, 9, 0.4);
  color: #c97c38;
}
.combo-results {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-bottom: 0.4rem;
}
.combo-result-badge {
  background: rgba(34, 197, 94, 0.12);
  border: 1px solid rgba(34, 197, 94, 0.35);
  color: #86efac;
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
  font-size: 0.8rem;
  font-weight: 600;
}
.combo-details {
  margin-top: 0.4rem;
}
.combo-summary {
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.combo-summary:hover {
  color: var(--text);
}
.combo-description {
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.4rem;
  line-height: 1.55;
  padding-left: 0.5rem;
  border-left: 2px solid var(--border);
}
.combo-prereq {
  font-size: 0.82rem;
  color: var(--muted);
  margin-top: 0.4rem;
  padding-left: 0.5rem;
  border-left: 2px solid var(--border);
}

/* Lazy-loaded panels */
.lazy-loading-panels {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.lazy-loading {
  padding: 1.5rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.85rem;
}
.bracket-loading {
  display: inline-block;
  width: 5rem;
  height: 1.4rem;
  background: var(--border);
  border-radius: 999px;
  opacity: 0.5;
}

/* Bracket badge */
.bracket-details {
  position: relative;
  display: inline-block;
}
.bracket-details[open] > summary::after {
  content: "";
}
.bracket-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  list-style: none;
  border: 1px solid transparent;
  white-space: nowrap;
}
.bracket-badge::-webkit-details-marker {
  display: none;
}
.bracket-1 {
  background: rgba(100, 180, 100, 0.15);
  border-color: rgba(100, 180, 100, 0.4);
  color: #86efac;
}
.bracket-2 {
  background: rgba(59, 130, 246, 0.15);
  border-color: rgba(59, 130, 246, 0.45);
  color: #93c5fd;
}
.bracket-3 {
  background: rgba(234, 179, 8, 0.15);
  border-color: rgba(234, 179, 8, 0.45);
  color: #fde047;
}
.bracket-4 {
  background: rgba(249, 115, 22, 0.15);
  border-color: rgba(249, 115, 22, 0.45);
  color: #fdba74;
}
.bracket-5 {
  background: rgba(239, 68, 68, 0.15);
  border-color: rgba(239, 68, 68, 0.45);
  color: #fca5a5;
}
.bracket-popout {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 100;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  min-width: 220px;
  max-width: 320px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
.bracket-popout-title {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.5rem;
}
.bracket-reasons {
  margin: 0;
  padding-left: 1.2rem;
  font-size: 0.85rem;
  color: var(--text);
  line-height: 1.6;
}

/* Commander Synergy panel */
.synergy-subtype-note {
  font-size: 0.82rem;
  color: var(--muted);
  margin: 0 0 0.75rem;
}
.synergy-bar {
  display: flex;
  height: 22px;
  border-radius: 999px;
  overflow: hidden;
  gap: 2px;
  margin-bottom: 1rem;
}
.synergy-seg {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.72rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.85);
  min-width: 28px;
  overflow: hidden;
  white-space: nowrap;
}
.synergy-seg-direct {
  background: linear-gradient(90deg, #2563eb, #3b82f6);
  border-radius: 999px 0 0 999px;
}
.synergy-seg-supporting {
  background: linear-gradient(90deg, #15803d, #22c55e);
}
.synergy-seg-unrelated {
  background: rgba(100, 116, 139, 0.55);
  border-radius: 0 999px 999px 0;
}
.synergy-stats {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}
.synergy-stat {
  flex: 1;
  min-width: 160px;
}
.synergy-stat-details > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  list-style: none;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
  user-select: none;
}
.synergy-stat-details > summary::-webkit-details-marker {
  display: none;
}
.synergy-stat-details > summary:hover .synergy-stat-label {
  color: var(--text);
}
.synergy-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
.synergy-dot-direct {
  background: #3b82f6;
}
.synergy-dot-supporting {
  background: #22c55e;
}
.synergy-dot-unrelated {
  background: #64748b;
}
.synergy-stat-label {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
  flex: 1;
}
.synergy-stat-count {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text);
}
.synergy-stat-pct {
  font-weight: 400;
  color: var(--muted);
}
.synergy-card-list {
  margin: 0.4rem 0 0.25rem;
  padding-left: 1.4rem;
  font-size: 0.82rem;
  color: var(--muted);
  line-height: 1.65;
  max-height: 220px;
  overflow-y: auto;
  columns: 2;
}

/* Dead card / upgrade targets panel */
.dead-cards-note {
  font-size: 0.88rem;
  color: var(--muted);
  margin: 0 0 0.6rem;
}
.dead-cards-details {
  font-size: 0.88rem;
}
.dead-cards-summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.85rem;
  padding: 0.2rem 0;
}
.dead-cards-summary:hover {
  color: var(--text);
}
.dead-cards-list {
  margin: 0.5rem 0 0.25rem;
  padding-left: 1.2rem;
  line-height: 1.7;
  columns: 2;
  max-height: 280px;
  overflow-y: auto;
}
.dead-card-name {
  color: var(--text);
}
.dead-card-tag {
  font-size: 0.72rem;
  background: var(--border);
  color: var(--muted);
  border-radius: 3px;
  padding: 0 4px;
  margin-left: 4px;
  vertical-align: middle;
}

/* ── Game tracker — tablet full-screen app ─────────────────── */

/* Body in game mode: no scrolling, header covered by overlay */
body.game-mode {
  overflow: hidden;
}

/* Full-screen app container — dark felt table */
.game-app {
  position: fixed;
  inset: 0;
  z-index: 500;
  background-color: #0b0f06;
  background-image:
    radial-gradient(ellipse at 50% 50%, #131a0c 0%, #0b0f06 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Ccircle cx='1' cy='1' r='0.6' fill='rgba(255,255,255,0.025)'/%3E%3C/svg%3E");
  background-blend-mode: normal, overlay;
  background-size:
    100% 100%,
    4px 4px;
}

/* Launch overlay — shown before fullscreen is entered */
.game-launch-overlay {
  position: fixed;
  inset: 0;
  z-index: 700;
  background: radial-gradient(ellipse at 50% 50%, #131a0c 0%, #0b0f06 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}
.game-launch-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
  text-align: center;
  padding: 2rem;
}
.game-launch-format {
  font-size: 1.1rem;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.game-launch-btn {
  padding: 1.1rem 2.8rem;
  background: rgba(34, 197, 94, 0.15);
  border: 1px solid rgba(34, 197, 94, 0.45);
  border-radius: 14px;
  font-size: 1.35rem;
  font-weight: 800;
  color: #22c55e;
  cursor: pointer;
  touch-action: manipulation;
  min-height: 60px;
  min-width: 220px;
}
.game-launch-btn:hover {
  background: rgba(34, 197, 94, 0.27);
}
.game-launch-btn:active {
  transform: scale(0.96);
}
.game-launch-skip {
  font-size: 0.78rem;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: underline;
  padding: 0;
}

/* Fullscreen toggle button in topbar */
.fs-btn {
  font-size: 1rem;
  padding: 0.1rem 0.35rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--muted);
  cursor: pointer;
  line-height: 1;
  touch-action: manipulation;
}
.fs-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.12);
}

/* Floating control bar at top center */
.game-topbar {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 600;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  background: rgba(26, 19, 12, 0.96);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 10px 10px;
  padding: 0.4rem 1rem;
  white-space: nowrap;
  font-size: 0.82rem;
  box-shadow: 0 3px 16px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(10px);
}
.game-topbar-link {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.78rem;
}
.game-topbar-link:hover {
  color: var(--text);
}
.game-topbar-sep {
  color: var(--border);
  user-select: none;
}
.game-topbar-end-btn {
  background: #ef444418;
  border-color: #ef444460;
  color: #ef4444;
  font-size: 0.78rem;
  padding: 0.2rem 0.55rem;
}
.game-topbar-end-btn:hover {
  background: #ef444430;
}
.undo-btn {
  font-size: 0.78rem;
  padding: 0.2rem 0.55rem;
}
.undo-btn:disabled {
  opacity: 0.3;
  cursor: default;
}
.turn-label {
  font-weight: 700;
  color: var(--text);
}
.turn-val {
  font-weight: 800;
  font-size: 1rem;
  min-width: 24px;
  text-align: center;
  color: var(--text);
}

/* Player grid — fixed full viewport, gap acts as divider lines */
.tracker-grid {
  position: fixed;
  top: 42px; /* clear the floating topbar */
  bottom: 38px; /* clear the history bar (overridden to 0 for ended games) */
  left: 0;
  right: 0;
  display: grid;
  gap: 3px;
  background: #0b0f06;
  z-index: 500;
  /* Layout computed dynamically by JS — buildDynamicGrid() / applyLayout() */
}

/* Ended games have no histbar, so no bottom offset needed */
.game-app[data-ended] .tracker-grid {
  bottom: 0;
}

/* Portrait mode — grid layout handled by JS applyLayout(); only CSS needed is turn row visibility */
@media (orientation: portrait) {
  .portrait-turn-row {
    display: flex !important;
    align-items: center;
    gap: 0.4rem;
  }
}

/* Portrait turn controls — hidden in landscape.
   v3.26.0 step 2: the whole row is the click target (onclick="nextTurn()");
   the inner .portrait-next-btn is decorative-but-clickable (clicks bubble). */
.portrait-turn-row {
  display: none;
  cursor: pointer;
  border-radius: 6px;
  padding: 0.05rem 0.25rem;
  transition: background-color 0.12s ease;
}
.portrait-turn-row:hover {
  background: rgba(255, 255, 255, 0.06);
}
.portrait-turn-name {
  font-weight: 700;
  color: var(--text);
  max-width: 90px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.portrait-next-btn {
  padding: 0.15rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.07);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  min-height: 28px;
}
.portrait-next-btn:active {
  transform: scale(0.92);
}

/* Card slot — grid item wrapper; tracker-card fills it absolutely */
.card-slot {
  position: relative;
  overflow: hidden;
  min-height: 0;
  min-width: 0;
}
.card-slot-empty {
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.03);
}
/* Floating center turn-indicator overlay — sits on top of the playmat grid,
   not a grid cell, so it doesn't consume a quadrant.
   v3.26.1 (iteration): border tracks the current-turn player's seat color via
   --current-player-color, set per-render() from SEAT_COLORS[seatIdx]. Falls
   back to the original faint-white border when the var isn't set (e.g. before
   first render). 0.3s transition keeps the color flip smooth as turns advance. */
.card-slot-center {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(13, 17, 8, 0.92);
  border: 2px solid var(--current-player-color, rgba(255, 255, 255, 0.12));
  border-radius: 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.55);
  padding: 0;
  pointer-events: auto;
  transition: border-color 0.3s ease;
}

/* Turn indicator content inside the floating overlay.
   v3.26.0 step 2: the whole panel is the click target (onclick="nextTurn()");
   the inner .turn-next-btn is decorative-but-clickable (clicks bubble). */
.turn-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  text-align: center;
  padding: 0.8rem 1.4rem;
  cursor: pointer;
  border-radius: 10px;
  transition: background-color 0.12s ease;
}
.turn-center:hover {
  background: rgba(255, 255, 255, 0.04);
}
.turn-center-round {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--muted);
  text-transform: uppercase;
}
.turn-center-name {
  font-size: clamp(1rem, 3vh, 1.8rem);
  font-weight: 800;
  color: var(--text);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* v3.26.7 — turn timing in the center turn marker. Two values: the active
   turn's elapsed time (primary, with ⏱ glyph) and the total game elapsed
   (secondary, after a ·). Both live-tick at 1Hz via the setInterval that
   re-calls render(). Tabular-nums so seconds advancing don't shift
   horizontal layout. */
.turn-center-timer {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.3rem;
  margin-top: 0.2rem;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.turn-timer-label {
  font-size: 0.85rem;
}
.turn-timer-sep {
  opacity: 0.4;
}
.turn-timer-game-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.7;
}
.portrait-timer {
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--muted);
}
.tc-on-clock {
  font-size: 0.7rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  margin-top: 0.1rem;
  opacity: 0.75;
}
.turn-next-btn {
  padding: 0.5rem 1.2rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #000;
  color: var(--text);
  font-size: 0.9rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  min-height: 44px;
}
.turn-next-btn:hover {
  background: rgba(255, 255, 255, 0.12);
}
.turn-next-btn:active {
  transform: scale(0.94);
}

/* Individual player card — playmat style */
.tracker-card {
  --player-color: #3b82f6;
  position: absolute;
  inset: 0;
  margin: auto;
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 38%, transparent) 0%,
      transparent 75%
    ),
    color-mix(in srgb, var(--player-color) 20%, #0d100a);
  border: none;
  border-top: 4px solid var(--player-color);
  padding: 1.25rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  overflow-x: hidden;
  overflow-y: auto;
  transition: opacity 0.2s;
}
.tracker-card.tc-eliminated {
  opacity: 0.25;
}
.tracker-card.tc-active-turn {
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 55%, transparent) 0%,
      transparent 75%
    ),
    color-mix(in srgb, var(--player-color) 32%, #0d100a);
  border-top-width: 6px;
}
/* v3.26.1: commander art panel background — opt-in via .has-commander-art class
   set in JS render() when seatDefs[i].commanderImageUrls is non-empty. Layered
   background composition (top → bottom in source order = top → bottom in
   rendered z-order): player-color radial tint preserves seat identity; dark
   linear gradient from bottom upward keeps life total + buttons readable;
   commander art via --commander-art-url-primary is the bottom layer. The
   element-relative `to top` gradient direction rotates with the card via
   transform: rotate(...), so the dark band lands at the player's
   reading-bottom for all four seat rotations.

   Partner / Choose-a-Background / Friends Forever decks (two commander rows
   tagged) additionally get .has-partner-art — side-by-side treatment:
   primary art_crop fills the left half, secondary fills the right, each at
   50% width × 100% height (explicit size, not cover). NOTE: this distorts
   the source aspect (art_crop is landscape ~1.37; each card half is portrait
   ~0.34) — commander faces stretch vertically. Iteration knob: switch to
   per-half overlay divs with object-fit:cover for aspect-preserving display
   at the cost of heavier cropping per half. */
.tracker-card.has-commander-art {
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 22%, transparent) 0%,
      transparent 65%
    ),
    linear-gradient(
      to top,
      rgba(13, 16, 10, 0.92) 0%,
      rgba(13, 16, 10, 0.72) 30%,
      rgba(13, 16, 10, 0.35) 60%,
      rgba(13, 16, 10, 0.12) 100%
    ),
    var(--commander-art-url-primary, none) center / cover no-repeat;
}
.tracker-card.tc-active-turn.has-commander-art {
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 38%, transparent) 0%,
      transparent 65%
    ),
    linear-gradient(
      to top,
      rgba(13, 16, 10, 0.92) 0%,
      rgba(13, 16, 10, 0.72) 30%,
      rgba(13, 16, 10, 0.35) 60%,
      rgba(13, 16, 10, 0.12) 100%
    ),
    var(--commander-art-url-primary, none) center / cover no-repeat;
}
.tracker-card.has-commander-art.has-partner-art {
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 22%, transparent) 0%,
      transparent 65%
    ),
    linear-gradient(
      to top,
      rgba(13, 16, 10, 0.92) 0%,
      rgba(13, 16, 10, 0.72) 30%,
      rgba(13, 16, 10, 0.35) 60%,
      rgba(13, 16, 10, 0.12) 100%
    ),
    var(--commander-art-url-primary, none) left center / 50% 100% no-repeat,
    var(--commander-art-url-secondary, none) right center / 50% 100% no-repeat;
}
.tracker-card.tc-active-turn.has-commander-art.has-partner-art {
  background:
    radial-gradient(
      ellipse at 50% 45%,
      color-mix(in srgb, var(--player-color) 38%, transparent) 0%,
      transparent 65%
    ),
    linear-gradient(
      to top,
      rgba(13, 16, 10, 0.92) 0%,
      rgba(13, 16, 10, 0.72) 30%,
      rgba(13, 16, 10, 0.35) 60%,
      rgba(13, 16, 10, 0.12) 100%
    ),
    var(--commander-art-url-primary, none) left center / 50% 100% no-repeat,
    var(--commander-art-url-secondary, none) right center / 50% 100% no-repeat;
}
/* Seat order badge — absolute, top-left corner */
.tc-seat-badge {
  position: absolute;
  top: 0.45rem;
  left: 0.45rem;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: var(--player-color);
  color: #0d1108;
  font-weight: 900;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  flex-shrink: 0;
}

/* Rotations — 180° (far side). rotate(180deg) ALONE is correct: the
   rotation and the opposite-side viewer cancel, restoring the same
   screen-left = player-left mapping as the 0° (near) case. v3.25.1 bug 2:
   the previous row-reverse (.tc-life-section) + column-reverse
   (.tc-life-btn-col) rules double-flipped this, swapping PLUS/MINUS on the
   far players' side — removed. tc-header is intentionally NOT reversed
   (elim button stays TR, seat badge stays TL after rotation). */
.tracker-card[data-rotate="180"] {
  transform: rotate(180deg);
  border-top: none;
  border-bottom: 4px solid var(--player-color);
}

/* 90°/270° rotated cards (side players): override the base inset:0 + margin:auto
   centering — that combo is over-constrained when JS sets explicit width/height
   and was rendering the card as a thin sliver in some browsers. Position the
   card's center at the slot center, then translate + rotate around that origin. */
.tracker-card[data-rotate="90"],
.tracker-card[data-rotate="270"] {
  inset: auto;
  margin: 0;
  top: 50%;
  left: 50%;
  transform-origin: center center;
}
.tracker-card[data-rotate="90"] {
  transform: translate(-50%, -50%) rotate(90deg);
  border-top: none;
  border-right: 4px solid var(--player-color);
}
/* 90°/270°: symmetric — NEITHER carries a life-section flex rule.
   Dev-verified from both p4 (right-end, 90°) and p8 (left-end, 270°)
   physical seats — PLUS/MINUS columns render correctly. If a future
   change appears to regress this, the symmetric contract is "both have
   row-reverse" or "neither does" — never asymmetric. Re-verify from
   both side-seats before flipping. */
.tracker-card[data-rotate="270"] {
  transform: translate(-50%, -50%) rotate(270deg);
  border-top: none;
  border-left: 4px solid var(--player-color);
}

/* Layout picker popover */
.layout-picker-btn {
  font-size: 0.78rem;
  padding: 0.2rem 0.55rem;
  background: #ffffff10;
  border-color: var(--border);
  color: var(--muted);
}
.layout-picker-btn:hover {
  color: var(--text);
  background: #ffffff18;
}
.layout-picker-popover {
  position: fixed;
  top: 2.6rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 700;
  background: rgba(26, 19, 12, 0.98);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7);
  min-width: 200px;
}
.layout-picker-title {
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.1rem;
}
.layout-option-btn {
  padding: 0.45rem 0.9rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  cursor: pointer;
  font-size: 0.85rem;
  text-align: left;
}
.layout-option-btn:hover,
.layout-option-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Card header — now only holds the elim button or placement badge,
   right-aligned (player names moved into .tc-life-center above the life total). */
.tc-header {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  flex-shrink: 0;
  gap: 0.3rem;
}
.tc-names {
  text-align: center;
}
.tc-player {
  font-weight: 700;
  font-size: clamp(0.95rem, 1.7vw, 1.3rem);
  color: var(--player-color);
}
.tc-deck {
  font-size: 0.75rem;
  color: var(--muted);
  margin-top: 0.1rem;
}
.tc-elim-btn {
  background: #000;
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 0.25rem 0.55rem;
  font-size: 1rem;
  opacity: 0.55;
  color: var(--text);
  min-width: 36px;
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tc-elim-btn:hover {
  opacity: 1;
  background: #ef444420;
  border-color: #ef4444;
}
/* v3.26.6 — per-seat commander art panel toggle. Mirrors .tc-elim-btn shape
   but hovers cyan (opt-in/visual control affordance vs the destructive
   red elim button). Inline-form wrapper has no styling of its own — exists
   only to carry the CSRF token + POST target. The .tc-art-toggle-off
   modifier dims further when the seat is currently in art-hidden mode,
   communicating state without a separate icon. */
.tc-art-toggle-form {
  display: inline-flex;
  margin: 0;
  padding: 0;
}
.tc-art-toggle-btn {
  background: #000;
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 0.25rem 0.55rem;
  font-size: 1rem;
  opacity: 0.55;
  color: var(--text);
  min-width: 36px;
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tc-art-toggle-btn.tc-art-toggle-off {
  opacity: 0.3;
}
.tc-art-toggle-btn:hover {
  opacity: 1;
  background: #06b6d420;
  border-color: #06b6d4;
}
.tc-placement {
  font-size: 1rem;
  font-weight: 700;
}
.placement-1 {
  color: #f59e0b;
}
.placement-2 {
  color: var(--muted);
}
.placement-3 {
  color: #b45309;
}

/* Life section */
.tc-life-section {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  flex: 1;
  min-height: 0;
}
.tc-life-btn-col {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.tc-life-btn {
  min-width: 58px;
  min-height: 52px;
  border-radius: 8px;
  border: 1px solid var(--border);
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 700;
  text-align: center;
  background: #000;
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition:
    transform 0.08s,
    background 0.1s;
}
.tc-life-plus {
  color: #4ade80;
  border-color: #22c55e40;
}
.tc-life-plus:hover {
  background: #22c55e18;
}
.tc-life-plus:active {
  background: #22c55e35;
  transform: scale(0.93);
}
.tc-life-minus {
  color: #f87171;
  border-color: #ef444440;
}
.tc-life-minus:hover {
  background: #ef444418;
}
.tc-life-minus:active {
  background: #ef444435;
  transform: scale(0.93);
}
.tc-life {
  font-size: clamp(2.5rem, 8vh, 7rem);
  font-weight: 900;
  line-height: 1;
  min-width: 3ch;
  text-align: center;
  color: var(--player-color);
  font-variant-numeric: tabular-nums;
  transition: color 0.2s;
}
.tc-life.tc-life-dead {
  color: #ef4444;
}

/* Life center column — holds life total + pill row, bounded by button cols */
.tc-life-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

/* P / E counter pills with inline ± buttons */
.tc-cpill-row {
  display: flex;
  gap: 0.35rem;
  justify-content: center;
  border-top: 1px solid var(--border);
  padding-top: 0.35rem;
  flex-shrink: 0;
  width: 100%;
}
.tc-cpill {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  padding: 0.15rem 0.3rem;
  border-radius: 6px;
  font-size: 0.78rem;
  font-weight: 700;
  border: 1px solid;
}
.tc-cpill-lbl {
  font-size: 0.85rem;
  margin-right: 0.1rem;
}
.tc-cpill-btn {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #000;
  color: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  flex-shrink: 0;
}
.tc-cpill-btn:active {
  background: rgba(255, 255, 255, 0.18);
  transform: scale(0.9);
}
.tc-cpill-p {
  background: #1e1a2e;
  border-color: #3b2e54;
  color: #a78bfa;
}
.tc-cpill-e {
  background: #1a1e14;
  border-color: #2d3d1e;
  color: #86efac;
}
/* v3.26.5 — single typed counter pill replacing the fixed Poison + Energy
   slots. Neutral palette (no semantic color tied to a specific counter type;
   the .tc-counter-danger override on the value still surfaces threshold
   when Poison is the active counter). */
.tc-cpill-x {
  background: #181820;
  border-color: #2a2a35;
  color: #cbd5e1;
}
.tc-cpill-x .tc-extra-type {
  background: transparent;
  color: inherit;
  border: none;
  font: inherit;
  font-weight: 700;
  padding: 0 0.15rem;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.tc-cpill-x .tc-extra-type:focus {
  outline: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 3px;
}
.tc-cpill-val {
  min-width: 1.6ch;
  text-align: center;
}
.tc-ctr-btn {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: 1px solid var(--border);
  cursor: pointer;
  background: #000;
  color: var(--text);
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.tc-ctr-btn:hover {
  background: rgba(255, 255, 255, 0.1);
}
.tc-ctr-btn:active {
  background: rgba(255, 255, 255, 0.18);
  transform: scale(0.9);
}
.tc-counter-val {
  min-width: 22px;
  text-align: center;
  font-weight: 700;
  font-size: 0.95rem;
}
.tc-counter-danger {
  color: #ef4444;
}
.tc-counter-warn {
  color: #f59e0b;
}

/* Commander damage */
.tc-cmd-details {
  border-top: 1px solid var(--border);
  padding-top: 0.4rem;
}
.tc-cmd-summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.78rem;
  list-style: none;
  user-select: none;
}
.tc-cmd-summary:hover {
  color: var(--text);
}
.tc-cmd-grid {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-top: 0.5rem;
}
.tc-cmd-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.tc-cmd-from {
  flex: 1;
  font-size: 0.78rem;
  color: var(--muted);
}
.tc-final-note {
  font-size: 0.8rem;
  color: var(--muted);
  text-align: center;
}

/* History bar at bottom center */
.game-histbar {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 600;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: rgba(26, 19, 12, 0.94);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  padding: 0.3rem 0.8rem;
  max-width: 90vw;
  overflow: hidden;
  backdrop-filter: blur(10px);
}
.history-entries {
  display: flex;
  gap: 0.35rem;
  flex-wrap: nowrap;
  overflow: hidden;
}
.history-entry {
  background: var(--border);
  border-radius: 3px;
  padding: 1px 6px;
  white-space: nowrap;
  font-size: 0.75rem;
  color: var(--muted);
}

/* End game modal */
.end-game-modal {
  position: fixed;
  inset: 0;
  z-index: 700;
  background: rgba(0, 0, 0, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.end-game-inner {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.5rem;
  max-width: 520px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
}

.end-game-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.end-game-player {
  width: 130px;
  font-weight: 600;
  flex-shrink: 0;
}

/* ─────────────────────────────────────────────────────────────────
   v3.28.13 — Folio game-tracker redesign (cluster re-opener).
   These rules OVERRIDE selected pre-v3.28.13 tracker rules above
   (.game-app palette, .tracker-grid offsets, .tracker-card layout,
   .tc-life typography, .tc-life-section layout). The rest of the
   pre-v3.28.13 tracker block — rotation (.tracker-card[data-rotate]),
   commander art panel background, .tc-seat-badge, .tc-elim-btn,
   .tc-art-toggle-btn, .tc-counter-warn/.tc-counter-danger,
   .end-game-modal — is PRESERVED VERBATIM and inherited.
   See folio-game-tracker-gap-2026-05-25.md (vault) for the gap
   analysis + 73-item preserve-verbatim inventory.
   gameFingerprint() NOT touched (35th+ successive release).
   ───────────────────────────────────────────────────────────────── */

/* Folio palette swap on the full-screen app container. The old
   #0b0f06/#131a0c "dark felt" gradient is replaced by Folio warm-charcoal
   tokens. The subtle SVG noise overlay is preserved for texture. */
body.game-mode .game-app {
  background-color: var(--bg);
  background-image:
    radial-gradient(ellipse at 50% 50%, var(--panel) 0%, var(--bg) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Ccircle cx='1' cy='1' r='0.6' fill='rgba(245,241,212,0.022)'/%3E%3C/svg%3E");
}

/* New full-width masthead (top band). Replaces the .game-topbar
   floating bar — content + Undo / Pause / End game live here now. */
.game-masthead {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 600;
  display: grid;
  grid-template-columns: minmax(220px, 1fr) auto minmax(220px, 1fr);
  align-items: center;
  gap: 1rem;
  height: 64px;
  padding: 0 1.25rem;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--panel) 92%, transparent),
    color-mix(in srgb, var(--panel) 78%, transparent)
  );
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(10px);
  color: var(--text);
  font-size: 0.86rem;
}
.mast-left {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
}
.mast-back {
  color: var(--muted);
  text-decoration: none;
  font-size: 1.05rem;
  line-height: 1;
  padding: 0.2rem 0.4rem;
  border-radius: 4px;
  transition: color 0.12s, background 0.12s;
}
.mast-back:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
}
.mast-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mast-eyebrow em {
  font-style: italic;
  color: var(--text);
}
.mast-hairline {
  display: inline-block;
  width: 24px;
  height: 1px;
  background: var(--brand-gold);
  flex: 0 0 auto;
}
.mast-sep {
  color: var(--border);
  font-style: normal;
  user-select: none;
}
.mast-format {
  color: var(--text-dim);
  font-style: normal;
}
.mast-center {
  text-align: center;
  min-width: 0;
  overflow: hidden;
}
.mast-turn-line {
  font-family: Newsreader, Georgia, serif;
  font-size: 1.05rem;
  color: var(--text);
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  white-space: nowrap;
}
.mast-turn-label {
  color: var(--muted);
  font-style: normal;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.mast-turn-num {
  font-style: italic;
  font-weight: 500;
  font-size: 1.2rem;
  color: var(--brand-gold);
}
.mast-turn-name {
  font-style: italic;
  font-weight: 500;
  color: var(--text);
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.3s ease;
}
.mast-turn-actor {
  color: var(--muted);
  font-style: normal;
  font-size: 0.82rem;
}
.mast-turn-sep {
  color: var(--border);
  font-style: normal;
}
.mast-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1.25rem;
  min-width: 0;
}
.mast-stats {
  display: inline-flex;
  align-items: center;
  gap: 1.25rem;
}
.mast-stat {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
  line-height: 1;
}
.mast-stat-label {
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}
.mast-stat-val {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 1rem;
  color: var(--text);
}
.mast-stat-of {
  color: var(--muted);
  font-style: normal;
  font-size: 0.78rem;
  margin-left: 0.1rem;
}
.mast-controls {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.mast-btn {
  padding: 0.35rem 0.7rem;
  background: rgba(245, 241, 212, 0.04);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text-dim);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.mast-btn:hover:not(:disabled) {
  color: var(--text);
  background: rgba(245, 241, 212, 0.08);
  border-color: var(--brand-gold);
}
.mast-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
.mast-btn-end {
  background: rgba(138, 46, 34, 0.18);
  border-color: rgba(138, 46, 34, 0.5);
  color: var(--danger);
}
.mast-btn-end:hover {
  background: rgba(138, 46, 34, 0.32);
  border-color: var(--danger);
  color: var(--danger);
}
.mast-btn-fs {
  padding: 0.3rem 0.55rem;
  font-size: 0.95rem;
  line-height: 1;
}
.mast-btn-paused {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.mast-btn-delete-discrete {
  background: rgba(138, 46, 34, 0.12);
  border-color: rgba(138, 46, 34, 0.32);
  color: rgba(138, 46, 34, 0.7);
  font-size: 0.7rem;
  padding: 0.2rem 0.5rem;
  opacity: 0.6;
}
.mast-btn-delete-discrete:hover {
  opacity: 1;
}

/* Grid clearance for new masthead height + bottom bar height. The
   pre-v3.28.13 .tracker-grid top/bottom offsets were 42px/38px against
   the floating .game-topbar / .game-histbar; v3.28.13's bands are
   full-width and slightly taller. */
body.game-mode .tracker-grid {
  top: 64px;
  bottom: 52px;
}
body.game-mode .game-app[data-ended] .tracker-grid {
  bottom: 0;
}

/* Paused dim — subtle visual cue across the whole tracker.
   The masthead Pause button gets its own .mast-btn-paused active style. */
.game-app.game-paused .tracker-grid::after {
  content: 'PAUSED';
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 599;
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: clamp(2rem, 6vh, 4rem);
  color: var(--brand-gold);
  background: rgba(15, 26, 46, 0.78);
  padding: 0.5rem 2rem;
  border: 1px solid var(--brand-gold);
  border-radius: 10px;
  letter-spacing: 0.2em;
  pointer-events: none;
}

/* Override the pre-v3.28.13 .tracker-card layout. The card was a flex
   column; v3.28.13 makes it a panel-local CSS grid with explicit
   corner zones (TL identity, TR counters+status+controls, mid cmd-strip,
   BL life numeral, BR button pad). Rotation transforms continue to wrap
   the card; the grid is panel-coordinate. Padding tightened so the
   commander art reads through more of the panel. */
body.game-mode .tracker-card {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "tl tr"
    "mid mid"
    "bl br";
  gap: 0.4rem 0.5rem;
  padding: 0.9rem 1rem;
  overflow: hidden;
}
.tc-corner {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  z-index: 1;
}
.tc-corner-tl {
  grid-area: tl;
  align-items: flex-start;
  gap: 0.2rem;
}
.tc-corner-tr {
  grid-area: tr;
  align-items: flex-end;
  gap: 0.35rem;
}
.tc-corner-bl {
  grid-area: bl;
  justify-content: flex-end;
  align-items: flex-start;
}
.tc-corner-br {
  grid-area: br;
  justify-content: flex-end;
  align-items: flex-end;
}
.tc-identity {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.92rem;
  color: var(--text);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  flex-wrap: wrap;
}
.tc-player-name {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  color: var(--text);
  font-size: 1.05rem;
}
.tc-commander-name {
  color: var(--text-dim);
  font-style: italic;
  font-size: 0.82rem;
}
.tc-identity-sep {
  color: var(--border);
}
/* On-clock timer below the identity line (already existed but tightened
   for the new corner placement). */
body.game-mode .tc-on-clock {
  font-size: 0.7rem;
  color: var(--text-dim);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

/* TR corner — status pills + counter pills + small controls. */
.tc-status-pills {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.25rem;
}
.tc-active-pill {
  display: none;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--brand-gold-soft);
  color: var(--brand-gold);
  border: 1px solid var(--brand-gold);
}
.tc-elim-pill {
  display: none;
  align-items: center;
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 0.75rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: rgba(138, 46, 34, 0.22);
  color: var(--danger);
  border: 1px solid rgba(138, 46, 34, 0.6);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.tc-counter-pills {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: flex-end;
  max-width: 100%;
}
body.game-mode .tc-cpill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.45rem;
  background: rgba(15, 26, 46, 0.75);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.78rem;
  color: var(--text);
  font-weight: 600;
}
body.game-mode .tc-cpill-poison {
  background: rgba(60, 24, 60, 0.78);
  border-color: rgba(167, 139, 250, 0.42);
  color: #d4b8ff;
}
body.game-mode .tc-cpill-experience {
  background: rgba(24, 60, 38, 0.78);
  border-color: rgba(134, 239, 172, 0.42);
  color: #b0f0c3;
}
body.game-mode .tc-cpill-energy {
  background: rgba(60, 48, 18, 0.78);
  border-color: rgba(245, 158, 11, 0.4);
  color: #fbd38d;
}
body.game-mode .tc-cpill-rad {
  background: rgba(60, 24, 18, 0.78);
  border-color: rgba(239, 68, 68, 0.42);
  color: #fda4a4;
}
body.game-mode .tc-cpill-storm {
  background: rgba(24, 36, 60, 0.78);
  border-color: rgba(59, 130, 246, 0.42);
  color: #a8c7ff;
}
body.game-mode .tc-cpill-custom {
  background: rgba(40, 40, 50, 0.78);
  border-color: rgba(245, 241, 212, 0.25);
  color: var(--text-dim);
}
.tc-cpill-icon {
  font-size: 0.92rem;
  line-height: 1;
}
.tc-cpill-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: inherit;
  opacity: 0.85;
}
body.game-mode .tc-cpill-btn {
  width: 20px;
  height: 20px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.4);
  color: inherit;
  border-radius: 4px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  font-size: 0.85rem;
}
body.game-mode .tc-cpill-val {
  min-width: 2ch;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}
.tc-cpill-rm {
  width: 18px;
  height: 18px;
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.5;
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0;
}
.tc-cpill-rm:hover {
  opacity: 1;
  color: var(--danger);
}
.tc-corner-controls {
  display: inline-flex;
  gap: 0.3rem;
}
body.game-mode .tc-elim-btn,
body.game-mode .tc-art-toggle-btn {
  min-width: 28px;
  min-height: 28px;
  padding: 0.15rem 0.4rem;
  font-size: 0.85rem;
}
.tc-add-counter-btn {
  background: rgba(245, 241, 212, 0.05);
  border: 1px dashed var(--border);
  border-radius: 5px;
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.2rem 0.5rem;
  cursor: pointer;
  min-height: 28px;
}
.tc-add-counter-btn:hover {
  color: var(--brand-gold);
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
}

/* Always-visible CMDR damage strip (mid zone). Read-only strip; cells
   are click-targets that open the global damage matrix scoped to the
   receiver (Fork G). */
.tc-cmd-strip {
  grid-area: mid;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  z-index: 1;
}
.tc-cmd-strip-label {
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.tc-cmd-strip-cells {
  display: inline-flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  justify-content: center;
}
.tc-cmd-cell {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  padding: 0.25rem 0.5rem;
  background: rgba(15, 26, 46, 0.7);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 600;
  min-width: 40px;
  transition: background 0.12s, border-color 0.12s;
}
.tc-cmd-cell:hover {
  background: rgba(15, 26, 46, 0.92);
  border-color: var(--brand-gold);
}
.tc-cmd-cell-from {
  font-size: 0.6rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.tc-cmd-cell-val {
  font-size: 0.95rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* BL — life numeral (italic Newsreader). Replaces the old centered
   tc-life-section + tc-life clamp(2.5rem, 8vh, 7rem) sans-serif. */
.tc-life-block {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1;
}
.tc-life-label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
body.game-mode .tc-life {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: clamp(3rem, 9vh, 7.5rem);
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.65);
  transition: color 0.2s;
  padding: 0;
  margin: 0;
}
body.game-mode .tc-life.tc-life-dead {
  color: var(--danger);
}

/* BR — life-adjust button pad. 2 columns × 3 rows: + column on the left,
   − column on the right; rows paired by magnitude (5, 1, 10). Reading down
   either column gives "all + actions" or "all − actions"; reading across a
   row gives the matched pair. Replaces a 3-col × 2-row layout that filled
   row-major and scrambled the +/− pairing. */
.tc-life-pad {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-rows: repeat(3, auto);
  gap: 0.35rem;
}
body.game-mode .tc-life-btn {
  min-width: 48px;
  min-height: 38px;
  padding: 0.3rem 0.5rem;
  background: rgba(15, 26, 46, 0.8);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  touch-action: manipulation;
  transition: transform 0.08s, background 0.1s, border-color 0.1s;
}
body.game-mode .tc-life-plus {
  color: #b8e8c4;
  border-color: rgba(106, 130, 83, 0.45);
}
body.game-mode .tc-life-plus:hover {
  background: rgba(106, 130, 83, 0.22);
  border-color: var(--brand-green);
}
body.game-mode .tc-life-plus:active {
  background: rgba(106, 130, 83, 0.4);
  transform: scale(0.94);
}
body.game-mode .tc-life-minus {
  color: #f0a8a0;
  border-color: rgba(138, 46, 34, 0.5);
}
body.game-mode .tc-life-minus:hover {
  background: rgba(138, 46, 34, 0.22);
  border-color: var(--danger);
}
body.game-mode .tc-life-minus:active {
  background: rgba(138, 46, 34, 0.4);
  transform: scale(0.94);
}

/* The pre-v3.28.13 .tc-life-section flex layout is no longer used by
   the redesigned template (the corner-grid replaces it). Nullify it
   so any inherited rule doesn't leak. */
body.game-mode .tc-life-section {
  all: unset;
}

/* Active panel — Fork F. The center-cell color-tracking cue (removed
   with the floating turn indicator) is replaced by a thicker active
   border in the player color. */
body.game-mode .tracker-card.tc-active-turn {
  border-top-width: 8px;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--player-color) 38%, transparent);
}

/* Bottom bar — § Chronicle + actions + LAST ACTION. Replaces the
   pre-v3.28.13 .game-histbar fixed-overlay strip. */
.game-bottombar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 600;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  height: 52px;
  padding: 0 1.25rem;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--panel) 92%, transparent),
    color-mix(in srgb, var(--panel) 78%, transparent)
  );
  border-top: 1px solid var(--border);
  backdrop-filter: blur(10px);
  color: var(--text);
  font-size: 0.82rem;
}
.bottombar-chron {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
  overflow: hidden;
}
.bottombar-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 0.88rem;
  color: var(--brand-gold);
  flex-shrink: 0;
}
.bottombar-eyebrow em {
  font-style: italic;
  color: var(--brand-gold);
}
.bottombar-entries {
  display: inline-flex;
  gap: 0.7rem;
  align-items: center;
  flex-wrap: nowrap;
  overflow: hidden;
  min-width: 0;
}
.chron-entry {
  display: inline-flex;
  gap: 0.3rem;
  align-items: baseline;
  font-size: 0.76rem;
  color: var(--text-dim);
  white-space: nowrap;
}
.chron-ts {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 0.72rem;
}
.chron-text {
  color: var(--text-dim);
}
.bottombar-actions {
  display: inline-flex;
  gap: 0.45rem;
  justify-content: center;
}
.bottombar-btn {
  padding: 0.4rem 0.85rem;
  background: rgba(245, 241, 212, 0.05);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.bottombar-btn:hover {
  background: rgba(245, 241, 212, 0.1);
  border-color: var(--brand-gold);
}
.bottombar-btn-primary {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--brand-gold);
  font-weight: 700;
}
.bottombar-btn-primary:hover {
  background: var(--brand-gold);
  color: var(--brand-near-black);
  border-color: var(--brand-gold);
}
.bottombar-lastaction {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  justify-content: flex-end;
  min-width: 0;
  overflow: hidden;
}
.bottombar-lastaction-label {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  flex-shrink: 0;
}
.bottombar-lastaction-text {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 0.9rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Floating add-counter menu. */
.add-counter-menu {
  position: fixed;
  z-index: 750;
  width: 180px;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.45rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
}
.add-counter-menu-title {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  padding: 0.2rem 0.4rem 0.3rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.2rem;
}
.add-counter-opt {
  text-align: left;
  background: transparent;
  border: none;
  padding: 0.4rem 0.55rem;
  border-radius: 4px;
  color: var(--text);
  font-size: 0.85rem;
  cursor: pointer;
}
.add-counter-opt:hover {
  background: var(--brand-gold-soft);
  color: var(--brand-gold);
}

/* Damage matrix modal (Fork G). */
.damage-matrix-modal {
  position: fixed;
  inset: 0;
  z-index: 720;
  background: rgba(0, 0, 0, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.damage-matrix-inner {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.25rem 1.5rem;
  max-width: 920px;
  width: 100%;
  max-height: 85vh;
  overflow: auto;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.7);
}
.damage-matrix-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.damage-matrix-close {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 6px;
  width: 32px;
  height: 32px;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}
.damage-matrix-close:hover {
  color: var(--text);
  border-color: var(--brand-gold);
}
.damage-matrix-grid {
  overflow: auto;
}
table.dmtx {
  border-collapse: collapse;
  width: 100%;
  color: var(--text);
}
table.dmtx th,
table.dmtx td {
  border: 1px solid var(--border);
  padding: 0.4rem 0.6rem;
  font-size: 0.85rem;
  text-align: center;
}
table.dmtx thead th {
  background: var(--panel-2);
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  color: var(--text-dim);
}
table.dmtx .dmtx-rowhead {
  background: var(--panel-2);
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  color: var(--text-dim);
  text-align: left;
}
.dmtx-row-focus .dmtx-rowhead,
.dmtx-row-focus .dmtx-cell {
  background: var(--brand-gold-soft);
}
.dmtx-name {
  display: inline-block;
  max-width: 110px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}
.dmtx-cell {
  min-width: 96px;
}
.dmtx-cell-self {
  color: var(--muted);
}
.dmtx-btn {
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.35);
  color: var(--text);
  border-radius: 4px;
  cursor: pointer;
  font-weight: 700;
  line-height: 1;
  padding: 0;
}
.dmtx-btn:hover {
  background: rgba(245, 241, 212, 0.08);
  border-color: var(--brand-gold);
}
.dmtx-val {
  display: inline-block;
  min-width: 2ch;
  margin: 0 0.4rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--text);
}
.damage-matrix-foot {
  margin-top: 0.85rem;
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-size: 0.8rem;
  color: var(--muted);
  text-align: right;
}

/* Mobile / narrow-width treatment. The masthead's stats cluster wraps;
   the bottombar collapses to a tighter layout; the per-seat button pad
   reduces to a single row of L+/S+/+10/L−/S−/−10 if needed. */
@media (max-width: 980px) {
  .game-masthead {
    height: auto;
    min-height: 64px;
    padding: 0.5rem 0.85rem;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    gap: 0.5rem 0.85rem;
  }
  .mast-left { grid-column: 1; grid-row: 1; }
  .mast-center { grid-column: 2; grid-row: 1; text-align: right; }
  .mast-right { grid-column: 1 / -1; grid-row: 2; gap: 0.8rem; }
  .mast-stats { gap: 0.85rem; }
  body.game-mode .tracker-grid {
    top: 108px;
  }
}
@media (max-width: 640px) {
  .bottombar-lastaction { display: none; }
  .game-bottombar {
    grid-template-columns: 1fr auto;
    gap: 0.5rem;
  }
  .mast-stat-label { font-size: 0.6rem; }
}

/* First-player picker modal (v3.25.2 — new-game flow). Overlay/sizing
   mirror .end-game-modal; uses the defined theme tokens (--panel,
   --muted) rather than the undefined --bg-card/--text-muted the legacy
   .end-game-inner references (pre-existing bug, tracked separately). */
.fp-modal {
  position: fixed;
  inset: 0;
  z-index: 700;
  background: rgba(0, 0, 0, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.fp-modal-inner {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.5rem;
  max-width: 460px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  text-align: center;
}
.fp-modal-title {
  margin: 0 0 1rem;
  font-size: 1.15rem;
}
.fp-seat-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  margin-bottom: 1rem;
}
.fp-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  min-width: 5.5rem;
  padding: 0.6rem 0.75rem;
  border: 2px solid var(--border);
  border-radius: 10px;
  background: #ffffff10;
  color: var(--text);
  cursor: pointer;
  font: inherit;
}
.fp-chip:hover {
  border-color: var(--accent);
}
.fp-chip.selected {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}
.fp-chip.rolling {
  border-color: var(--accent);
  background: #ffffff28;
  transform: scale(1.06);
}
.fp-chip-seat {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.fp-chip.selected .fp-chip-seat {
  color: #ffffffcc;
}
.fp-chip-name {
  font-weight: 700;
  font-size: 0.95rem;
}
.fp-roll-btn {
  display: block;
  width: 100%;
  padding: 0.7rem 1rem;
  margin-bottom: 0.75rem;
  border: 1px solid var(--accent);
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
}
.fp-roll-btn:disabled {
  opacity: 0.55;
  cursor: progress;
}
.fp-result {
  min-height: 1.4rem;
  margin-bottom: 1rem;
  font-weight: 600;
  color: var(--text);
}
.fp-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}
.fp-cancel-btn,
.fp-start-btn {
  padding: 0.55rem 1.1rem;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
}
.fp-cancel-btn {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
}
.fp-start-btn {
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
}
.fp-start-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Notes modal — opens from games-list rows to view/edit Game.notes (v3.26.0).
   Mirrors the .fp-modal overlay idiom; left-aligned content with a wider
   max-width to give the textarea room. */
.notes-modal {
  position: fixed;
  inset: 0;
  z-index: 700;
  background: rgba(0, 0, 0, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.notes-modal-inner {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.5rem;
  max-width: 560px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
}
.notes-modal-textarea {
  width: 100%;
  min-height: 6rem;
  padding: 0.5rem;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: inherit;
  font: inherit;
  resize: vertical;
}
.notes-cell {
  cursor: pointer;
  max-width: 240px;
}
.notes-cell-preview {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.85rem;
  color: var(--muted);
}
.notes-cell-add {
  font-size: 0.85rem;
  color: var(--muted);
  font-style: italic;
}
.notes-cell:hover .notes-cell-preview,
.notes-cell:hover .notes-cell-add {
  color: var(--accent-2);
}

/* New game form */
.player-count-row {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.player-count-btn {
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--border);
  background: var(--panel);
  border-radius: 4px;
  cursor: pointer;
  color: var(--text);
  font-size: 0.9rem;
}
.player-count-btn.active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.seat-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.seat-row-label {
  width: 70px;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--muted);
  padding-top: 0.3rem;
  flex-shrink: 0;
}
.seat-row-fields {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  flex: 1;
}
.seat-row-fields input {
  width: 140px;
}
.seat-row-fields select {
  flex: 1;
  min-width: 160px;
}

/* Seat position pill button */
.seat-pos-pill {
  padding: 0.25rem 0.7rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  touch-action: manipulation;
}
.seat-pos-pill.unassigned {
  color: var(--muted);
  background: rgba(255, 255, 255, 0.03);
}
.seat-pos-pill.assigned {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(59, 130, 246, 0.08);
}

/* Seat diagram */
.seat-diagram-wrap {
  margin-top: 1.5rem;
  margin-bottom: 0.25rem;
}
.seat-diagram-label {
  font-size: 0.78rem;
  color: var(--muted);
  margin-bottom: 0.5rem;
}
.seat-diagram {
  display: grid;
  grid-template-rows: auto auto auto;
  gap: 4px;
  max-width: 420px;
  user-select: none;
}
.sd-top-row,
.sd-bot-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 4px;
}
.sd-mid-row {
  display: grid;
  grid-template-columns: 80px 1fr 80px;
  gap: 4px;
  align-items: stretch;
}
.sd-seat {
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
  text-align: center;
  transition:
    background 0.12s,
    border-color 0.12s;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.sd-seat:hover:not(.sd-seat-inactive) {
  background: rgba(255, 255, 255, 0.07);
  border-color: var(--muted);
}
.sd-seat.sd-seat-filled {
  border-color: var(--accent);
  background: rgba(59, 130, 246, 0.1);
}
.sd-seat.sd-seat-pending {
  border-color: #22c55e;
  background: rgba(34, 197, 94, 0.1);
  animation: seat-pulse 0.8s infinite alternate;
}
.sd-seat.sd-seat-inactive {
  opacity: 0.25;
  cursor: default;
}
@keyframes seat-pulse {
  from {
    background: rgba(34, 197, 94, 0.08);
  }
  to {
    background: rgba(34, 197, 94, 0.2);
  }
}
.sd-pos {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--muted);
  letter-spacing: 0.05em;
}
.sd-name {
  font-size: 0.78rem;
  color: var(--text);
}
.sd-center {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  color: var(--muted);
}

/* Deck record badge on deck detail */
.deck-record {
  font-weight: 700;
  color: var(--accent);
}

/* Bracket Estimator V2 panel */
.bracket-v2-panel {
  margin-top: 1rem;
}
/* (.bracket-v2-row defined below as a grid — earlier flex declaration removed) */
.bracket-v2-badge {
  width: 64px;
  height: 64px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.4rem;
  font-weight: 800;
  color: #0d0f0f;
  flex-shrink: 0;
}
.bracket-v2-meta {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.bracket-v2-mech {
  font-size: 0.95rem;
}
.bracket-v2-findings-details summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--muted);
  list-style: none;
  padding: 0.25rem 0;
}
.bracket-v2-findings-details summary::-webkit-details-marker {
  display: none;
}
.bracket-v2-findings {
  list-style: none;
  padding: 0.4rem 0 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.bracket-v2-finding {
  font-size: 0.875rem;
  padding: 0.4rem 0.6rem;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.04);
  border-left: 3px solid #4a5568;
}
.bracket-v2-finding.bracket-v2-info {
  border-left-color: #4a90e2;
}
.bracket-v2-finding.bracket-v2-warning {
  border-left-color: #c46a20;
  background: rgba(196, 106, 32, 0.08);
}
.bracket-v2-finding.bracket-v2-critical {
  border-left-color: #c43a2a;
  background: rgba(196, 58, 42, 0.1);
}
.bracket-v2-sev-icon {
  margin-right: 0.4rem;
}

/* Bracket V2 panel — intent + confidence + survey */
.bracket-v2-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 1rem;
}
.bracket-v2-intent {
  font-size: 0.95rem;
}
.bracket-v2-aligned {
  color: #4ade80;
  margin-left: 0.5rem;
  font-size: 0.85rem;
}
.bracket-v2-confidence {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 220px;
}
.bracket-v2-conf-row {
  display: grid;
  grid-template-columns: 110px 1fr 36px;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
}
.bracket-v2-conf-label {
  color: var(--muted);
}
.bracket-v2-conf-bar-wrap {
  height: 6px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  overflow: hidden;
}
.bracket-v2-conf-bar {
  height: 100%;
  background: #4a90e2;
}
.bracket-v2-conf-val {
  text-align: right;
  color: var(--muted);
}
.bracket-v2-intent-survey summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: #4a90e2;
  padding: 0.25rem 0;
  list-style: none;
}
.bracket-v2-intent-survey summary::-webkit-details-marker {
  display: none;
}
.bracket-v2-intent-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-top: 0.5rem;
}
.bracket-v2-intent-form label {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.bracket-v2-intent-form button {
  grid-column: 1 / -1;
  justify-self: start;
  margin-top: 0.4rem;
}
@media (max-width: 768px) {
  .bracket-v2-row {
    grid-template-columns: auto 1fr;
  }
  .bracket-v2-confidence {
    grid-column: 1 / -1;
  }
  .bracket-v2-intent-form {
    grid-template-columns: 1fr;
  }
}

/* Token cataloging */
.token-needs-panel table {
  margin-top: 0.4rem;
}
.token-status-ok {
  color: #4ade80;
  font-weight: 600;
}
.token-status-missing {
  color: #f59e0b;
  font-weight: 600;
}
.filter-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted);
  user-select: none;
}
.filter-checkbox input {
  margin: 0;
}

/* Token search picker on /tokens/new */
.token-search-results {
  margin-top: 0.5rem;
}
.token-search-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.5rem;
}
.token-search-card {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.4rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
}
.token-search-card:hover {
  background: rgba(74, 144, 226, 0.15);
  border-color: #4a90e2;
}
.token-search-card img {
  width: 100%;
  border-radius: 4px;
  display: block;
}
.token-search-noimg {
  aspect-ratio: 0.72;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.75rem;
}
.token-search-meta {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

/* Token completion tiles on set detail */
.card-tile.token-tile-owned {
  border-left: 3px solid #4ade80;
}
.card-tile.token-tile-missing {
  border-left: 3px solid #f59e0b;
  opacity: 0.78;
}

/* Double-faced token in the search picker — show both sides */
.token-search-faces {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.2rem;
}
.token-search-faces img {
  width: 100%;
  border-radius: 4px;
}

/* Side popouts (image previews on /tokens) — open to the right of the trigger
   instead of below. Avoids pushing rows when the image is taller than the row. */
.inline-details.inline-details-side .edit-popout {
  left: calc(100% + 4px);
  right: auto;
  top: 0;
  max-width: 240px;
}

/* ============================================================================
   Mobile bottom-tab navigation
   ----------------------------------------------------------------------------
   Above 768px: bottom nav and More overlay are hidden; the v3.27.8 left
   sidebar (.app-sidebar) is the desktop nav.
   At or below 768px: the sidebar hides (see the .app-shell @media block
   above), and bottom-nav (Home, Collection, Decks, Games, More) is fixed
   to the viewport bottom; "More" opens a full-screen overlay containing
   Import, Pending, Locations, Tokens, Sets, Drawers/Audit (conditional),
   Admin (conditional), Account, Logout.
   v3.27.16 — the .site-header nav / .site-header .header-right /
   .site-header .brand-wordmark dead rules that used to hide the
   retired horizontal-header DOM at ≤768px were removed here. The
   sidebar-hide rule (.app-sidebar { display: none }) lives in the
   .app-shell @media block; this block now only owns the bottom-nav
   reveal + the body padding-bottom that makes room for it.
   ============================================================================ */
.bottom-nav,
.more-menu {
  display: none;
}

@media (max-width: 768px) {
  body {
    padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  }
  body.game-mode {
    padding-bottom: 0;
  }
  body.more-menu-open {
    overflow: hidden;
  }
  .bottom-nav {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 800;
    background: linear-gradient(180deg, #06142a 0%, #050f22 100%);
    border-top: 1px solid rgba(86, 115, 168, 0.45);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.4);
  }
  body.game-mode .bottom-nav {
    display: none;
  }
  .bottom-nav-tab {
    background: transparent;
    border: none;
    color: var(--muted);
    text-decoration: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 0.45rem 0.25rem 0.55rem;
    min-height: 60px;
    font: inherit;
    cursor: pointer;
    position: relative;
    -webkit-tap-highlight-color: transparent;
  }
  .bottom-nav-tab.active {
    color: var(--text);
  }
  .bottom-nav-tab.active::before {
    content: "";
    position: absolute;
    top: 0;
    left: 18%;
    right: 18%;
    height: 3px;
    background: var(--accent);
    border-radius: 0 0 3px 3px;
  }
  .bottom-nav-tab:active {
    background: rgba(255, 255, 255, 0.04);
  }
  .bottom-nav-icon {
    font-size: 1.35rem;
    line-height: 1;
  }
  .bottom-nav-label {
    font-size: 0.72rem;
    letter-spacing: 0.02em;
  }
  .bottom-nav-badge {
    position: absolute;
    top: 0.3rem;
    right: 28%;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--danger);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
  }
  .bottom-nav-badge[hidden] {
    display: none;
  }
  .more-menu {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 900;
    background: rgba(26, 19, 12, 0.97);
    overflow-y: auto;
    padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  }
  .more-menu[hidden] {
    display: none;
  }
  .more-menu-inner {
    padding: 1.25rem 1.25rem 0;
    max-width: 480px;
    margin: 0 auto;
  }
  .more-menu-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
  }
  .more-menu-title {
    margin: 0;
    font-size: 1.35rem;
  }
  .more-menu-close {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 999px;
    width: 44px;
    height: 44px;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
  }
  .more-menu-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }
  .more-menu-list a {
    display: block;
    padding: 0.95rem 1rem;
    background: rgba(58, 44, 29, 0.55);
    border: 1px solid rgba(181, 138, 71, 0.32);
    border-radius: 12px;
    color: var(--text);
    text-decoration: none;
    font-weight: 600;
    font-size: 1rem;
    min-height: 44px;
  }
  .more-menu-list a:active {
    background: rgba(58, 44, 29, 0.8);
  }
  /* v3.29.3 — Donate in the mobile More menu. Inherits the .more-menu-list a
     base shell + structure (padding / radius / min-height), repointed to the
     gold-accent palette so it reads as the matching mobile mirror of the
     sidebar Donate. */
  .more-menu-list a.more-menu-donate {
    background: var(--brand-gold-soft);
    border-color: var(--brand-gold);
    color: var(--brand-gold);
    font-weight: 700;
  }
  .more-menu-list a.more-menu-donate:active {
    background: var(--brand-gold);
    color: var(--brand-near-black);
  }
  .more-menu-logout {
    margin-top: 1.5rem;
  }
  .more-menu-logout button {
    width: 100%;
    padding: 0.85rem 1rem;
    min-height: 48px;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 12px;
    color: var(--text);
    font-weight: 600;
    cursor: pointer;
  }
}

/* ============================================================================
   Stage 4 — mobile form/grid collapses + popout escape from table-wrap.
   ----------------------------------------------------------------------------
   At ≤ 768px:
   - .filter-row controls go full-width and stack one per row (kills the
     desktop min-width: 140px that produced 5-stack chaos on phones).
   - Long search placeholders are shortened via [data-mobile-placeholder]
     swap in base.html script.
   - Popouts (.edit-popout, .bracket-popout, .layout-picker-popover) cap
     at 90vw and overflow-auto. When inside a .table-wrap (overflow:auto
     scroll container that would clip them), they switch to position:fixed
     centered on the viewport.
   - Wide desktop grids (.health-row, .deck-create-form,
     .bracket-v2-conf-row, .deck-item-actions) collapse to single column.
   ============================================================================ */
@media (max-width: 768px) {
  /* Filter-row controls full-width on phones */
  .filter-row {
    flex-direction: column;
    align-items: stretch;
  }
  .filter-row > * {
    width: 100%;
  }
  .filter-row input,
  .filter-row select,
  .filter-row textarea,
  .filter-row button {
    min-width: 0;
    width: 100%;
  }
  .filter-row a {
    width: 100%;
  }
  .filter-row a button {
    width: 100%;
  }

  /* Popouts: cap at 90vw + scroll inside if tall */
  .edit-popout,
  .bracket-popout,
  .layout-picker-popover {
    max-width: 90vw;
    max-height: 70vh;
    overflow: auto;
  }
  /* Popouts inside a horizontally-scrolling table-wrap would be clipped.
     Switch them to viewport-fixed so they escape the scroll container. */
  .table-wrap .edit-popout,
  .table-wrap .bracket-popout {
    position: fixed;
    top: 50%;
    left: 50%;
    right: auto;
    transform: translate(-50%, -50%);
    max-width: 90vw;
    width: 320px;
    z-index: 950;
  }

  /* Logout-button label: hide the trailing "(Display Name)" on phones */
  .logout-display-name {
    display: none;
  }

  /* Health row: bar wraps to its own row below label/count/link */
  .health-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
  }
  .health-row .health-bar-wrap {
    flex: 1 1 100%;
    order: 99;
  }
  .health-row .health-label {
    flex: 0 1 auto;
  }
  .health-row .health-count {
    margin-left: auto;
  }
  .health-pip-row .pip-sym {
    flex: 0 0 24px;
  }

  /* bracket-v2 confidence rows: label wraps if needed */
  .bracket-v2-conf-row {
    grid-template-columns: 1fr 36px;
    row-gap: 0.2rem;
  }
  .bracket-v2-conf-row .bracket-v2-conf-label {
    grid-column: 1 / -1;
    font-size: 0.78rem;
  }
  .bracket-v2-conf-row .bracket-v2-conf-bar-wrap {
    grid-column: 1;
  }
  .bracket-v2-conf-row .bracket-v2-conf-val {
    grid-column: 2;
  }

  /* Deck create form: stack each control to its own row */
  .deck-create-form {
    grid-template-columns: 1fr;
  }
  .deck-create-form input,
  .deck-create-form select,
  .deck-create-form button,
  .deck-create-form textarea {
    width: 100%;
  }

  /* Deck-item action row inside card body: stack qty inputs above buttons */
  .deck-item-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .deck-item-actions input,
  .deck-item-actions button {
    width: 100%;
  }
}

/* ============================================================================
   Stage 6 — remaining page-level mobile fixes (post-audit sweep).
   ----------------------------------------------------------------------------
   - Inventory-card grid shrinks 320 → 260px so tablet portrait shows two cards
     per row. Single-column rule for cards <768px stays in place.
   - .pending-action: form + select fill the row on mobile so the location
     picker isn't squeezed under the 72px thumb.
   - .card-actions-body inline-forms: select/qty inputs use min-width:0 + flex
     so the "Add to Deck" / "Move" dropdowns don't blow past 100% width.
   - .hero-row + .hero-stats-inline: stack vertically below 480px so the
     bracket badge and stats don't crowd the deck title on small phones.
   - Inline button-rows (deck hero, location hero, card_detail Actions) wrap
     and get a slight gap bump on mobile.
   ============================================================================ */
@media (max-width: 768px) {
  .inventory-grid {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  }
  .pending-action,
  .pending-action form,
  .pending-action select,
  .pending-action button {
    width: 100%;
  }
  .pending-action form {
    flex-direction: column;
    align-items: stretch;
  }
  .card-actions-body .inline-form {
    width: 100%;
    flex-wrap: wrap;
  }
  .card-actions-body .inline-form select,
  .card-actions-body .inline-form input[type="text"] {
    flex: 1 1 auto;
    min-width: 0;
  }
  .card-actions-body .inline-form button {
    flex: 0 0 auto;
  }
}

@media (max-width: 480px) {
  .hero-row {
    flex-direction: column;
    align-items: stretch;
  }
  .hero-stats-inline {
    gap: 0.6rem 1rem;
  }
  .panel-title {
    font-size: 1.45rem;
  }

  /* Inventory grid + thumb compaction for true phones.
     - Grid minmax(220px) lets wider phones (~480px landscape) fit 2 cards
       per row instead of always 1.
     - Thumb width 130px (was 170px at the 768 tier) trims a chunky vertical
       block on iPhone-SE-width portraits without making the art unreadable
       — a 130×182 (aspect 0.716) crop is still the readable Scryfall crop.
       Action drawer below stays full-width via existing card-body rules. */
  .inventory-grid {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  }
  .inventory-thumb {
    width: 130px;
  }

  /* Drawer-pill density: tighten horizontal padding so more pills fit per
     row on /collection at iPhone-SE-width. The 44px min-height set on the
     base .drawer-pill rule preserves the tap target — vertical padding
     stays comfortable via inline-flex align-items: center. */
  .drawer-pill {
    padding: 0.4rem 0.7rem;
  }
}

/* ============================================================================
   Stage 5 — form layouts: game_new seat rows + import panels.
   ----------------------------------------------------------------------------
   game_new.html: .seat-row-fields stack vertically; selects + inputs fill
   row width. Player-count buttons + seat-pos-pill picked up by the global
   44px tap-target rule.

   import.html: .filter-row controls already stack via the Stage 4 block;
   no extra rules required.
   ============================================================================ */
@media (max-width: 768px) {
  .seat-row {
    flex-direction: column;
    align-items: stretch;
  }
  .seat-row-label {
    width: auto;
  }
  .seat-row-fields {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }
  .seat-row-fields input,
  .seat-row-fields select,
  .seat-row-fields button {
    width: 100% !important;
    min-width: 0 !important;
  }
}

/* ============================================================================
   Tap-target enforcement — 44px minimum at tablet-portrait and below.
   ----------------------------------------------------------------------------
   Excludes the live game tracker (`body.game-mode` set by game_detail.html JS).
   Explicit :not() list covers the small tracker buttons even before JS sets
   .game-mode (avoids a brief layout distortion on slow first paint).

   In-scope: every <button> outside the tracker, plus .btn-like / .return-summary
   / .bracket-badge / .health-cards-link (these are <summary> or <a> elements
   styled as buttons).

   Out-of-scope by design: .tc-cpill-btn, .tc-ctr-btn, .tc-elim-btn, .tc-life-btn,
   .tc-art-toggle-btn (v3.26.6), .fs-btn (per audit Issue 7.7 — tracker is
   tablet-landscape-first).
   ============================================================================ */
@media (max-width: 768px) {
  body:not(.game-mode)
    button:not(.tc-cpill-btn):not(.tc-ctr-btn):not(.tc-elim-btn):not(
      .tc-life-btn
    ):not(.tc-art-toggle-btn):not(.fs-btn) {
    min-height: 44px;
  }
  body:not(.game-mode) .btn-like,
  body:not(.game-mode) .return-summary,
  body:not(.game-mode) .bracket-badge,
  body:not(.game-mode) .health-cards-link {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* ============================================================================
   Mobile popover pattern (≤ 768px)
   ----------------------------------------------------------------------------
   Apply .mobile-popover to any popout element to opt it into a centered,
   viewport-constrained, backdrop-dismissable presentation on phones. The
   element retains its existing desktop layout above 768px.

   The backdrop element + body scroll lock + dismiss wiring are handled by
   the IIFE in base.html — CSS here just defines the visual presentation.

   Usage: see docs/mobile_patterns.md.
   ============================================================================ */
.mobile-popover-backdrop {
  display: none;
}
.mobile-popover-close {
  display: none;
}
@media (max-width: 768px) {
  .mobile-popover {
    position: fixed !important;
    top: 50% !important;
    left: 50% !important;
    right: auto !important;
    bottom: auto !important;
    transform: translate(-50%, -50%) !important;
    width: min(90vw, 400px) !important;
    max-width: min(90vw, 400px) !important;
    max-height: 80vh !important;
    overflow: auto !important;
    z-index: 1100 !important;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6) !important;
  }
  .mobile-popover-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 1090;
  }
  body.mobile-popover-open {
    overflow: hidden;
  }
  .mobile-popover-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 0.25rem;
    right: 0.25rem;
    width: 44px;
    height: 44px;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.6rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
  }
  .mobile-popover-close:hover,
  .mobile-popover-close:focus {
    color: var(--text);
  }
}

/* ============================================================================
   Stacking tables (≤ 480px)
   ----------------------------------------------------------------------------
   Opt-in: tag a <table> with class="stacking-table" and add data-label
   attributes to the <td> cells that should render with a label prefix on
   phones. Cells without data-label render as-is (typical pattern: first
   cell is a heading, last cell is action buttons).

   Above 480px: the table renders normally (inside .table-wrap, which
   handles horizontal scroll if it overflows).
   At or below 480px: each <tr> becomes a self-contained card, each <td>
   becomes a row inside the card with "Label: value" pairs, action cells
   sit at the bottom of the card with a top divider.

   Used by: decks.html, locations.html, games.html.
   ============================================================================ */
@media (max-width: 480px) {
  /* Reset the desktop `.table-wrap table { min-width: 900px }` so the
     stacking table can shrink to viewport width without triggering
     horizontal scroll on the .table-wrap. */
  .table-wrap .stacking-table {
    min-width: 0;
  }
  .stacking-table,
  .stacking-table thead,
  .stacking-table tbody,
  .stacking-table tr,
  .stacking-table th,
  .stacking-table td {
    display: block;
  }
  .stacking-table thead {
    /* Visually hidden but available to screen readers when navigated as a table.
       Browsers strip semantics when display: block is applied anyway, so the
       practical effect is "hidden". */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .stacking-table tr {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--panel-2);
    padding: 0.8rem 1rem;
    margin-bottom: 0.75rem;
  }
  .stacking-table td {
    padding: 0.35rem 0;
    border: none;
    width: auto;
    overflow-wrap: anywhere;
  }
  /* Cells with data-label render as label+value pairs */
  .stacking-table td[data-label]::before {
    content: attr(data-label);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    font-weight: 600;
    display: block;
    margin-bottom: 0.1rem;
  }
  /* First cell (heading: name + link, no data-label) — larger font, no top padding */
  .stacking-table td:first-child {
    font-size: 1.05rem;
    font-weight: 600;
    padding-top: 0;
    padding-bottom: 0.5rem;
  }
  /* Last cell (action buttons, no data-label) — divider on top, full-width buttons stack */
  .stacking-table td:last-child {
    margin-top: 0.5rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border);
    text-align: left !important;
    white-space: normal !important;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  .stacking-table td:last-child a,
  .stacking-table td:last-child form,
  .stacking-table td:last-child details {
    margin: 0 !important;
  }
  /* The .inline-details popout summary is the Edit button — give it room */
  .stacking-table td:last-child .inline-details {
    display: inline-block;
  }
}

/* ============================================================================
   Deck list-view (v3.20.0)
   ----------------------------------------------------------------------------
   Moxfield-style compact text rows grouped by user-selectable axis, arranged
   in CSS multi-columns so many cards fit on a single screen. Each row is one
   line: qty + name + mana + tiny status tags. Detail (set, price, type) is
   reached via the hover/tap image preview rather than crowding the row.
   ============================================================================ */

.deck-view-toggle {
  display: inline-flex;
  gap: 0.4rem;
  margin: 0;
}
.deck-view-toggle-btn {
  padding: 0.4rem 0.9rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-size: 0.85rem;
  cursor: pointer;
}
.deck-view-toggle-btn.active {
  background: var(--accent, #2563eb);
  border-color: var(--accent, #2563eb);
  color: #fff;
  font-weight: 600;
}

.deck-list-view {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.deck-list-group {
  background: var(--panel-bg, #13161c);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.6rem 0.9rem 0.8rem;
}
.deck-list-group-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.5rem;
}
.deck-list-group-title {
  font-size: 1.05rem;
  font-weight: 700;
  margin: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
}
.deck-list-group-count {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--muted, var(--muted));
}
.deck-list-subgroups {
  font-size: 0.78rem;
  color: var(--muted, var(--muted));
  flex: 1 1 auto;
  line-height: 1.45;
}
.deck-list-subgroup {
  white-space: nowrap;
}

/* CSS multi-column layout. Each row is a flex line that lives inside a
   column; `break-inside: avoid` keeps a single row from splitting across
   the column gutter. column-count drops based on viewport. */
.deck-list-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  column-count: 3;
  column-gap: 1.4rem;
}
@media (max-width: 1100px) {
  .deck-list-rows {
    column-count: 2;
  }
}
@media (max-width: 640px) {
  .deck-list-rows {
    column-count: 1;
  }
}
.deck-list-row {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.15rem 0.2rem;
  border-radius: 3px;
  font-size: 0.88rem;
  line-height: 1.55;
  break-inside: avoid;
  cursor: default;
}
.deck-list-row:hover {
  background: rgba(255, 255, 255, 0.04);
}
.dlr-qty {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--muted, var(--muted));
  min-width: 1.8ch;
  text-align: right;
  flex: 0 0 auto;
}
.dlr-qty::after {
  content: "×";
  margin-left: 0.15ch;
  opacity: 0.55;
}
.dlr-name {
  color: var(--text);
  text-decoration: none;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dlr-name:hover {
  text-decoration: underline;
}
.dlr-mana {
  font-family: monospace;
  font-size: 0.78rem;
  color: var(--muted, var(--muted));
  white-space: nowrap;
  flex: 0 0 auto;
}
.dlr-meta {
  display: inline-flex;
  gap: 0.18rem;
  flex: 0 0 auto;
}
.dlr-tag {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 0.05rem 0.32rem;
  border-radius: 999px;
  background: rgba(120, 120, 130, 0.18);
  border: 1px solid rgba(120, 120, 130, 0.5);
  color: #cbd5e1;
  line-height: 1.4;
}
.dlr-tag.lang {
  background: rgba(245, 158, 11, 0.16);
  border-color: rgba(245, 158, 11, 0.55);
  color: #f59e0b;
}
.dlr-tag.finish {
  background: rgba(96, 165, 250, 0.18);
  border-color: rgba(96, 165, 250, 0.55);
  color: #93c5fd;
}
.dlr-tag.proxy {
  background: rgba(148, 163, 184, 0.18);
  border-color: rgba(148, 163, 184, 0.55);
  color: var(--muted);
}
.dlr-tag.warn {
  background: rgba(245, 158, 11, 0.18);
  border-color: rgba(245, 158, 11, 0.55);
  color: #f59e0b;
}
.dlr-tag.bad {
  background: rgba(239, 68, 68, 0.2);
  border-color: rgba(239, 68, 68, 0.6);
  color: #fca5a5;
}

/* Floating image-preview tooltip — fixed positioned by JS. Desktop: floats
   near the cursor; mobile: centered modal-style overlay. z-index sits ABOVE
   the switch-printing modal (1300) so hovering a printing row inside the
   modal still surfaces the card image instead of being occluded. */
.deck-list-image-preview {
  position: fixed;
  z-index: 1400;
  pointer-events: none;
  width: 240px;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  background: #000;
}
.deck-list-image-preview img {
  display: block;
  width: 100%;
  height: auto;
}
@media (max-width: 768px) {
  .deck-list-image-preview {
    width: min(80vw, 320px);
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    pointer-events: auto;
  }
}

/* ============================================================================
   In-place card editing (v3.21.0)
   ----------------------------------------------------------------------------
   Switch-printing modal + basic-land +/- controls. Modal is loaded into a
   viewport-fixed host via HTMX; CSS shows the backdrop + centered card via
   the .modal-open class on the partial's root. Both the list view and the
   grid view's actions drawer drive the same modal endpoint.
   ============================================================================ */

/* List-view per-row actions (⇄ + basic-land +/−). Lives next to .dlr-meta
   in the deck-list-row flex layout. */
.dlr-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  flex: 0 0 auto;
}
.dlr-qty-form,
.dlr-qty-form button,
.dlr-switch-btn {
  margin: 0;
  padding: 0;
}
.dlr-qty-form {
  display: inline-block;
}
.dlr-qty-btn,
.dlr-switch-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted, var(--muted));
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  line-height: 1;
}
.dlr-qty-btn:hover,
.dlr-switch-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
}
.dlr-switch-btn {
  width: 24px;
  font-size: 0.95rem;
}

/* Grid-view basic-land qty form: same compact button shape inside the
   existing card-actions-drawer's card-actions-body. */
.card-actions-body .basic-qty-form button {
  padding: 0.35rem 0.65rem;
}

/* ---- Switch-printing modal ---- */

.switch-printing-modal {
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: none;
  align-items: center;
  justify-content: center;
}
.switch-printing-modal.modal-open {
  display: flex;
}
.switch-printing-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: -1;
  cursor: pointer;
}
.switch-printing-modal-card {
  position: relative;
  background: var(--panel-bg, #13161c);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.6);
  width: min(92vw, 760px);
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.switch-printing-modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1rem 1.25rem 0.75rem;
  gap: 1rem;
  border-bottom: 1px solid var(--border);
}
.switch-printing-modal-title {
  margin: 0.15rem 0 0;
  font-size: 1.15rem;
  font-weight: 700;
}
.switch-printing-modal-close {
  background: transparent;
  border: none;
  color: var(--muted, var(--muted));
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.1rem 0.4rem;
  border-radius: 6px;
}
.switch-printing-modal-close:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}

.switch-printing-section {
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--border);
  overflow-y: auto;
}
.switch-printing-section:last-of-type {
  border-bottom: none;
}
.switch-printing-section-owned {
  background: rgba(96, 165, 250, 0.04);
}
.switch-printing-section-title {
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0 0 0.25rem;
  color: var(--text);
}
.switch-printing-section-blurb {
  margin: 0 0 0.6rem;
  font-size: 0.8rem;
}

.switch-printing-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.switch-printing-row {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.02);
  font-size: 0.86rem;
}
.switch-printing-row.current {
  border-color: rgba(96, 165, 250, 0.5);
  background: rgba(96, 165, 250, 0.08);
}
.switch-printing-row.owned {
  border-color: rgba(34, 197, 94, 0.35);
}
.switch-printing-row-set {
  font-weight: 500;
}
.switch-printing-row-locations,
.switch-printing-row-meta {
  font-size: 0.78rem;
}
.switch-printing-row-finishes,
.switch-printing-row-form {
  display: inline-flex;
  gap: 0.3rem;
  justify-self: end;
}
.switch-printing-finish-form {
  display: inline-block;
  margin: 0;
}
.switch-printing-finish-btn,
.switch-printing-pick {
  padding: 0.3rem 0.7rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-size: 0.8rem;
  cursor: pointer;
  white-space: nowrap;
}
.switch-printing-finish-btn:hover,
.switch-printing-pick:hover {
  background: rgba(255, 255, 255, 0.06);
}
.switch-printing-finish-btn:disabled,
.switch-printing-pick:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.switch-printing-finish-btn.owned {
  border-color: rgba(34, 197, 94, 0.5);
  color: #86efac;
}
.switch-printing-owned-count {
  opacity: 0.75;
  font-weight: 400;
}
.switch-printing-row-current-badge {
  display: inline-block;
  margin-left: 0.4rem;
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: rgba(96, 165, 250, 0.18);
  border: 1px solid rgba(96, 165, 250, 0.55);
  color: #93c5fd;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.switch-printing-frame-effect {
  display: inline-block;
  font-size: 0.7rem;
  padding: 0.05rem 0.35rem;
  margin-left: 0.3rem;
  border-radius: 999px;
  background: rgba(120, 120, 130, 0.18);
  border: 1px solid rgba(120, 120, 130, 0.5);
  color: #cbd5e1;
}

@media (max-width: 640px) {
  .switch-printing-modal-card {
    width: 96vw;
    max-height: 92vh;
  }
  .switch-printing-row {
    grid-template-columns: 1fr;
    grid-template-areas:
      "set"
      "meta"
      "finishes";
    gap: 0.3rem;
  }
  .switch-printing-row-set {
    grid-area: set;
  }
  .switch-printing-row-meta,
  .switch-printing-row-locations {
    grid-area: meta;
  }
  .switch-printing-row-finishes,
  .switch-printing-row-form {
    grid-area: finishes;
    justify-self: start;
    flex-wrap: wrap;
  }
}

/* === Review-tags panel (v3.23.3) ===
   Surfaces auto/medium tags awaiting user review on deck detail.
   Collapsed by default via <details>; chip-row inside each row lets
   the user confirm or remove per-tag, with a row-level "Confirm row"
   shortcut for cards with multiple auto-suggestions. */
.review-tags-panel {
  border-left: 3px solid rgba(245, 158, 11, 0.5); /* amber accent — "needs attention" */
}
.review-tags-details {
  display: block;
}
.review-tags-summary {
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  font-weight: 600;
  list-style: none;
  padding: 0.1rem 0;
}
.review-tags-summary::-webkit-details-marker {
  display: none;
}
.review-tags-summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 0.4rem;
  transition: transform 0.15s ease;
  color: var(--muted);
}
.review-tags-details[open] .review-tags-summary::before {
  transform: rotate(90deg);
}
.review-tags-summary-label {
  flex: 1 1 auto;
}
.review-tags-summary-count {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  background: rgba(245, 158, 11, 0.18);
  color: #f59e0b;
  border: 1px solid rgba(245, 158, 11, 0.4);
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 500;
}
.review-tags-help {
  margin: 0.5rem 0 0.85rem 0;
  font-size: 0.85rem;
  line-height: 1.45;
}
.review-tags-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.review-tags-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4rem 0.6rem;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
}
.review-tags-row-card {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.review-tags-row-name {
  font-weight: 500;
  color: inherit;
  text-decoration: none;
}
.review-tags-row-name:hover {
  text-decoration: underline;
}
.review-tags-row-confirmed {
  font-size: 0.78rem;
}
.review-tags-chip-row {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  flex-wrap: wrap;
  flex: 0 0 auto;
}
.review-tags-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  padding: 0.15rem 0.1rem 0.15rem 0.55rem;
  background: rgba(120, 120, 130, 0.14);
  border: 1px dashed rgba(120, 120, 130, 0.5);
  border-radius: 999px;
  font-size: 0.8rem;
  color: #cbd5e1;
}
.review-tags-chip-label {
  padding-right: 0.2rem;
}
.review-tags-chip-form {
  display: inline-flex;
  margin: 0;
}
.review-tags-chip-btn {
  background: transparent;
  border: 0;
  padding: 0.1rem 0.35rem;
  cursor: pointer;
  font-size: 0.78rem;
  line-height: 1;
  border-radius: 999px;
  color: var(--muted);
  min-height: 0;
}
.review-tags-chip-btn:hover {
  background: rgba(255, 255, 255, 0.06);
}
.review-tags-chip-confirm:hover {
  color: #22c55e;
}
.review-tags-chip-remove:hover {
  color: #ef4444;
}
.review-tags-row-confirm-all-form {
  display: inline-flex;
  margin: 0 0 0 0.35rem;
}
.review-tags-row-confirm-all-btn {
  background: rgba(34, 197, 94, 0.18);
  color: #22c55e;
  border: 1px solid rgba(34, 197, 94, 0.45);
  padding: 0.22rem 0.55rem;
  border-radius: 999px;
  font-size: 0.78rem;
  cursor: pointer;
  min-height: 0;
}
.review-tags-row-confirm-all-btn:hover {
  background: rgba(34, 197, 94, 0.28);
}
.review-tags-empty {
  margin: 0.5rem 0;
  font-style: italic;
}

@media (max-width: 640px) {
  .review-tags-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.4rem;
  }
  .review-tags-chip-row {
    width: 100%;
  }
}

/* =================================================================
   v3.27.17 — Public landing page
   =================================================================
   The landing template (templates/landing.html) does NOT extend
   base.html — it ships its own full HTML document and has no app
   shell (no sidebar, no .app-topbar). These rules style that surface
   in isolation. Token reuse only: every value resolves via the
   v3.27.7/v3.27.15 token layer; zero parallel tokens added.

   Renders the navy palette in the body background through the
   <body class="landing-body"> hook, then composes the rest of the
   page from .landing-* prefixed classes so they cannot collide
   with the app-shell rules above. The fixed-position body::before
   noise + body::after radial washes from the v3.27.15 reskin
   continue to render under the landing body — same calmer-finish
   visual direction. */

.landing-body {
  background: var(--bg);
  color: var(--text);
  margin: 0;
  font-family: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  min-height: 100vh;
}

.landing-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 2rem;
  border-bottom: 1px solid var(--border);
  position: relative;
  z-index: 2;
}
.landing-brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.landing-brand-logo {
  height: 32px;
  width: auto;
  display: block;
}
.landing-header-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.landing-nav-link {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 500;
}
.landing-nav-link:hover {
  color: var(--text);
}
.landing-cta-small {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 1.1rem;
  background: var(--brand-gold);
  color: var(--brand-near-black);
  border-radius: var(--radius-sm);
  font-weight: 700;
  text-decoration: none;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  transition: filter 0.15s ease;
}
.landing-cta-small:hover {
  filter: brightness(1.08);
}

.landing-main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem 4rem;
  position: relative;
  z-index: 1;
}

.landing-hero {
  text-align: center;
  padding: 3rem 1rem 4rem;
}
.landing-hero-logo {
  width: 220px;
  max-width: 50vw;
  height: auto;
  margin: 0 auto 2rem;
  display: block;
}
.landing-hero-tagline {
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  line-height: 1.15;
  margin: 0 0 1.25rem;
  color: var(--brand-gold);
  letter-spacing: 0.04em;
  text-transform: none;
  font-weight: 700;
}
.landing-hero-sub {
  font-size: 1.05rem;
  line-height: 1.55;
  max-width: 640px;
  margin: 0 auto 2rem;
  color: var(--text-dim);
}
.landing-hero-ctas {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
}
.landing-cta-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.75rem;
  background: var(--brand-gold);
  color: var(--brand-near-black);
  border-radius: var(--radius);
  font-weight: 700;
  text-decoration: none;
  font-size: 1rem;
  letter-spacing: 0.02em;
  transition:
    filter 0.15s ease,
    transform 0.15s ease;
  box-shadow: var(--shadow-sm);
}
.landing-cta-primary:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
}
.landing-cta-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.75rem;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
  font-size: 1rem;
  transition:
    background 0.15s ease,
    border-color 0.15s ease;
}
.landing-cta-secondary:hover {
  background: var(--panel-2);
  border-color: var(--brand-gold-soft);
}

.landing-section-title {
  font-size: 1.6rem;
  margin: 0 0 1.5rem;
  text-align: center;
  color: var(--text);
  letter-spacing: 0.01em;
}

.landing-features {
  padding: 2.5rem 0;
}
.landing-feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-4);
}
.landing-feature {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
  transition: border-color 0.15s ease;
}
.landing-feature:hover {
  border-color: var(--brand-gold-soft);
}
.landing-feature-title {
  margin: 0 0 0.65rem;
  color: var(--brand-gold);
  font-size: 1.1rem;
  letter-spacing: 0.02em;
}
.landing-feature-body {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.93rem;
  line-height: 1.55;
}

.landing-showcase {
  padding: 2.5rem 0;
}
.landing-screenshot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}
.landing-screenshot-placeholder {
  /* Placeholder tile until real screenshots land — keeps the layout
     honest and gives the eventual <img> swap a known aspect target.
     16:10-ish ratio via the padding-top trick on the inner span. */
  background: var(--panel-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  aspect-ratio: 16 / 10;
  display: flex;
  align-items: center;
  justify-content: center;
}
.landing-screenshot-label {
  color: var(--muted);
  font-size: 0.9rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.landing-secondary {
  padding: 2.5rem 0 1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-4);
}
.landing-secondary-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
}
.landing-secondary-title {
  margin: 0 0 0.65rem;
  color: var(--text);
  font-size: 1.05rem;
}
.landing-secondary-body {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.93rem;
  line-height: 1.55;
}
.landing-secondary-link,
.landing-secondary-body a {
  color: var(--brand-gold);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.landing-footer {
  border-top: 1px solid var(--border);
  margin-top: 3rem;
  padding: 2rem 1.5rem;
  position: relative;
  z-index: 2;
}
.landing-footer-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.landing-footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
  font-weight: 700;
  letter-spacing: 0.06em;
}
.landing-footer-logo {
  height: 26px;
  width: auto;
  display: block;
}
.landing-footer-nav {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}
.landing-footer-nav a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.9rem;
}
.landing-footer-nav a:hover {
  color: var(--text);
}
.landing-footer-fineprint {
  flex-basis: 100%;
  font-size: 0.8rem;
  color: var(--muted);
  margin: 0;
  line-height: 1.5;
}

/* Mobile — collapse the header nav onto its own row, recenter, tighten
   hero. Single small breakpoint; the desktop layout already flexes
   gracefully down to ~600px wide via the auto-fit grid columns. */
@media (max-width: 600px) {
  .landing-header {
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.9rem 1rem;
  }
  .landing-hero {
    padding: 1.5rem 0.5rem 2.5rem;
  }
  .landing-main {
    padding: 1.5rem 1rem 3rem;
  }
  .landing-footer-inner {
    justify-content: center;
    text-align: center;
  }
}

/* ============================================================================
   v3.27.19 — Decklist Collection Check
   ----------------------------------------------------------------------------
   The decklist-check page renders inside the standard .app-shell, so its
   surface inherits the navy palette + topbar + sidebar from base.html. The
   rules below are page-specific composition only; every value resolves via
   the v3.27.7 / v3.27.15 token layer. Zero new tokens.

   Sections rendered in order on the page:
   * .pagehead — title + tagline (existing component, no per-page styling)
   * .decklist-form — paste textarea + submit + reset
   * .decklist-summary — counts strip across the top of results
   * .decklist-section-* — Have / Partial / Missing / Basics / Unresolved
     each carrying their own border-left accent in the bucket color.
   ============================================================================ */

.decklist-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.decklist-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dim);
}
.decklist-textarea {
  /* Token-reuse styling — same palette as the existing inputs across the
     app; explicit min-height so the textarea is comfortable for a 60-100
     line decklist without the user needing to drag the resize handle. */
  width: 100%;
  min-height: 240px;
  padding: var(--space-3);
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family:
    ui-monospace,
    "SF Mono",
    Menlo,
    Consolas,
    monospace;
  font-size: 0.86rem;
  line-height: 1.55;
  resize: vertical;
}
.decklist-textarea:focus {
  outline: 2px solid var(--brand-gold-soft);
  outline-offset: -1px;
  border-color: var(--brand-gold);
}
.decklist-form-actions {
  display: flex;
  gap: var(--space-3);
  align-items: center;
}
/* Primary submit button — gold pill that matches the v3.27.17 landing
   CTA visual language. Reuses --brand-gold + --brand-near-black for the
   on-gold text contrast. */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 1.1rem;
  background: var(--brand-gold);
  color: var(--brand-near-black);
  border: 0;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: filter 0.15s ease;
}
.btn-primary:hover {
  filter: brightness(1.08);
}

.decklist-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.decklist-summary-stats {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  color: var(--text-dim);
  font-size: 0.9rem;
}
.decklist-summary-stats strong {
  color: var(--text);
  font-weight: 700;
}
.decklist-summary-buckets {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.decklist-bucket-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.75rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.decklist-bucket-have {
  border-color: rgba(46, 125, 70, 0.6);
  color: var(--brand-green);
}
.decklist-bucket-partial {
  border-color: rgba(200, 161, 90, 0.6);
  color: var(--brand-gold);
}
.decklist-bucket-missing {
  border-color: rgba(199, 61, 61, 0.55);
  color: var(--brand-red);
}
.decklist-bucket-basics {
  border-color: var(--border);
  color: var(--text-dim);
}

/* Section header dot — small colored circle that ties each section's
   panel-title to the corresponding chip color in the summary strip. */
.decklist-section-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 0.5rem;
  vertical-align: middle;
}
.decklist-section-dot-have {
  background: var(--brand-green);
}
.decklist-section-dot-partial {
  background: var(--brand-gold);
}
.decklist-section-dot-missing {
  background: var(--brand-red);
}
.decklist-section-dot-basics {
  background: var(--muted);
}

.decklist-section {
  border-left: 3px solid transparent;
}
.decklist-section-have {
  border-left-color: rgba(46, 125, 70, 0.5);
}
.decklist-section-partial {
  border-left-color: rgba(200, 161, 90, 0.5);
}
.decklist-section-missing {
  border-left-color: rgba(199, 61, 61, 0.4);
}
.decklist-section-basics {
  border-left-color: var(--border);
}
.decklist-section-unresolved {
  border-left-color: rgba(199, 61, 61, 0.4);
}

.decklist-card-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.decklist-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.decklist-card-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
}
.decklist-card-name {
  font-weight: 600;
  color: var(--text);
}
.decklist-card-counts {
  font-size: 0.86rem;
  color: var(--text-dim);
}
.decklist-card-counts strong {
  color: var(--brand-gold);
}

/* Missing + Basics bucket cards are single-row (no expand) — use a
   flat two-column row layout instead of the multi-row Have/Partial
   layout above. */
.decklist-card-list-missing .decklist-card,
.decklist-card-list-basics .decklist-card {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
}

.decklist-card-details summary {
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--text-dim);
  padding: 0.15rem 0;
}
.decklist-card-details summary:hover {
  color: var(--text);
}
.decklist-printings {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.decklist-printing {
  /* Each printing-and-location row — set / collector / qty / finish-or-
     proxy badge / location name. The .decklist-printing-locked variant
     (deck or manual-mode location) is dimmer so the eye lands on
     tradeable copies first. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.6rem;
  background: var(--panel);
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
  color: var(--text);
}
.decklist-printing-locked {
  opacity: 0.72;
}
.decklist-printing-set {
  font-weight: 700;
  color: var(--brand-gold);
  letter-spacing: 0.04em;
}
.decklist-printing-num {
  color: var(--text-dim);
}
.decklist-printing-qty {
  color: var(--text);
  font-weight: 600;
}
.decklist-printing-loc {
  margin-left: auto;
  color: var(--text-dim);
}
.decklist-printing-locked-mark {
  font-size: 0.95rem;
}

/* v3.27.19 — count-sorted collection view group headers.
   Three-level grouping per the v3.27.18 spec settlement (name →
   printing → location): the name header is the top tier with the
   total owned count appended; the printing header is the middle tier
   showing the set + collector for the run of rows sharing that
   printing; each row's location is shown by the existing
   inventory_card macro (the bottom tier). No new tokens. */
.collection-group-name {
  margin: 1.25rem 0 0.35rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--border);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.01em;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.collection-group-name-count {
  color: var(--brand-gold);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}
.collection-group-printing {
  margin: 0.6rem 0 0.35rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.collection-group-printing::before {
  content: "▾ ";
  color: var(--muted);
  font-size: 0.75rem;
}
/* Stack each row group as a single-column grid so the printing header
   sits ABOVE its rows rather than next to them; subsequent rows in the
   same printing flow naturally onto the next line. */
.collection-group-rows {
  margin-bottom: 0.5rem;
}

/* v3.27.19 enhancement — per-printing thumbnail in the decklist
   checker. Tiny so 8+ printings fit cleanly inside an open <details>.
   The MTG card aspect ratio is ~63:88, so 28px wide × 39px tall keeps
   the image legible without crowding the row. Anchor wraps the
   image to make the click target the card-detail page. */
.decklist-printing-thumb-wrap {
  flex-shrink: 0;
  line-height: 0;
  text-decoration: none;
}
.decklist-printing-thumb {
  width: 28px;
  height: 39px;
  border-radius: 3px;
  display: block;
  background: var(--panel);
  border: 1px solid var(--border);
  /* MTG card art clips at the very edge; tiny visible border helps
     the image read as a card rather than a generic thumbnail. */
}
.decklist-printing-thumb-empty {
  /* Same footprint as the loaded image so rows without an image_url
     don't reflow shorter than rows with one. */
  border-style: dashed;
}

/* ============================================================================
   === Chronicle page (v3.28.2) ===
   ----------------------------------------------------------------------------
   Two-column page: Chronicle Entry on the left (1.55fr), Archive of Issues
   on the right (1fr). Editorial register throughout — Newsreader italic
   headings, antique-gold hairline rules, brass-soft current-entry highlight.
   Translated from the Folio design package's chrCss block in page_chronicle.jsx.
   Token mapping: --cream → --text, --cream-2 → --text-dim, --cream-3 → --muted,
   --brass-2 → --accent-2, --brass-soft → --brand-gold-soft. --border-soft is
   a new production token (v3.28.2) for the inner editorial dividers.
   ============================================================================ */
.chronicle-grid {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: 22px;
  align-items: start;
}
@media (max-width: 980px) {
  .chronicle-grid {
    grid-template-columns: 1fr;
  }
}

.chronicle-entry {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 26px 32px 28px;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.chronicle-entry::before {
  content: "";
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  bottom: 12px;
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  pointer-events: none;
}
.chronicle-entry > * {
  position: relative;
  z-index: 1;
}

.chronicle-mast {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.chronicle-stamp {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 22px;
  color: var(--text);
  font-weight: 500;
}
.chronicle-stamp b {
  color: var(--accent-2);
  font-style: italic;
  font-weight: 500;
}
.chronicle-raw {
  margin-top: 6px;
  display: flex;
  gap: 14px;
  align-items: baseline;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
}
.chronicle-raw b {
  color: var(--text-dim);
  font-weight: 600;
}
.chronicle-raw .chronicle-status {
  color: var(--brand-green);
}

.chronicle-title {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 36px;
  line-height: 1.05;
  font-weight: 400;
  color: var(--text);
  letter-spacing: -0.012em;
  margin: 0;
}
.chronicle-title b {
  font-style: normal;
  font-weight: 500;
}

.chronicle-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.chronicle-section h3 {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 18px;
  color: var(--text);
  font-weight: 500;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.chronicle-section h3::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-soft);
}
.chronicle-section h3 .ct {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-style: normal;
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.12em;
}
.chronicle-section ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.chronicle-section li {
  font-family: "Newsreader", Georgia, serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-dim);
  padding-left: 22px;
  position: relative;
}
.chronicle-section li::before {
  content: "·";
  position: absolute;
  left: 6px;
  top: 0;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 18px;
  color: var(--accent-2);
  line-height: 1.4;
}

.chronicle-note {
  margin-top: 4px;
  padding: 16px 18px;
  background: rgba(0, 0, 0, 0.18);
  border-left: 2px solid var(--accent-2);
  border-radius: 0 4px 4px 0;
}
.chronicle-note-label {
  font-size: 9.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--accent-2);
  font-weight: 700;
  margin-bottom: 6px;
}
.chronicle-note p {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 0;
}

/* Not-found state — rendered for /chronicle/v{unknown-version}. */
.chronicle-notfound {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 26px 32px;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 16px;
  color: var(--muted);
  line-height: 1.55;
}
.chronicle-notfound b {
  color: var(--text);
  font-style: normal;
  font-weight: 500;
}
.chronicle-notfound a {
  color: var(--accent-2);
  text-decoration: underline;
  text-decoration-color: var(--border);
}

/* === Archive of Issues === */
.chronicle-archive {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.chronicle-archive-head {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.chronicle-archive-head .crumb {
  font-size: 9px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--accent-2);
  font-weight: 700;
}
.chronicle-archive-head h2 {
  margin: 6px 0 0;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 24px;
  color: var(--text);
  font-weight: 500;
}
.chronicle-archive-head h2 b {
  font-style: normal;
  font-weight: 500;
}

.chronicle-folio {
  display: flex;
  flex-direction: column;
}
.chronicle-folio-h {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 17px;
  color: var(--accent-2);
  font-weight: 500;
  margin-bottom: 6px;
}
.chronicle-issue {
  padding: 6px 0;
}
.chronicle-issue-h {
  font-family: "Newsreader", Georgia, serif;
  font-size: 13px;
  color: var(--text-dim);
  font-weight: 500;
  letter-spacing: 0.04em;
  padding-bottom: 4px;
  border-bottom: 1px dotted var(--border-soft);
  display: flex;
  align-items: center;
  gap: 8px;
}
.chronicle-issue-h .raw {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 9.5px;
  color: var(--muted);
  letter-spacing: 0.04em;
  margin-left: auto;
  text-transform: uppercase;
}

.chronicle-entries {
  display: flex;
  flex-direction: column;
  padding-left: 2px;
}
/* Each archive row is a link in production (the design package had them
   non-clickable as a static mockup). The hover lift signals affordance. */
.chronicle-entry-row {
  display: grid;
  grid-template-columns: 60px 1fr auto;
  gap: 10px;
  align-items: baseline;
  padding: 5px 0;
  border-bottom: 1px dotted var(--border-soft);
  text-decoration: none;
  transition: background 0.12s ease;
}
.chronicle-entry-row:last-child {
  border-bottom: 0;
}
.chronicle-entry-row:hover {
  background: rgba(212, 168, 95, 0.06);
}
.chronicle-entry-row.current {
  background: var(--brand-gold-soft);
  margin: 0 -8px;
  padding: 5px 8px;
  border-radius: 4px;
  border-bottom: 0;
  position: relative;
}
.chronicle-entry-row.current::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  width: 3px;
  height: 18px;
  transform: translateY(-50%);
  background: var(--accent-2);
  border-radius: 0 2px 2px 0;
}
.chronicle-entry-row .ent {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
}
.chronicle-entry-row .nm {
  font-family: "Newsreader", Georgia, serif;
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chronicle-entry-row.current .nm {
  color: var(--text);
}
.chronicle-entry-row .dt {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.04em;
}

/* Topbar version pill — now a link to /chronicle (v3.28.2). */
.version-pill {
  text-decoration: none;
}
.version-pill:hover {
  background: var(--panel-2);
  color: var(--text);
}

/* ============================================================================
   === Auth two-column shell (v3.28.4) ===
   ----------------------------------------------------------------------------
   Standalone layout for /login, /register, /forgot-password, /reset-password.
   The four templates extend ``_auth_layout.html`` (not ``base.html``) — auth
   pages have no app shell (sidebar, topbar) because the user is logged out.
   Two-column at desktop: editorial brand pane on the left, form pane on the
   right. Collapses to single column at ≤768px (form first, brand pane
   above; brand-pane hero hidden at narrow widths so the form stays primary).
   Translated from the Folio design package's page_auth.jsx ``.au-*`` classes;
   token-mapped --cream → --text, --cream-2 → --text-dim, --cream-3 → --muted,
   --brass-2 → --accent-2, --brass-soft → --brand-gold-soft. Form input
   styling deliberately NOT adopted from the design (the design uses italic
   Newsreader inputs; production keeps sans inputs for autofill / password /
   mono-space-paste affordances — same utility-vs-editorial split v3.28.3
   used for the eyebrow scope).
   ============================================================================ */

/* Outer shell — fixed-aspect on desktop via min-height: 100vh + grid. */
.auth-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--bg);
  color: var(--text);
  position: relative;
  overflow: hidden;
}
/* Mirrors the body::before fractal-noise texture from v3.28.1 / v3.27.15.
   The auth shell doesn't render base.html so it has to opt in to the
   ambient texture itself. */
.auth-shell::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.025'/%3E%3C/svg%3E");
}

/* === Left brand pane — crest + wordmark + hero + footer === */
.auth-brand {
  position: relative;
  z-index: 1;
  background: linear-gradient(160deg, var(--panel) 0%, var(--bg) 100%);
  border-right: 1px solid var(--border);
  padding: 4.5rem 5rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
}
.auth-brand-top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.875rem;
  text-align: center;
}
.auth-brand-crest {
  width: 140px;
  height: 140px;
  filter: drop-shadow(0 16px 36px rgba(0, 0, 0, 0.55));
}
.auth-brand-wordmark {
  height: 22px;
  width: auto;
  display: block;
  margin: 0 auto;
}
.auth-brand-tagline {
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--accent-2);
  font-weight: 500;
  font-style: italic;
  margin-top: 0.375rem;
}

/* Editorial hero — stamp + Newsreader italic H1 + Newsreader body. */
.auth-brand-hero {
  margin-top: 5rem;
}
.auth-brand-stamp {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 9.5px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--accent-2);
  font-weight: 700;
}
.auth-brand-stamp .ln {
  width: 32px;
  height: 1px;
  background: var(--accent-2);
  flex: 0 0 auto;
}
.auth-brand-hero h1 {
  margin: 1rem 0 0;
  font-family: "Newsreader", Georgia, serif;
  font-weight: 400;
  font-style: italic;
  font-size: clamp(2rem, 3.6vw, 3.125rem);
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--text);
}
.auth-brand-hero h1 b {
  font-style: normal;
  font-weight: 500;
}
.auth-brand-hero p {
  margin: 1.125rem 0 0;
  font-family: "Newsreader", Georgia, serif;
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 29rem;
}
.auth-brand-hero p b {
  font-weight: 500;
  color: var(--text);
}

/* Footer — version (rendered via | folio filter) + tagline + posture. */
.auth-brand-footer {
  display: flex;
  gap: 1.25rem;
  align-items: baseline;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.75rem;
  color: var(--muted);
  flex-wrap: wrap;
}
.auth-brand-footer .v {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-style: normal;
  color: var(--text-dim);
}

/* === Right form pane === */
.auth-form-wrap {
  position: relative;
  z-index: 1;
  padding: 4.5rem 5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.auth-form {
  max-width: 28rem;
  width: 100%;
  margin: 0 auto;
}
.auth-form-head {
  margin-bottom: 1.75rem;
}
/* Form-pane H1 — Newsreader italic large display, matches package's .au-form .head h2.
   Per v3.28.3 .pagehead-title's pattern but slightly larger for the auth surface. */
.auth-form-head h1 {
  margin: 0.625rem 0 0.25rem;
  font-family: "Newsreader", Georgia, serif;
  font-weight: 400;
  font-style: italic;
  font-size: 2rem;
  line-height: 1.05;
  letter-spacing: -0.012em;
  color: var(--text);
}
.auth-form-head h1 b {
  font-style: normal;
  font-weight: 500;
}
.auth-form-head .auth-form-sub {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.875rem;
  color: var(--muted);
  margin-top: 0.5rem;
}
.auth-form-head .auth-form-sub a {
  color: var(--accent-2);
}

/* Existing .stack-form / form inputs / .panel styling is reused inside
   .auth-form. The form pane is intentionally NOT wrapped in a .panel —
   the brand pane provides the visual containment, and the form floats
   directly on the warm-charcoal ground. */
.auth-form .stack-form button[type="submit"] {
  margin-top: 0.5rem;
}

/* Error / state messages inside the form pane carry the editorial register
   but stay color-coded. */
.auth-form .auth-error {
  color: var(--accent-2);
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  margin: 0 0 1rem;
}
.auth-form .auth-success {
  color: var(--brand-green);
  font-family: "Newsreader", Georgia, serif;
  font-weight: 500;
  font-style: italic;
  font-size: 0.95rem;
  margin: 0 0 1rem;
}
.auth-form .auth-notice {
  color: var(--muted);
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.55;
  margin: 0 0 1rem;
}
.auth-form .auth-foot-links {
  margin-top: 1.5rem;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.875rem;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.auth-form .auth-foot-links a {
  color: var(--accent-2);
}

/* === Responsive collapse at ≤768px ===
   Two-column → single-column; brand pane condenses to crest + wordmark
   only (the editorial hero hides — the form must be the focus on phones).
   Form pane gets reduced padding so the input width remains usable. */
@media (max-width: 768px) {
  .auth-shell {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    min-height: 100vh;
  }
  .auth-brand {
    border-right: none;
    border-bottom: 1px solid var(--border);
    padding: 2rem 1.5rem 1.5rem;
    gap: 1rem;
  }
  .auth-brand-top {
    flex-direction: row;
    gap: 0.875rem;
    text-align: left;
    justify-content: center;
  }
  .auth-brand-crest {
    width: 56px;
    height: 56px;
  }
  .auth-brand-wordmark {
    margin: 0;
  }
  .auth-brand-tagline {
    text-align: left;
    margin-top: 0.25rem;
  }
  /* Hide the editorial hero + footer at phone widths — form first. */
  .auth-brand-hero,
  .auth-brand-footer {
    display: none;
  }
  .auth-form-wrap {
    padding: 2rem 1.5rem 3rem;
  }
  .auth-form-head h1 {
    font-size: 1.625rem;
  }
}

/* ============================================================================
   === Folio dashboard (v3.28.5) ===
   ----------------------------------------------------------------------------
   The Folio masthead + search hero + nine `§`-numbered editorial panels.
   Replaces the v3.27.8 "Quick Actions launcher" + v3.27.10/v3.27.11 three-tile
   shape with the design package's full dashboard. Translated from
   page_dashboard.jsx's dashCss block; token-mapped per the cluster pattern
   (--cream → --text, --cream-2 → --text-dim, --cream-3 → --muted,
   --brass-2 → --accent-2, --brass-soft → --brand-gold-soft, --sage →
   --brand-green, --oxblood → --danger). The Holdings 30-day chart + delta
   are deferred — they need a price-history schema; v3.28.5 renders a clean
   "available once price history lands" placeholder instead.

   Layout: masthead (2-col: text+stat-block) → search hero (1-row) →
   main grid (2-col: 1.5fr left / 1fr right) → lower row (4 equal cols).
   Responsive collapse at ≤980px (main grid → 1 col) and ≤768px (lower
   row → 1 col, masthead text+stat-block stack).
   ============================================================================ */

/* === MASTHEAD ===
   v3.28.5 cleanup pass: pumped masthead and stat-block typography scale.
   Originally the masthead H1 was clamp(1.5rem, 3vw, 2rem) ≈ the .pagehead-title
   size, but the dashboard is the editorial centerpiece — needs to read as
   a magazine cover, not a generic page heading. */
.dashboard-masthead {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}
.dashboard-masthead-title {
  /* Newsreader italic display, large. Matches the design's .dsh-masthead h1
     font-size 26px at desktop, scales up at wider viewports. */
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(2rem, 4vw, 2.8rem);
  line-height: 1.05;
  letter-spacing: -0.012em;
  color: var(--text);
  margin: 0.4rem 0 0;
}
.dashboard-masthead-title b {
  font-style: normal;
  font-weight: 500;
}
.dashboard-masthead-insight {
  margin-top: 0.5rem;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1.05rem;
  color: var(--text-dim);
  line-height: 1.45;
}
.dashboard-masthead-insight::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--accent-2);
  flex: 0 0 auto;
}
.dashboard-masthead-insight b {
  color: var(--text);
  font-style: italic;
  font-weight: 500;
}
.dashboard-masthead-insight i {
  color: var(--accent-2);
  font-style: italic;
}
.dashboard-masthead-right {
  display: flex;
  gap: 1.75rem;
}
.dashboard-masthead-stat {
  text-align: right;
}
.dashboard-masthead-stat .lbl {
  font-size: 0.62rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.dashboard-masthead-stat .v {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1.5rem;
  color: var(--text);
  font-weight: 500;
  margin-top: 0.25rem;
  line-height: 1;
}
.dashboard-masthead-stat .v.brass {
  color: var(--accent-2);
}

/* === SEARCH HERO === */
.dashboard-search-hero {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.75rem;
  align-items: center;
  margin: 1rem 0 1.25rem;
}
.dashboard-search-input {
  padding: 0.8rem 1.1rem;
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1rem;
  width: 100%;
}
.dashboard-search-input::placeholder {
  color: var(--muted);
  font-style: italic;
}
.dashboard-search-chips {
  display: flex;
  gap: 0.375rem;
  flex-wrap: wrap;
}
.dashboard-search-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.45rem 0.75rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-dim);
  font-family: "Montserrat", sans-serif;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: background 0.12s ease, color 0.12s ease;
}
.dashboard-search-chip:hover {
  background: var(--panel-2);
  color: var(--text);
}

/* === MAIN GRID ===
   v3.28.5 cleanup: bigger gaps + bigger panel padding to match the design's
   breathing-room aesthetic. The page felt cramped at the previous values. */
.dashboard-grid {
  display: grid;
  grid-template-columns: 1.55fr 1fr;
  gap: 1.4rem;
  align-items: start;
  margin-bottom: 1.4rem;
}
.dashboard-col {
  display: grid;
  gap: 1.125rem;
  align-content: start;
}

/* === PANEL — editorial header ("§ N · Title") + content + optional aux ===
   v3.28.5 cleanup: § header pumped from 0.6rem → 0.72rem (uppercase
   letter-spaced — still small, but now readable). Panel padding bumped for
   breathing room. Aux uses .82rem Newsreader italic so it carries actual
   visual weight beside the § header. */
.dashboard-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.4rem;
  display: flex;
  flex-direction: column;
}
.dashboard-panel-h {
  font-family: "Montserrat", sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin: 0 0 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.dashboard-panel-h::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-soft);
}
.dashboard-panel-h .aux {
  flex: 0 0 auto;
  text-transform: none;
  letter-spacing: 0.02em;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.82rem;
  color: var(--muted);
  font-weight: 400;
}
.dashboard-panel-urgent {
  background: linear-gradient(180deg, rgba(138, 46, 34, 0.10), var(--panel));
  border-color: rgba(138, 46, 34, 0.45);
}
/* v3.28.5 cleanup: empty states get a "deliberate pause" treatment rather
   than reading as placeholder text. Italic Newsreader at body size, a faint
   sage-tone dot marker that signals "intentional / nothing wrong here". */
.dashboard-empty {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--muted);
  margin: 0;
  padding: 0.5rem 0;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.dashboard-empty::before {
  content: "·";
  font-size: 1.25rem;
  color: var(--brand-green);
  line-height: 1;
  flex: 0 0 auto;
}

/* === § I TODAY'S BRIEF ===
   v3.28.5 cleanup: typography pumped one step (0.875rem → 0.95rem); first
   item gets a .major modifier with larger italic-display treatment per the
   design's `.dsh-brief-item.major .lede { font-size: 15px; font-style: italic; }`. */
.dashboard-brief {
  list-style: none;
  padding: 0;
  margin: 0;
}
.dashboard-brief-item {
  display: grid;
  grid-template-columns: 1.75rem 1fr;
  gap: 0.625rem;
  padding: 0.55rem 0;
  border-bottom: 1px dotted var(--border-soft);
}
.dashboard-brief-item:last-child {
  border-bottom: 0;
}
.dashboard-brief-item .num {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--accent-2);
}
.dashboard-brief-item .lede {
  font-family: "Newsreader", Georgia, serif;
  font-size: 0.95rem;
  color: var(--text-dim);
  line-height: 1.45;
}
.dashboard-brief-item.major .lede {
  font-style: italic;
  font-size: 1.1rem;
  color: var(--text);
}
.dashboard-brief-item .lede b {
  color: var(--text);
  font-weight: 500;
}
.dashboard-brief-item.major .lede b {
  font-style: italic;
}
.dashboard-brief-item .lede i {
  color: var(--accent-2);
  font-style: italic;
}
.dashboard-brief-item .detail {
  grid-column: 2;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 0.3rem;
  line-height: 1.45;
}

/* === § II HOLDINGS ===
   v3.28.5 cleanup: typography pumped one step; deferred-chart placeholder
   gets more breathing room. */
.dashboard-holdings {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
}
.dashboard-holdings-lede {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1rem;
  color: var(--text-dim);
  line-height: 1.5;
}
.dashboard-holdings-lede b {
  color: var(--text);
  font-weight: 500;
}
.dashboard-holdings-deferred {
  padding: 0.875rem 1rem;
  background: rgba(0, 0, 0, 0.18);
  border-left: 2px solid var(--border-soft);
  border-radius: 0 4px 4px 0;
}
.dashboard-holdings-deferred-note {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.55;
}

/* === § III CHRONICLE === */
.dashboard-chron {
  list-style: none;
  padding: 0;
  margin: 0;
}
.dashboard-chron-row {
  display: grid;
  grid-template-columns: 1.75rem 5rem 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.45rem 0;
  border-bottom: 1px dotted var(--border-soft);
  font-family: "Newsreader", Georgia, serif;
  font-size: 0.875rem;
}
.dashboard-chron-row:last-child {
  border-bottom: 0;
}
.dashboard-chron-row .ix {
  font-style: italic;
  color: var(--accent-2);
}
.dashboard-chron-row .verb {
  font-style: italic;
  color: var(--text-dim);
}
.dashboard-chron-row .verb.plc {
  color: var(--brand-green);
}
.dashboard-chron-row .verb.del {
  color: var(--danger);
}
.dashboard-chron-row .nm {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dashboard-chron-row .set {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.66rem;
  color: var(--muted);
  letter-spacing: 0.08em;
}

/* === § IV PRIORITY QUEUE ===
   v3.28.5 cleanup: number pumped 2.25rem → 3.5rem so the queue size is
   the panel's visual anchor (matches the design's "28" prominence).
   CTA button gets more padding for action-affordance weight. */
.dashboard-pq {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.dashboard-pq-headline {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.dashboard-pq-headline .num {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 3.5rem;
  font-weight: 500;
  color: var(--text);
  line-height: 1;
}
.dashboard-pq-headline .meta {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--muted);
}
.dashboard-pq-cta {
  align-self: flex-start;
  padding: 0.75rem 1.25rem;
  background: var(--accent-2);
  color: var(--bg);
  border: 1px solid var(--accent-2);
  border-radius: var(--radius-sm);
  font-family: "Montserrat", sans-serif;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
}
.dashboard-pq-cta:hover {
  filter: brightness(1.05);
}
/* v3.28.5 cleanup: celebratory "queue clear" empty state. Sage-toned
   check mark + italic Newsreader copy reads as "all good" rather than
   "no data" — addresses the empty-space concern on the Priority Queue
   panel without growing scope. */
.dashboard-pq-clear {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 0 0.25rem;
}
.dashboard-pq-clear-mark {
  font-size: 2rem;
  color: var(--brand-green);
  font-weight: 500;
  line-height: 1;
  flex: 0 0 auto;
}
.dashboard-pq-clear-text {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1.15rem;
  color: var(--text);
  margin: 0;
  line-height: 1.25;
}
.dashboard-pq-clear-sub {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0.15rem 0 0;
}

/* === § V SPOTLIGHT ===
   v3.28.5 cleanup: card art bumped 86 → 110px (closer to the design's
   visual prominence); name bigger; stats row tightened. */
.dashboard-spot {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1rem;
  align-items: start;
  text-decoration: none;
  color: inherit;
}
.dashboard-spot-art {
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.5));
  border-radius: 5px;
  overflow: hidden;
}
.dashboard-spot-art img {
  width: 110px;
  height: auto;
  display: block;
  border-radius: 5px;
}
.dashboard-spot-art-placeholder {
  width: 110px;
  height: 153px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 5px;
}
.dashboard-spot-copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.dashboard-spot-name {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.25rem;
  color: var(--text);
  margin: 0 0 0.3rem;
  line-height: 1.15;
}
.dashboard-spot-set {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.08em;
}
.dashboard-spot-stats {
  display: flex;
  gap: 1rem;
  margin-top: 0.625rem;
  padding-top: 0.625rem;
  border-top: 1px dotted var(--border-soft);
}
.dashboard-spot-stats .l {
  display: block;
  font-size: 0.58rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.dashboard-spot-stats .v {
  display: block;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--text);
  margin-top: 0.2rem;
}
/* v3.28.5 cleanup: Spotlight Identity uses the canonical .mana-pip SVG
   (Scryfall card-symbols) via the mana_pips() macro. Sized down from the
   default 24px to 18px so it fits the Spotlight stats row's compact scale.
   Same approach as Deck Performance below — override .mana-pip dimensions
   in each dashboard panel's scope rather than introducing yet another
   custom-pip variant. */
.dashboard-spot-pips {
  display: inline-flex;
  gap: 2px;
  align-items: center;
  margin-top: 0.15rem;
}
.dashboard-spot-pips .mana-pip {
  width: 18px;
  height: 18px;
  margin-left: 0;
}

/* === LOWER ROW (4 columns) ===
   v3.28.5 cleanup: `align-items: stretch` so all four panels match the
   tallest panel's height. Visual rhythm — empty space at the bottom of
   short panels reads as deliberate breathing room rather than truncation. */
.dashboard-lower {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.4rem;
  align-items: stretch;
}

/* === § VI DECK PERFORMANCE ===
   v3.28.5 cleanup: typography pumped; per-row mana-identity pips column
   added (matches the design's pips-before-name pattern). */
.dashboard-decks {
  list-style: none;
  padding: 0;
  margin: 0;
}
.dashboard-decks-row {
  border-bottom: 1px dotted var(--border-soft);
}
.dashboard-decks-row:last-child {
  border-bottom: 0;
}
.dashboard-decks-row a {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: 0.5rem;
  align-items: center;
  padding: 0.5rem 0;
  text-decoration: none;
}
.dashboard-decks-row .pips {
  display: inline-flex;
  gap: 1px;
  flex: 0 0 auto;
  align-items: center;
}
/* v3.28.5 cleanup: Deck Performance pips use the canonical .mana-pip SVG
   (Scryfall card-symbols) via the mana_pips() macro. Sized down from the
   default 24px to 16px to fit the compact deck-row scale. */
.dashboard-decks-row .mana-pip {
  width: 16px;
  height: 16px;
  margin-left: 0;
}
.dashboard-decks-row .nm {
  font-family: "Newsreader", Georgia, serif;
  font-size: 0.92rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dashboard-decks-row .n {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.7rem;
  color: var(--muted);
}
.dashboard-decks-row .w {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.92rem;
  font-weight: 500;
}
.dashboard-decks-row .w.up {
  color: var(--brand-green);
}
.dashboard-decks-row .w.dn {
  color: var(--muted);
}

/* === § VII COLOR IDENTITY ===
   v3.28.5 cleanup: bar height pumped 5px → 8px; pip slightly larger;
   row padding bumped for breathing room. */
.dashboard-colid {
  list-style: none;
  padding: 0;
  margin: 0;
}
.dashboard-colid-row {
  display: grid;
  grid-template-columns: 1.5rem 1fr auto;
  gap: 0.625rem;
  align-items: center;
  padding: 0.4rem 0;
}
.dashboard-colid-row .pip {
  width: 1.35rem;
  height: 1.35rem;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 0.72rem;
}
.dashboard-colid-w .pip { background: #F1E6C5; color: #3a2f1c; }
.dashboard-colid-u .pip { background: #3C6BB0; color: #E6EEF8; }
.dashboard-colid-b .pip { background: #23201E; color: #D8D2C6; }
.dashboard-colid-r .pip { background: #B23A2C; color: #fff; }
.dashboard-colid-g .pip { background: #2F6E4A; color: #fff; }
.dashboard-colid-c .pip { background: #8C8579; color: #fff; }
.dashboard-colid-m .pip {
  background: linear-gradient(135deg, #C9A55E, #8B6914);
  color: #1a1a1a;
}
.dashboard-colid-row .bar {
  height: 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.3);
  overflow: hidden;
}
.dashboard-colid-row .bar .fill {
  height: 100%;
  background: var(--accent-2);
}
.dashboard-colid-w .bar .fill { background: #F1E6C5; }
.dashboard-colid-u .bar .fill { background: #3C6BB0; }
.dashboard-colid-b .bar .fill { background: #6a6058; }
.dashboard-colid-r .bar .fill { background: #B23A2C; }
.dashboard-colid-g .bar .fill { background: #2F6E4A; }
.dashboard-colid-c .bar .fill { background: #8C8579; }
.dashboard-colid-m .bar .fill { background: #C9A55E; }
.dashboard-colid-row .val {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.78rem;
  color: var(--text);
}
.dashboard-colid-row .val small {
  font-size: 0.62rem;
  color: var(--muted);
  letter-spacing: 0.04em;
}

/* === § VIII COLLECTION STATISTICS ===
   v3.28.5 cleanup: typography pumped one step; rows breathe. */
.dashboard-stats {
  list-style: none;
  padding: 0;
  margin: 0;
}
.dashboard-stats li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0.45rem 0;
  border-bottom: 1px dotted var(--border-soft);
  font-size: 0.9rem;
}
.dashboard-stats li:last-child {
  border-bottom: 0;
}
.dashboard-stats .l {
  color: var(--muted);
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
}
.dashboard-stats .v {
  color: var(--text);
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.85rem;
  font-weight: 600;
}

/* === § IX QUICK ACTIONS ===
   v3.28.5 cleanup: per-action card padding and text both pumped; hint
   reads as italic Newsreader body rather than caption. */
.dashboard-actions {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.dashboard-actions a {
  display: flex;
  flex-direction: column;
  padding: 0.7rem 0.875rem;
  background: rgba(0, 0, 0, 0.15);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease;
  gap: 0.15rem;
}
.dashboard-actions a:hover {
  background: var(--brand-gold-soft);
  border-color: var(--accent-2);
}
.dashboard-actions .nm {
  font-family: "Newsreader", Georgia, serif;
  font-size: 0.98rem;
  font-weight: 500;
  color: var(--text);
}
.dashboard-actions .hint {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.78rem;
  color: var(--muted);
}

/* === Responsive collapse === */
@media (max-width: 980px) {
  .dashboard-grid {
    grid-template-columns: 1fr;
  }
  .dashboard-lower {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 768px) {
  .dashboard-masthead {
    grid-template-columns: 1fr;
    align-items: start;
  }
  .dashboard-masthead-right {
    gap: 1rem;
    flex-wrap: wrap;
  }
  .dashboard-masthead-stat {
    text-align: left;
  }
  .dashboard-search-hero {
    grid-template-columns: 1fr;
  }
  .dashboard-lower {
    grid-template-columns: 1fr;
  }
}

/* ===================================================================
   v3.28.6 — Folio Locations table polish
   ===================================================================
   Page-fork decision: table stays. The data is hierarchical (cabinet ›
   drawer › slot) and grows numerous; a flat card grid loses both. So
   the table inherits the Folio editorial register without restructuring.
   Adds: per-location note as a sub-line beneath the name; capacity
   meter as an inline cell (bar + pc); last-touched as a small column.
   Token-driven; consumes the v3.28.1 Folio palette. Zero parallel
   tokens. =================================================== */

/* Note sub-line beneath the location name. Newsreader italic; muted
   register so the name still leads. */
.locations-note {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12.5px;
  color: var(--text-dim);
  margin-top: 4px;
  line-height: 1.35;
  max-width: 36ch;
}

.locations-name-link {
  font-weight: 500;
}

/* Inline capacity meter — narrow bar + percent + raw quantity. Brass
   gradient by default; oxblood when over 95% (the design package's
   `attn` treatment). */
.locations-meter {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 120px;
}
.locations-meter-bar {
  flex: 1 1 70px;
  height: 6px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
  min-width: 50px;
}
.locations-meter-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--brand-gold), var(--brand-gold) 60%, #e0c486);
  transition: width 0.2s ease-out;
}
.locations-meter-bar-attn {
  border-color: rgba(138, 46, 34, 0.55);
}
.locations-meter-bar-attn .locations-meter-fill {
  background: linear-gradient(90deg, #8a2e22, #c44a3a);
}
.locations-meter-pc {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--text);
  white-space: nowrap;
  font-weight: 500;
}
.locations-meter-sep {
  color: var(--muted);
}
.locations-count-plain {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 13px;
  color: var(--text);
}

/* Row highlight when capacity ≥95% — subtle warning tint matching the
   v3.28.1 oxblood register without overpowering the row content. */
.locations-row-attn {
  background: rgba(138, 46, 34, 0.06);
}

/* Last-touched cell — small mono date in the editorial register. The
   v3.28.3 editorial_date filter renders the "21 May 2026" form. */
.locations-last-touched {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--text-dim);
  white-space: nowrap;
}
.locations-last-touched-empty {
  color: var(--muted);
}

/* Locations create form — two-row grid so name + type + mode + parent +
   capacity sit on row 1; note (wide) + Create button on row 2. The
   original single-row .filter-row didn't have room for two new fields
   without forcing horizontal scroll on laptop widths. */
.locations-create-form {
  margin-top: 0.5rem;
  gap: 0.75rem;
}
.locations-create-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: flex-end;
}
.locations-create-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  flex: 1 1 130px;
  min-width: 130px;
}
.locations-create-field label {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.locations-create-field-name {
  flex: 2 1 220px;
}
.locations-create-field-capacity {
  flex: 0 0 130px;
}
.locations-create-field-note {
  flex: 1 1 100%;
}
.locations-create-hint,
.locations-edit-hint {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  margin-left: 0.25rem;
}
.locations-create-submit {
  display: flex;
  align-items: flex-end;
  flex: 0 0 auto;
}

/* Responsive — the meter cell shrinks gracefully at narrow widths; on
   phones the cells stack via the existing .stacking-table rules
   (data-label attribute drives the mobile-card layout). */
@media (max-width: 768px) {
  .locations-meter {
    min-width: 0;
  }
  .locations-meter-bar {
    flex: 1 1 60px;
    min-width: 40px;
  }
  .locations-note {
    max-width: none;
  }
  .locations-create-field {
    flex: 1 1 100%;
  }
  .locations-create-field-capacity {
    flex: 1 1 100%;
  }
}

/* ===================================================================
   v3.28.7 — Folio smaller-page polish (Decks + Pending + Card Detail +
   Deck Detail). Token-driven; consumes the v3.28.1 Folio palette. Zero
   parallel tokens. Built responsive for the v3.28.10 verify pass.
   =================================================================== */

/* ---------- Decks editorial rows (full table → editorial-row rewrite) ---------- */

/* Featured deck — gold gradient frame, larger typography. */
.decks-featured {
  background: linear-gradient(180deg, var(--brand-gold-soft), var(--panel));
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 1.5rem 1.75rem;
  position: relative;
  margin-top: 1.25rem;
}
.decks-featured-tag {
  position: absolute;
  top: -0.6rem;
  left: 1.5rem;
  background: var(--bg);
  color: var(--brand-gold);
  padding: 0.15rem 0.7rem;
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 700;
}
.decks-featured-grid {
  display: grid;
  grid-template-columns: auto 1.2fr 1fr;
  gap: 1.5rem;
  align-items: stretch;
}
.decks-featured-art {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 90px;
  padding: 0.5rem;
}
.decks-featured-art-pip img.mana-pip {
  width: 36px;
  height: 36px;
  margin-left: 0;
}
.decks-featured-copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.decks-featured-name {
  margin: 0;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.15;
  color: var(--text);
  letter-spacing: -0.005em;
}
.decks-featured-name a {
  color: inherit;
  text-decoration: none;
}
.decks-featured-name a:hover {
  color: var(--brand-gold);
}
.decks-featured-meta {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--text-dim);
  margin: 0.4rem 0 0.6rem;
}
.decks-featured-pips {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.decks-featured-pips img.mana-pip {
  width: 16px;
  height: 16px;
  margin-left: 0;
}
.decks-featured-fmt {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--text-dim);
}
.decks-featured-cards {
  color: var(--muted);
}
.decks-featured-blurb {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-dim);
  border-left: 2px solid var(--brand-gold);
  padding-left: 0.75rem;
  margin: 0 0 0.6rem;
  max-width: 48ch;
}
.decks-featured-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: auto;
}
.decks-featured-tag-chip {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 600;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.2);
}
.decks-featured-record {
  display: flex;
  flex-direction: column;
  padding-left: 1.25rem;
  border-left: 1px solid var(--border);
  align-items: stretch;
  min-width: 130px;
}
.decks-featured-record-label {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.decks-featured-record-v {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 2rem;
  color: var(--text);
  font-weight: 500;
  line-height: 1;
  margin-top: 0.25rem;
}
.decks-featured-record-sep {
  color: var(--brand-gold);
  font-size: 1.2rem;
  margin: 0 0.1rem;
}
.decks-featured-record-pct {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12px;
  font-weight: 600;
  margin-top: 0.25rem;
}
.decks-featured-record-pct.up {
  color: var(--brand-green);
}
.decks-featured-record-pct.dn {
  color: var(--danger);
}
.decks-featured-record-last {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  margin-top: 0.5rem;
}
.decks-featured-open {
  margin-top: 0.75rem;
  padding: 0.5rem 0.9rem;
  background: var(--brand-gold);
  color: var(--bg);
  border: 1px solid var(--brand-gold);
  border-radius: 4px;
  font-family: "Montserrat", sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  display: inline-block;
}
.decks-featured-open:hover {
  filter: brightness(1.1);
}
.decks-featured-controls {
  margin-top: 0.6rem;
}

/* Compact editorial rows for non-featured decks. */
.decks-rows {
  margin-top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.decks-row {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.25rem;
  display: grid;
  grid-template-columns: 1.5fr 1fr 0.8fr auto;
  gap: 1.25rem;
  align-items: center;
}
.decks-row-attn {
  border-color: rgba(138, 46, 34, 0.35);
}
.decks-row-copy {
  min-width: 0;
}
.decks-row-name {
  margin: 0;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
}
.decks-row-name a {
  color: inherit;
  text-decoration: none;
}
.decks-row-name a:hover {
  color: var(--brand-gold);
}
.decks-row-meta {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10.5px;
  color: var(--text-dim);
  margin-top: 0.2rem;
}
.decks-row-pips {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.decks-row-pips img.mana-pip {
  width: 14px;
  height: 14px;
  margin-left: 0;
}
.decks-row-fmt {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11.5px;
  color: var(--text-dim);
}
.decks-row-cards {
  color: var(--muted);
}
.decks-row-blurb {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--muted);
  margin: 0.35rem 0 0;
  line-height: 1.4;
  max-width: 50ch;
}
.decks-row-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}
.decks-row-stat-l {
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.decks-row-stat-v {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 13.5px;
  color: var(--text);
  margin-top: 0.15rem;
  font-weight: 500;
}
.decks-row-stat-empty {
  color: var(--muted);
}
.decks-row-record {
  text-align: right;
  min-width: 90px;
}
.decks-row-record-v {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1.25rem;
  color: var(--text);
  font-weight: 500;
}
.decks-row-record-sep {
  color: var(--brand-gold);
  font-size: 0.9rem;
  margin: 0 0.1rem;
}
.decks-row-record-pct {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  font-weight: 600;
  margin-top: 0.15rem;
}
.decks-row-record-pct.up {
  color: var(--brand-green);
}
.decks-row-record-pct.dn {
  color: var(--danger);
}
.decks-row-record-last {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 0.1rem;
}
.decks-row-record-empty {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11.5px;
  color: var(--muted);
}
.decks-row-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.decks-row-open {
  padding: 0.4rem 0.75rem;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 4px;
  font-family: "Montserrat", sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
}
.decks-row-open:hover {
  background: var(--panel-2);
}
.decks-edit-hint {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  margin-left: 0.25rem;
}

/* ---------- Pending: progress strip + editorial batch headers ---------- */

.pending-progress-strip {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem;
  align-items: center;
  padding: 0.85rem 1.25rem;
}
.pending-progress-label {
  display: flex;
  flex-direction: column;
}
.pending-progress-l {
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.pending-progress-v {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12.5px;
  color: var(--text);
  font-weight: 600;
  margin-top: 0.15rem;
}
.pending-progress-bar {
  height: 6px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}
.pending-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--brand-gold), #d4a85f);
}
.pending-progress-aux {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--muted);
}

/* Editorial batch headers (non-drawer-sorter users). */
.pending-batch-summary {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: 1rem;
  align-items: center;
  padding: 0.65rem 0;
  cursor: pointer;
  list-style: none;
}
.pending-batch-summary::-webkit-details-marker {
  display: none;
}
.pending-batch-ix {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1.3rem;
  color: var(--brand-gold);
  font-weight: 500;
  line-height: 1;
  min-width: 1.6rem;
}
.pending-batch-h {
  min-width: 0;
}
.pending-batch-source {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1.05rem;
  color: var(--text);
  font-weight: 500;
  letter-spacing: -0.005em;
}
.pending-batch-note {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--muted);
  margin-top: 0.1rem;
}
.pending-batch-count {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--text-dim);
  padding: 0.25rem 0.65rem;
  background: var(--brand-gold-soft);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-weight: 600;
}
.pending-batch-date {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}

/* ---------- Card Detail Folio polish ---------- */

.card-detail-hero {
  margin-top: 1rem;
}
.card-detail-hero-grid {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 1.5rem;
  align-items: flex-start;
}
.card-detail-art {
  flex: 0 0 auto;
}
.card-detail-art-img {
  width: 100%;
  max-width: 240px;
  height: auto;
  display: block;
  border-radius: 12px;
  filter: drop-shadow(0 16px 28px rgba(0, 0, 0, 0.5));
}
.card-detail-art-placeholder {
  width: 240px;
  height: 335px;
  background: var(--panel-2);
  border: 1px dashed var(--border);
  border-radius: 12px;
}
.card-detail-hero-copy {
  min-width: 0;
}
.card-detail-name {
  margin: 0.25rem 0 0.4rem;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-weight: 500;
  font-size: 2rem;
  line-height: 1.05;
  color: var(--text);
  letter-spacing: -0.012em;
}
.card-detail-type {
  font-family: "Newsreader", serif;
  font-size: 14px;
  color: var(--text-dim);
  margin-bottom: 0.5rem;
}
.card-detail-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--text-dim);
  margin-bottom: 0.5rem;
}
.card-detail-mana {
  color: var(--brand-gold);
  font-weight: 600;
}
.card-detail-cmc {
  color: var(--muted);
}
.card-detail-meta-sep {
  color: var(--muted);
}
.card-detail-rarity {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
}
.card-detail-rarity-common {
  color: var(--text-dim);
}
.card-detail-rarity-uncommon {
  color: #c0c5cc;
}
.card-detail-rarity-rare {
  color: var(--brand-gold);
}
.card-detail-rarity-mythic {
  color: #e08a3c;
}
.card-detail-identity {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.card-detail-identity-lbl {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.card-detail-identity-pips img.mana-pip {
  width: 18px;
  height: 18px;
  margin-left: 0;
}
.card-detail-stats {
  margin: 0.5rem 0;
}
.card-detail-scryfall-link {
  margin: 0.5rem 0 0;
  font-size: 12px;
}

.card-detail-oracle {
  margin-top: 1rem;
}
.card-detail-oracle-body {
  font-family: "Newsreader", serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text);
  white-space: pre-line;
  margin-top: 0.5rem;
  max-width: 70ch;
}

/* § panels — Printings / In Decks / Prices / Legality / History */
.card-detail-panel {
  margin-top: 1rem;
}
.card-detail-panel-h {
  margin: 0 0 0.85rem;
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--brand-gold);
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.card-detail-panel-h::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.card-detail-panel-aux {
  text-transform: none;
  letter-spacing: 0.02em;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  font-weight: 400;
}

/* § I — Printings */
.card-detail-printings {
  display: flex;
  flex-direction: column;
}
.card-detail-printings-row {
  display: grid;
  grid-template-columns: 1.2rem 4rem 1fr 5.5rem 4rem 4rem;
  gap: 0.75rem;
  align-items: center;
  padding: 0.4rem 0;
  border-bottom: 1px dotted var(--border);
}
.card-detail-printings-row:last-child {
  border-bottom: 0;
}
.card-detail-printings-head {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.card-detail-printings-current {
  background: var(--brand-gold-soft);
  padding-left: 0.4rem;
  padding-right: 0.4rem;
  border-radius: 4px;
}
.card-detail-printings-ix {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 10px;
  color: var(--muted);
}
.card-detail-printings-set {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--brand-gold);
  letter-spacing: 0.08em;
  font-weight: 600;
}
.card-detail-printings-collector a {
  font-family: "Newsreader", serif;
  font-size: 13px;
  color: var(--text);
  text-decoration: none;
}
.card-detail-printings-collector a:hover {
  color: var(--brand-gold);
}
.card-detail-printings-watch {
  color: var(--brand-gold);
  margin-left: 0.3rem;
}
.card-detail-printings-r {
  text-align: right;
}
.card-detail-printings-px {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--brand-gold);
  font-weight: 600;
}
.card-detail-printings-ow {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--text);
}
.card-detail-printings-ow-zero {
  color: var(--muted);
}
.card-detail-printings-act {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: right;
}
.card-detail-printings-act a {
  color: var(--brand-gold);
  text-decoration: none;
}
.card-detail-printings-cur {
  color: var(--muted);
  font-style: italic;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}

/* § II — In Decks */
.card-detail-in-decks {
  display: flex;
  flex-direction: column;
}
.card-detail-in-decks-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px dotted var(--border);
}
.card-detail-in-decks-row:last-child {
  border-bottom: 0;
}
.card-detail-in-decks-nm a {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 13.5px;
  color: var(--text);
  text-decoration: none;
}
.card-detail-in-decks-nm a:hover {
  color: var(--brand-gold);
}
.card-detail-in-decks-role {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--brand-gold);
  margin-top: 0.1rem;
}
.card-detail-in-decks-qty {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--text);
  font-weight: 600;
}

/* § III — Prices */
.card-detail-prices {
  display: flex;
  flex-direction: column;
}
.card-detail-prices-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px dotted var(--border);
}
.card-detail-prices-row:last-child {
  border-bottom: 0;
}
.card-detail-prices-l {
  font-family: "Newsreader", serif;
  font-size: 13px;
  color: var(--text-dim);
}
.card-detail-prices-v {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 13.5px;
  color: var(--text);
  font-weight: 600;
}
.card-detail-prices-deferred {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 0.6rem;
  padding-top: 0.5rem;
  border-top: 1px dotted var(--border);
}

/* § IV — Legality */
.card-detail-legality {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.4rem 1rem;
}
.card-detail-legality-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.3rem 0;
  font-family: "Newsreader", serif;
  font-size: 12.5px;
  color: var(--text-dim);
}
.card-detail-legality-fmt {
  color: var(--text);
}
.card-detail-legality-status {
  font-family: "Montserrat", sans-serif;
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
}
.card-detail-legality-legal .card-detail-legality-status {
  color: var(--brand-green);
}
.card-detail-legality-not_legal .card-detail-legality-status {
  color: var(--muted);
}
.card-detail-legality-banned .card-detail-legality-status {
  color: var(--danger);
}
.card-detail-legality-restricted .card-detail-legality-status {
  color: #e08a3c;
}

/* § V — History */
.card-detail-history {
  display: flex;
  flex-direction: column;
}
.card-detail-history-row {
  display: grid;
  grid-template-columns: 6rem 1fr auto;
  gap: 0.6rem;
  align-items: baseline;
  padding: 0.35rem 0;
  border-bottom: 1px dotted var(--border);
}
.card-detail-history-row:last-child {
  border-bottom: 0;
}
.card-detail-history-t {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-gold);
  font-weight: 700;
}
.card-detail-history-n {
  font-family: "Newsreader", serif;
  font-size: 12.5px;
  color: var(--text);
}
.card-detail-history-qty {
  color: var(--muted);
  margin-left: 0.25rem;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10.5px;
}
.card-detail-history-d {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10.5px;
  color: var(--muted);
}

/* ---------- Deck Detail § section mark (polish-only) ---------- */
.deck-detail-section-mark {
  color: var(--brand-gold);
  font-style: italic;
  font-family: "Newsreader", serif;
  font-weight: 500;
  margin-right: 0.25rem;
}

/* ---------- Responsive — every v3.28.7 surface collapses cleanly ---------- */
@media (max-width: 980px) {
  .decks-featured-grid {
    grid-template-columns: auto 1fr;
  }
  .decks-featured-record {
    grid-column: 1 / -1;
    border-left: 0;
    border-top: 1px solid var(--border);
    padding-left: 0;
    padding-top: 1rem;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    min-width: 0;
  }
  .decks-featured-open {
    margin-top: 0;
    margin-left: auto;
  }
  .decks-row {
    grid-template-columns: 1fr auto;
  }
  .decks-row-stats {
    grid-column: 1 / -1;
    grid-template-columns: repeat(2, 1fr);
  }
  .decks-row-record {
    grid-column: 1 / -1;
    text-align: left;
  }
  .decks-row-actions {
    grid-column: 1 / -1;
    justify-content: flex-start;
  }
  .card-detail-hero-grid {
    grid-template-columns: 1fr;
  }
  .card-detail-art-img {
    max-width: 180px;
  }
  .card-detail-printings-row {
    grid-template-columns: 1fr 4rem 4rem;
    row-gap: 0.2rem;
  }
  .card-detail-printings-ix,
  .card-detail-printings-set,
  .card-detail-printings-act {
    display: none;
  }
}

@media (max-width: 768px) {
  .decks-featured {
    padding: 1rem;
  }
  .decks-featured-grid {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .decks-featured-art {
    justify-content: flex-start;
  }
  .pending-progress-strip {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  .pending-batch-summary {
    grid-template-columns: auto 1fr;
    row-gap: 0.25rem;
  }
  .pending-batch-count,
  .pending-batch-date {
    grid-column: 2;
  }
  .card-detail-legality {
    grid-template-columns: 1fr;
  }
}

/* ===================================================================
   v3.28.8 — Folio Collection redesign (faceted sidebar + view toggle +
   rows mode + kebab menu)
   ===================================================================
   Two-column layout: faceted sidebar on the left, content area on the
   right. The boolean search input lives in the controls panel above
   (unchanged from pre-v3.28.8); the sidebar holds the new faceted
   filters. Both submit independently to /collection — facets and search
   are independent URL params, AND-composed at query time (Option A).

   Preserve-verbatim: inventory_card's 7 inline actions render in Grid
   mode via the unchanged macro; in Rows mode they live behind a kebab
   menu (⋮) with IDENTICAL form contracts.

   Token-driven; Folio palette (`--text` / `--text-dim` / `--muted` /
   `--brand-gold` / `--brand-gold-soft` / `--border` / `--panel` /
   `--panel-2`). Zero parallel tokens. =================================== */

/* Hero italic-lede tagline */
.collection-hero-lede b {
  color: var(--text);
  font-weight: 500;
}

/* Toolbar — search input enlarged so the boolean syntax is readable */
.collection-toolbar {
  align-items: stretch;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.collection-search-input {
  flex: 2 1 320px;
  min-width: 220px;
}

/* Grid/Rows segmented toggle */
.collection-view-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 5px;
  overflow: hidden;
  margin-left: auto;
}
.collection-view-seg {
  padding: 0.45rem 0.85rem;
  background: var(--panel);
  border: 0;
  color: var(--text-dim);
  font-family: "Montserrat", sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.collection-view-seg + .collection-view-seg {
  border-left: 1px solid var(--border);
}
.collection-view-seg:hover {
  background: var(--panel-2);
}
.collection-view-seg-on {
  background: var(--brand-gold);
  color: var(--bg);
  font-weight: 700;
}

/* Two-column layout */
.collection-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 1.5rem;
  margin-top: 1rem;
  align-items: flex-start;
}
.collection-content {
  min-width: 0;
}

/* Faceted sidebar */
.collection-sidebar {
  position: sticky;
  top: 1rem;
  align-self: flex-start;
}
.collection-sidebar-details {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.1rem;
}
.collection-sidebar-summary {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--brand-gold);
  font-weight: 700;
  cursor: pointer;
  margin-bottom: 0.85rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  list-style: none;
}
.collection-sidebar-summary::-webkit-details-marker {
  display: none;
}
.collection-sidebar-summary::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.collection-facet-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.collection-facet-group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.collection-facet-label {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--muted);
  letter-spacing: -0.005em;
}

.collection-facet-hint {
  font-size: 10.5px;
  color: var(--muted);
  opacity: 0.75;
  margin: 2px 0 4px;
  line-height: 1.3;
}

/* Color identity pip toggles */
.collection-facet-pips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.collection-facet-pip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.25rem 0.45rem;
  border: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.25);
  border-radius: 999px;
  cursor: pointer;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10px;
  color: var(--muted);
}
.collection-facet-pip input[type="checkbox"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}
.collection-facet-pip img.mana-pip {
  width: 18px;
  height: 18px;
  margin-left: 0;
}
.collection-facet-pip-c {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--text-dim);
  font-weight: 700;
  font-size: 10px;
}
/* v3.31.0 — the pip's active styling is driven ENTIRELY by its checkbox
   state via :has(input:checked). The server renders `checked` for active
   colors, so the initial (post-Apply) state highlights correctly, and
   toggling updates live in both directions. An earlier cut also kept a
   server-rendered `collection-facet-pip-on` class, but that class stuck
   after a reload — unchecking a previously-applied pip left the highlight
   on ("deselects invisibly"). Sourcing the highlight only from the live
   checkbox state removes that stale-class bug. */
.collection-facet-pip:has(input:checked) {
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
}
.collection-facet-pip:has(input:checked) .collection-facet-pip-c {
  color: var(--brand-gold);
  background: var(--bg);
}
.collection-facet-pip-count {
  color: var(--text-dim);
  font-weight: 600;
}

/* Checkbox lists with counts */
.collection-facet-checks {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.collection-facet-check {
  display: grid;
  grid-template-columns: 1.1rem 1fr auto;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0;
  font-family: "Newsreader", serif;
  font-size: 12.5px;
  color: var(--text-dim);
  cursor: pointer;
}
.collection-facet-check input[type="checkbox"] {
  accent-color: var(--brand-gold);
  width: 1rem;
  height: 1rem;
  margin: 0;
}
.collection-facet-check-count {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10px;
  color: var(--muted);
  text-align: right;
}

/* Price range inputs */
.collection-facet-range {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.collection-facet-range-input {
  flex: 1 1 0;
  min-width: 0;
  padding: 0.3rem 0.4rem;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11px;
}
.collection-facet-range-sep {
  color: var(--muted);
  font-size: 10px;
}

/* Sidebar action row */
.collection-facet-actions {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.4rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--border);
}
.collection-facet-apply {
  padding: 0.5rem 0.85rem;
  background: var(--brand-gold);
  color: var(--bg);
  border: 1px solid var(--brand-gold);
  border-radius: 4px;
  font-family: "Montserrat", sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.collection-facet-clear-link {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  text-decoration: none;
  text-align: center;
}
.collection-facet-clear-link:hover {
  color: var(--brand-gold);
}

/* Filter description above results */
.collection-filter-desc {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 13px;
  color: var(--text-dim);
  margin: 0 0 0.85rem;
}
.collection-filter-desc em {
  color: var(--muted);
  font-style: italic;
}

/* === ROWS view === */
.collection-rows-wrap {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.collection-row {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.55rem 0.8rem;
  display: grid;
  grid-template-columns: 32px 1.6fr 1fr 3rem 4.5rem 4.5rem auto;
  gap: 0.85rem;
  align-items: center;
  position: relative;
}
.collection-row-thumb {
  width: 32px;
  height: 44px;
  display: block;
  flex: 0 0 auto;
}
.collection-row-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 2px;
}
.collection-row-thumb-placeholder {
  width: 100%;
  height: 100%;
  background: var(--panel-2);
  border-radius: 2px;
}
.collection-row-copy {
  min-width: 0;
}
.collection-row-name {
  font-family: "Newsreader", serif;
  font-size: 14px;
  color: var(--text);
  letter-spacing: -0.005em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.collection-row-name a {
  color: inherit;
  text-decoration: none;
}
.collection-row-name a:hover {
  color: var(--brand-gold);
}
.collection-row-pending-mark {
  margin-left: 0.25rem;
  font-size: 11px;
  filter: grayscale(0.4);
}
.collection-row-meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.15rem;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10px;
  color: var(--text-dim);
}
.collection-row-meta-sep {
  color: var(--muted);
}
.collection-row-set {
  background: var(--brand-gold-soft);
  padding: 1px 5px;
  border-radius: 2px;
  color: var(--text);
  letter-spacing: 0.06em;
  font-weight: 600;
}
.collection-row-finish {
  font-size: 9.5px;
}
.collection-row-pips {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}
.collection-row-pips img.mana-pip {
  width: 13px;
  height: 13px;
  margin-left: 0;
}
.collection-row-proxy {
  color: var(--muted);
  font-style: italic;
}
.collection-row-loc {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.collection-row-qty {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12px;
  color: var(--text);
  text-align: right;
  font-weight: 600;
}
.collection-row-price {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 11.5px;
  color: var(--brand-gold);
  text-align: right;
  font-weight: 600;
}
.collection-row-total {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12px;
  color: var(--text);
  text-align: right;
  font-weight: 600;
}

/* Kebab menu (⋮) — collapses the 7 actions behind a single-tap surface */
.collection-row-kebab {
  position: relative;
}
.collection-row-kebab-summary {
  cursor: pointer;
  font-size: 1.1rem;
  padding: 0.35rem 0.55rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-dim);
  list-style: none;
  user-select: none;
}
.collection-row-kebab-summary::-webkit-details-marker {
  display: none;
}
.collection-row-kebab[open] .collection-row-kebab-summary {
  background: var(--brand-gold);
  color: var(--bg);
}
.collection-row-kebab-body {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 10;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.6rem 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-width: 260px;
  max-width: 360px;
  box-shadow: var(--shadow);
}
.collection-row-kebab-body .inline-form {
  flex-wrap: wrap;
  gap: 0.3rem;
  align-items: center;
}
.collection-row-kebab-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  border-top: 1px solid var(--border);
  padding-top: 0.4rem;
  margin-top: 0.2rem;
}

/* === Responsive === */
@media (max-width: 980px) {
  .collection-layout {
    grid-template-columns: 1fr;
  }
  .collection-sidebar {
    position: static;
  }
  /* Sidebar collapses to closed by default at narrow widths so the
     content gets the full row; the user can tap to expand. */
  .collection-sidebar-details:not([open]) {
    padding-bottom: 1rem;
  }
  .collection-row {
    grid-template-columns: 32px 1fr auto auto;
    grid-template-areas:
      "thumb name name kebab"
      "thumb loc qty price";
    row-gap: 0.3rem;
  }
  .collection-row-thumb {
    grid-area: thumb;
  }
  .collection-row-copy {
    grid-area: name;
  }
  .collection-row-loc {
    grid-area: loc;
    font-size: 11px;
  }
  .collection-row-qty {
    grid-area: qty;
  }
  .collection-row-price {
    grid-area: price;
  }
  .collection-row-total {
    display: none;
  }
  .collection-row-kebab {
    grid-area: kebab;
  }
  .collection-row-kebab-body {
    right: -0.5rem;
    min-width: 240px;
    max-width: calc(100vw - 1.5rem);
  }
}

@media (max-width: 768px) {
  .collection-toolbar {
    gap: 0.4rem;
  }
  .collection-view-toggle {
    margin-left: 0;
  }
  .collection-facet-pips {
    gap: 0.25rem;
  }
}

/* ===================================================================
   v3.28.10 — Folio Imports editorial polish
   ===================================================================
   Editorial-styling pass on the existing sequential funnel (entry →
   preview/reconcile → result) per the cluster's page-fork decision #2.
   No structural change; templates retain their form actions, hidden
   inputs, HTMX contracts, and consequential-write semantics. CSS only
   adjusts copy register, spacing, and section-header treatment.

   Token-driven entirely; consumes the v3.28.1 Folio palette + v3.27.7
   spacing/radius scales. Zero parallel tokens. Reuses
   `.deck-detail-section-mark` (v3.28.7) for the § panel marks.
   =================================================== */

/* Warning panel (no storage location yet) — restyle from raw inline
   border-left to a token-driven editorial warning panel. */
.imports-warning-panel {
  border-left: 4px solid var(--brand-gold);
}
.imports-warning-strong {
  font-weight: 600;
  margin: 0;
  color: var(--text);
}
.imports-warning-detail {
  margin: 0.25rem 0 0;
}

/* Imports entry hero — narrative italic lede beneath the eyebrow */
.imports-hero {
  margin-top: 1rem;
}
.imports-lede {
  max-width: 60ch;
}
.imports-lede b {
  color: var(--text);
  font-weight: 500;
}

/* Per-step panels (Upload CSV / Paste Card List / Search / Manual) */
.imports-step {
  margin-top: 1rem;
}
.imports-step-title {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
}
.imports-step-lede {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 13px;
  color: var(--text-dim);
  margin: 0.4rem 0 0.75rem;
  max-width: 64ch;
  line-height: 1.55;
}
.imports-step-lede code {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-style: normal;
  font-size: 11.5px;
  color: var(--text);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--border);
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
}
.imports-step-lede strong {
  color: var(--text);
  font-weight: 600;
}
.imports-form {
  gap: 0.6rem;
}

/* Preview hero — narrative lede with filename emphasized */
.imports-preview-hero {
  margin-top: 1rem;
}
.imports-preview-title {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.imports-preview-filename {
  color: var(--brand-gold);
  font-style: normal;
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 0.85em;
  margin-left: 0.4rem;
  background: var(--brand-gold-soft);
  padding: 0.1rem 0.5rem;
  border-radius: 3px;
}
.imports-preview-lede {
  max-width: 70ch;
}
.imports-preview-lede b {
  color: var(--text);
  font-weight: 500;
}
.imports-preview-lede em {
  color: var(--text);
  font-style: italic;
}
.imports-manual-meta {
  font-size: 12.5px;
  margin-top: 0.5rem;
}

/* Destination form — editorial label + chip-style submit button */
.imports-destination-row {
  align-items: center;
  gap: 0.6rem;
  margin: 0.85rem 0 0;
}
.imports-destination-label {
  font-family: "Montserrat", sans-serif;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
}
.imports-commit-form {
  gap: 0.5rem;
}
.imports-commit-btn {
  font-weight: 600;
}

/* Valid Rows / Invalid Rows / Failed Rows panels — Folio editorial
   register; tables inside still render via the existing global table
   chrome, so per-row data presentation stays familiar. */
.imports-rows-panel {
  margin-top: 1rem;
}
.imports-rows-invalid {
  border-left: 3px solid rgba(138, 46, 34, 0.4);
}

/* Search-results hero (Choose a Printing) */
.imports-search-hero {
  margin-top: 1rem;
}

/* Result hero — italic emphasis on the outcome line */
.imports-result-hero {
  margin-top: 1rem;
}
.imports-result-lede {
  max-width: 70ch;
}
.imports-result-lede em {
  color: var(--brand-gold);
  font-style: italic;
  font-weight: 500;
}

/* Responsive — all three steps already collapse naturally via the
   existing .filter-row + .stack-form rules + the v3.27.8 .page-shell
   wrapper. No additional breakpoints needed here. */

/* ===================================================================
   v3.30.15 — per-row Location resolution surfaces on the Import Preview
   step. Three panels render conditionally inside the existing commit
   form: ambiguous-name picker, missing-name auto-create confirm, and
   the clean-match duplicate (merge-with-existing) warning. Token reuse
   only (Folio palette + the existing imports-* idiom).
   =================================================================== */
.imports-loc-resolution {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-3) 0;
}
.imports-loc-panel {
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}
.imports-loc-panel-title {
  margin: 0 0 var(--space-2) 0;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  color: var(--text);
}
.imports-loc-panel-title code,
.imports-loc-missing-list code,
.imports-loc-dup-list code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-style: normal;
  font-size: 0.9em;
  padding: 0 4px;
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--brand-gold);
}
.imports-loc-ambiguous {
  border-left: 3px solid var(--brand-gold);
}
.imports-loc-missing {
  border-left: 3px solid var(--brand-gold-soft);
}
.imports-loc-duplicates {
  border-left: 3px solid var(--text-dim);
}
.imports-loc-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
}
.imports-loc-row + .imports-loc-row {
  border-top: 1px dashed var(--border);
}
.imports-loc-name {
  font-weight: 600;
  color: var(--text);
  min-width: 12rem;
}
.imports-loc-select {
  flex: 1;
}
.imports-loc-missing-list {
  margin: 0 0 var(--space-2) 0;
  padding-left: 1.25rem;
  color: var(--text-dim);
}
.imports-loc-missing-list li {
  margin: 0.25rem 0;
}
.imports-loc-confirm {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.95em;
  color: var(--text);
}
.imports-loc-confirm input[type="checkbox"] {
  accent-color: var(--brand-gold);
}
.imports-loc-dup-list {
  margin: 0;
  padding-left: 1.25rem;
  color: var(--text-dim);
}
.imports-loc-dup-list li {
  margin: 0.35rem 0;
}
.imports-loc-error {
  padding: var(--space-3);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: var(--panel-2);
  color: var(--danger);
  font-style: italic;
}
/* v3.30.16 — per-name Location Type dropdown inside the auto-create panel.
   Sits inline next to each missing location name; reuses the existing
   imports-loc-* idiom. The warning span flags an invalid CSV-supplied
   type that defaulted to "other" — the dropdown still works; the user
   can pick any valid type from the select. */
.imports-loc-type-select {
  margin-left: var(--space-2);
}
.imports-loc-type-warning {
  margin-left: var(--space-2);
  font-style: italic;
  color: var(--text-dim);
  font-size: 0.9em;
}
.imports-loc-type-warning code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 0 4px;
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--brand-gold);
}

/* ===================================================================
   v3.30.17 — deck-create panel + existing-deck informational note.
   Token reuse only (Folio palette + spacing/radius scales). The deck
   panel uses a slightly stronger brass-gold border than the generic
   .imports-loc-missing block to draw the eye, because creating a Deck
   record is a more consequential action than creating a generic
   storage location.
   =================================================================== */
.imports-loc-deck-create {
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
}
.imports-loc-deck-list {
  list-style: none;
  padding-left: 0;
}
.imports-loc-deck-row {
  margin: var(--space-2) 0;
}
.imports-loc-deck-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  user-select: none;
}
.imports-loc-deck-checkbox {
  margin: 0;
  cursor: pointer;
}
.imports-loc-existing-deck {
  border-color: var(--brand-gold-soft);
}
.imports-loc-existing-deck-list {
  list-style: disc;
  padding-left: var(--space-4);
  margin: var(--space-2) 0 0;
  font-style: italic;
  color: var(--text-dim);
}
.imports-loc-existing-deck-list li {
  margin: var(--space-1) 0;
}
.imports-loc-existing-deck-list code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 0 4px;
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--brand-gold);
  font-style: normal;
}
/* v3.30.17.1 — inline deck-confirm label appearing under the dropdown
   in the Case 3 (CSV-blank/unknown) auto-create rows. Indented to
   visually associate with the dropdown row above it; gold-soft
   background so it reads as the "yes, create a deck" affordance. */
.imports-loc-deck-inline-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-1) 0 0 var(--space-3);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: var(--brand-gold-soft);
  cursor: pointer;
  user-select: none;
}
.imports-loc-deck-inline-checkbox {
  margin: 0;
  cursor: pointer;
}
/* v3.30.20 — cross-user Deck.name conflict warning at preview time.
   Pre-v3.1.0 legacy decks.name UNIQUE auto-index would block
   create_deck on commit; surface to the user BEFORE they tick the
   confirm checkbox. Two contexts:
   1. Deck panel — the per-row checkbox is REPLACED by a warning span.
   2. Non-deck panel (Case 3) — the dropdown silently excludes "deck"
      and a small warning span explains why. */
.imports-loc-deck-conflict {
  background: rgba(138, 46, 34, 0.08);
  border-left: 3px solid var(--danger);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
}
.imports-loc-deck-conflict-warning {
  display: inline-block;
  color: var(--text);
  font-style: italic;
  font-size: 0.95em;
  line-height: 1.5;
}
.imports-loc-deck-conflict-warning strong {
  font-style: normal;
  color: var(--brand-gold);
}
.imports-loc-deck-conflict-panel {
  border-left: 3px solid var(--danger);
}
.imports-loc-deck-conflict-list {
  list-style: disc;
  padding-left: var(--space-4);
  margin: var(--space-2) 0 0;
}
.imports-loc-deck-conflict-list code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 0 4px;
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--brand-gold);
}

/* ===================================================================
   v3.28.11 — Folio Watchlist refresh (target price + light polish)
   ===================================================================
   The Watchlist page (v3.27.12) is already on the Folio palette. v3.28.11
   adds a per-row target_price column with an inline-edit popover that
   mirrors the existing note-field pattern, plus a row highlight when
   current price ≤ target. Plus a thin editorial italic lede on the
   hero. No structural change.
   =================================================== */

/* Page hero — italic Newsreader lede with emphasis on "Set a target price" */
.watchlist-hero {
  margin-top: 1rem;
}
.watchlist-lede em {
  color: var(--text);
  font-style: italic;
  font-weight: 500;
}

/* Table micro-typography */
.watchlist-table {
  /* Existing .stacking-table chrome carries; just add a few inline-text
     sizes the page hadn't pinned. */
}
.watchlist-card-meta {
  font-size: 0.78rem;
  margin-top: 0.15rem;
}
.watchlist-owned-sub,
.watchlist-current-sub {
  font-size: 0.72rem;
  font-style: italic;
  margin-top: 0.1rem;
}

/* Current Price column */
.watchlist-current-cell {
  min-width: 5.5rem;
}
.watchlist-current-price {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12.5px;
  color: var(--text);
  font-weight: 600;
}

/* Target Price column */
.watchlist-target-cell {
  min-width: 5.5rem;
}
.watchlist-target-display {
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 12.5px;
  color: var(--text);
  font-weight: 600;
}
.watchlist-target-met-mark {
  color: var(--brand-gold);
}
.watchlist-target-hint {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 11px;
  color: var(--muted);
  font-weight: 400;
  margin-left: 0.25rem;
}

/* Row highlight when current ≤ target — soft brass-gold tint + left
   border, matching the v3.28.7 Decks-row consistency-tier chip register
   so the alert reads in the same visual language. */
.watchlist-row-target-met {
  background: var(--brand-gold-soft);
  position: relative;
}
.watchlist-row-target-met > td:first-child {
  border-left: 3px solid var(--brand-gold);
}
.watchlist-row-target-met .watchlist-current-price {
  color: var(--brand-gold);
}

/* Actions cell alignment */
.watchlist-actions-cell {
  text-align: right;
  white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────
   v3.29.0 — Playgroups. Membership-scoped social grouping (the
   opener of the v3.29.x social-features minor). Token reuse only —
   inherits the locations/decks inline-popout idiom from v3.10.6 and
   the Folio palette + typography from v3.28.x. Zero parallel tokens.
   ───────────────────────────────────────────────────────────────── */

.playgroups-hero {
  margin-bottom: 1rem;
}

/* Flash messages — error / success bands above the content */
.playgroups-flash {
  padding: 0.7rem 1rem;
  margin-bottom: 0.85rem;
  border-radius: var(--radius);
  font-size: 0.9rem;
}
.playgroups-flash-error {
  background: rgba(138, 46, 34, 0.18);
  border: 1px solid rgba(138, 46, 34, 0.5);
  color: var(--danger);
}
.playgroups-flash-success {
  background: var(--brand-gold-soft);
  border: 1px solid var(--brand-gold);
  color: var(--brand-gold);
}

/* Index table */
.playgroups-table .playgroups-name-link {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.05rem;
  color: var(--text);
  text-decoration: none;
}
.playgroups-table .playgroups-name-link:hover {
  color: var(--brand-gold);
}
.playgroups-notes {
  font-size: 0.78rem;
  color: var(--text-dim);
  font-style: italic;
  margin-top: 0.2rem;
  max-width: 56ch;
}

/* Role badge — owner is brass-gold; member is muted neutral */
.playgroup-role-badge {
  display: inline-block;
  padding: 0.1rem 0.55rem;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 1px solid;
}
.playgroup-role-owner {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.playgroup-role-member {
  background: var(--panel-2);
  border-color: var(--border);
  color: var(--text-dim);
}

/* Empty state */
.playgroups-empty {
  text-align: center;
  padding: 2rem 1rem;
}
.playgroups-empty .panel-title {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  margin-bottom: 0.5rem;
}

/* Two-column actions grid (Create + Join side-by-side; collapses on narrow) */
.playgroups-actions-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 1rem;
}
@media (max-width: 768px) {
  .playgroups-actions-grid {
    grid-template-columns: 1fr;
  }
}
.playgroups-create-btn {
  margin-top: 0.4rem;
}

/* Detail page */
.playgroup-meta-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: 0.6rem;
  flex-wrap: wrap;
}

/* Join code block — code is a brass-bordered monospace pill */
.playgroup-code-panel .playgroup-code-help {
  color: var(--text-dim);
  font-size: 0.85rem;
}
.playgroup-code-display {
  margin: 0.6rem 0;
}
.playgroup-code-value {
  display: inline-block;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 1.1rem;
  font-weight: 600;
  padding: 0.45rem 0.85rem;
  background: var(--panel-2);
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius-sm);
  color: var(--brand-gold);
  letter-spacing: 0.02em;
  user-select: all;
}
.playgroup-code-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.6rem;
  flex-wrap: wrap;
}

/* Members table — sits on the existing .stacking-table chrome */
.playgroup-members-table td[data-label="Name"] {
  font-weight: 500;
}

/* Join confirm view */
.playgroups-join-confirm .panel-title em {
  color: var(--brand-gold);
}
.playgroups-join-actions {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  margin-top: 0.5rem;
  flex-wrap: wrap;
}

/* ─────────────────────────────────────────────────────────────────
   v3.29.1 — Collection sharing (Showcase + Share). Token reuse only —
   inherits the Folio palette + the v3.10.6 inline-popout idiom + the
   v3.29.0 .playgroup-* role-badge pattern. Zero parallel tokens.
   Showcase ≠ Share (the two-name discipline matches the model
   docstrings); the .showcase-* and .share-* rules sit side-by-side
   so the visual register stays cohesive across the two pages.
   ───────────────────────────────────────────────────────────────── */

/* Heroes inherit .pagehead-tagline italic Newsreader from v3.28.x */
.showcase-hero,
.share-hero,
.share-view-hero {
  margin-bottom: 1rem;
}

/* Flash messages — same shape as .playgroups-flash */
.showcase-flash,
.share-flash {
  padding: 0.7rem 1rem;
  margin-bottom: 0.85rem;
  border-radius: var(--radius);
  font-size: 0.9rem;
}
.showcase-flash-error,
.share-flash-error {
  background: rgba(138, 46, 34, 0.18);
  border: 1px solid rgba(138, 46, 34, 0.5);
  color: var(--danger);
}
.showcase-flash-success,
.share-flash-success {
  background: var(--brand-gold-soft);
  border: 1px solid var(--brand-gold);
  color: var(--brand-gold);
}

/* Showcase items grid */
.showcase-items-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0.85rem;
  margin-top: 0.85rem;
}
.showcase-item {
  display: flex;
  gap: 0.75rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.7rem;
}
.showcase-item-art img,
.showcase-item-placeholder {
  width: 64px;
  height: 88px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--panel);
  border: 1px solid var(--border);
}
.showcase-item-body {
  flex: 1 1 auto;
  min-width: 0;
}
.showcase-item-title {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.02rem;
  margin: 0 0 0.25rem;
  color: var(--text);
}
.showcase-item-meta {
  font-size: 0.78rem;
  color: var(--text-dim);
  margin-bottom: 0.35rem;
}
.showcase-item-qty {
  font-size: 0.85rem;
  color: var(--text);
  margin-bottom: 0.4rem;
}
.showcase-item-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  flex-wrap: wrap;
}
.showcase-empty {
  color: var(--text-dim);
  font-style: italic;
}

/* Instant client-side list filter (v3.32.x) — shared by the showcase/share
   list pages and the per-showcase/share card grids. See list-filter.js. */
.list-filter-wrap {
  margin-bottom: 1rem;
}
.list-filter-input {
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.95rem;
  width: 100%;
  max-width: 360px;
}
.list-filter-input::placeholder {
  color: var(--text-dim);
}
.list-filter-empty {
  color: var(--text-dim);
  font-style: italic;
  margin-top: 0.5rem;
}

/* Server-side card search inside a showcase / shared view (v3.32.3) — submits
   the app's boolean/Scryfall query, mirroring the Collection search bar. */
.list-search-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 1rem;
}
.list-search-form .list-filter-input {
  flex: 1 1 320px;
}

/* Shares list (on /shares) */
.share-table .share-pg-link {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
}
.share-table .share-pg-link:hover {
  color: var(--brand-gold);
}
.share-actions-cell {
  display: flex;
  gap: 0.45rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.share-create-form {
  max-width: 420px;
}
.share-empty {
  color: var(--text-dim);
  font-style: italic;
}

/* Shares panel on /playgroups/{id} */
.share-list {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin-top: 0.6rem;
}
.share-list-item {
  display: flex;
  gap: 0.85rem;
  align-items: center;
  justify-content: space-between;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.85rem;
}
.share-list-meta {
  min-width: 0;
}
.share-list-name {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: 1.02rem;
  color: var(--text);
  text-decoration: none;
}
.share-list-name:hover {
  color: var(--brand-gold);
}

/* Read-only share view */
.share-view-description {
  margin-top: 0.4rem;
  color: var(--text-dim);
  font-size: 0.92rem;
}
.share-view-meta {
  margin-top: 0.4rem;
  font-size: 0.82rem;
}
.share-view-grid {
  margin-top: 0.6rem;
}

/* ── v3.29.2 — Pairwise trading ─────────────────────────────────
   The third and final release of the v3.29.x social-features minor.
   Recording-only model (decision B1): trades record the agreement;
   the app never moves InventoryRow. Token reuse only — Folio palette
   + radius/spacing scales from v3.27.7. Zero parallel tokens. */

.trades-hero,
.trade-new-hero,
.trade-detail-hero {
  margin-bottom: 1rem;
}

.trades-flash {
  margin-bottom: 0.8rem;
  padding: 0.7rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  font-size: 0.94rem;
}
.trades-flash-error {
  border-color: var(--danger);
  background: rgba(138, 46, 34, 0.10);
  color: var(--text);
}
.trades-flash-success {
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
  color: var(--text);
}

.trades-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 0.6rem;
}

.trades-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.trades-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.7rem 0.4rem;
  border-bottom: 1px solid var(--border-soft, var(--border));
}
.trades-row:last-child {
  border-bottom: none;
}
.trades-row-closed {
  opacity: 0.85;
}
.trades-row-link {
  text-decoration: none;
  color: var(--text);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex: 1 1 auto;
  min-width: 0;
}
.trades-row-link:hover .trades-row-headline {
  color: var(--brand-gold);
}
.trades-row-headline {
  font-family: Newsreader, Georgia, serif;
  font-size: 1.02rem;
}
.trades-row-meta {
  font-size: 0.84rem;
  color: var(--text-dim);
}
.trades-row-status,
.trade-status-pill {
  flex: 0 0 auto;
  padding: 0.18rem 0.6rem;
  border-radius: var(--radius-pill);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-dim);
  white-space: nowrap;
}
.status-proposed {
  border-color: var(--brand-gold);
  background: var(--brand-gold-soft);
  color: var(--text);
}
.status-accepted {
  border-color: var(--brand-green);
  background: rgba(106, 130, 83, 0.14);
  color: var(--text);
}
.status-declined,
.status-cancelled,
.status-abandoned {
  border-color: var(--border);
  background: var(--panel-2);
  color: var(--muted);
}
.trades-empty {
  margin: 0.4rem 0;
  color: var(--text-dim);
  font-style: italic;
}

/* Construction page */
.trade-new-context {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
}
.trade-new-recipient-form {
  max-width: 460px;
}
.trade-pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0.6rem;
}
.trade-pick-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.6rem 0.7rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.trade-pick-prefilled {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
}
.trade-pick-card {
  flex: 1 1 auto;
  min-width: 0;
}
.trade-pick-name {
  font-family: Newsreader, Georgia, serif;
  font-style: italic;
  font-weight: 500;
  font-size: 0.98rem;
  color: var(--text);
}
.trade-pick-meta {
  margin-top: 0.2rem;
  font-size: 0.82rem;
  color: var(--text-dim);
}
.trade-pick-controls {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: flex-end;
}
.trade-pick-qty {
  width: 4rem;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: var(--radius-sm);
}
.trade-pick-add:disabled {
  background: var(--panel-2);
  color: var(--muted);
  cursor: default;
}

.trade-new-submit {
  display: flex;
  gap: 0.6rem;
}

/* Detail page */
.trade-detail-meta {
  margin-top: 0.3rem;
  font-size: 0.86rem;
}
.trade-items-grid {
  margin-top: 0.4rem;
}
.trade-note-panel p {
  font-family: Newsreader, Georgia, serif;
}
.trade-actions-panel {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.trade-decline-form {
  border-top: 1px solid var(--border-soft, var(--border));
  padding-top: 0.6rem;
}

.trade-agreed-panel {
  background: rgba(106, 130, 83, 0.10);
  border-color: var(--brand-green);
}
.trade-agreed-steps {
  margin: 0.4rem 0 0.6rem 1.4rem;
  padding: 0;
  color: var(--text);
}
.trade-agreed-steps li {
  margin: 0.2rem 0;
}

/* Share-view per-item propose link (v3.29.2). The link is rendered
   alongside each inventory_card by share_view.html. */
.share-view-item-wrap {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.share-view-propose-link {
  align-self: stretch;
  text-align: center;
  font-size: 0.84rem;
  padding: 0.35rem 0.6rem;
}

/* Nav badge — pairs with the .nav-item layout that ships from
   v3.27.15. Used by the Trades sidebar item to surface pending-action
   count. */
.nav-badge {
  display: inline-block;
  margin-left: auto;
  padding: 0 0.45rem;
  font-size: 0.72rem;
  font-weight: 700;
  background: var(--brand-gold);
  color: var(--brand-near-black);
  border-radius: var(--radius-pill);
  line-height: 1.6;
}

@media (max-width: 768px) {
  .trades-row {
    flex-direction: column;
    align-items: flex-start;
  }
  .trade-pick-grid {
    grid-template-columns: 1fr;
  }
}

/* ============================================================================
   v3.29.4 — Editorial Finish: Landing, Sets, Tokens
   ----------------------------------------------------------------------------
   Editorial design-language pass over three pages: the public landing, the
   Sets index, and the Tokens page. Token reuse only — the v3.29.4 spec
   permits exactly ONE new token (`--font-display`, the Source Serif 4 stack
   declared in :root above) and the `@font-face` woff2 wiring also above.
   No new color tokens. No per-template hardcoded colors.

   What this block adds:
     - `.editorial-display` — the Source Serif 4 display register, the
       "The X." titling voice with terminal period. Page H1s.
     - `.chapter-eyebrow` — letterspaced small-caps editorial eyebrow with
       roman numerals ("§ I — THE VOLUMES"). Landing-only at v3.29.4 ship;
       reusable elsewhere if a future page wants chapter framing.
     - `.section-divider` — centered § divider between sections.
     - `.intro-panel` — composed intro panel for catalogue pages (eyebrow +
       serif title + one-line lede). Used by sets + tokens.
     - `.editorial-card` — composed editorial card; restrained 1px borders,
       generous padding. Used by sets index restyle.
     - Landing-specific tweaks layered on top of the v3.27.17 .landing-*
       ruleset above (NOT replacing; the v3.27.17 markup stays). The .lp-*
       prefix is new — additive overrides that re-style title/lede/feature
       cards/screenshot panels to the editorial register.
     - Sets-specific restyle of the .set-dashboard-grid / .set-dashboard-card
       chrome to editorial card composition (not a structural change —
       same .a element, same href, same per-card content).
     - Tokens-specific intro-panel + filter-bar polish. Table chrome
       inherits global <table> rules; the .col-priority-* + .table-wrap
       responsive fallback (v3.27.16) is preserved untouched.

   Cluster precedent: v3.27.15 (navy aesthetic pass) and v3.28.1 (Folio
   reskin) both did token-driven sweeps without touching individual page
   markup beyond the minimum needed; v3.29.4 follows the same posture.
   ============================================================================ */

/* --- Editorial display register --------------------------------------------- */
.editorial-display {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.04;
  color: var(--text);
  margin: 0;
}

/* The "The X." page-title voice — short, article-led, terminal period.
   The terminal period is part of the typesetting (it's in the markup; we
   don't generate it via ::after to keep screen readers honest). Sizing
   ramps fluidly from phone to ultrawide. */
.the-x-title {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: clamp(2rem, 4.8vw, 3.4rem);
  letter-spacing: -0.015em;
  line-height: 1.02;
  color: var(--text);
  margin: 0;
}

/* --- Chapter eyebrow + § divider (editorial section motifs) ----------------- */
.chapter-eyebrow {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  font-family: "Montserrat", system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brand-gold);
  margin: 0 0 1rem;
}
.chapter-eyebrow .chapter-mark {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.05rem;
  letter-spacing: 0;
  color: var(--brand-gold);
}
.chapter-eyebrow .chapter-roman {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: none;
  color: var(--accent-2);
}

.section-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin: 3rem 0 2.5rem;
  color: var(--brand-gold);
}
.section-divider::before,
.section-divider::after {
  content: "";
  flex: 0 0 60px;
  height: 1px;
  background: var(--border);
}
.section-divider .section-divider-mark {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.6rem;
  color: var(--brand-gold);
  line-height: 1;
}

/* --- Catalogue intro panel (sets + tokens; potentially reusable) ------------ */
.intro-panel {
  padding: var(--space-5) var(--space-5);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin: 0 0 var(--space-5);
  box-shadow: var(--shadow-sm);
}
.intro-panel .intro-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-gold);
  margin: 0 0 0.5rem;
}
.intro-panel .intro-title {
  /* The "The X." voice — uses the same .the-x-title sizing scale at a
     hair smaller cap so the intro panel sits comfortably inside its
     own box rather than dominating the page. */
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  letter-spacing: -0.015em;
  line-height: 1.02;
  color: var(--text);
  margin: 0;
}
.intro-panel .intro-lede {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text-dim);
  margin: 0.55rem 0 0;
  max-width: 56ch;
}

/* --- Editorial card (sets restyle uses this composition) -------------------- */
.editorial-card {
  position: relative;
  display: block;
  padding: 1.1rem 1.15rem 1.2rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
  box-shadow: var(--shadow-sm);
  transition:
    border-color 0.18s ease,
    transform 0.18s ease,
    background 0.18s ease;
}
.editorial-card:hover,
.editorial-card:focus-visible {
  border-color: var(--brand-gold-soft);
  transform: translateY(-1px);
  background: var(--panel-2);
  outline: none;
}

/* ---------------------------------------------------------------------------- */
/* Landing-page editorial overrides (v3.29.4)                                   */
/* ---------------------------------------------------------------------------- */

/* Brand assets — swap from white/mono to full-color. The v3.27.17 .landing-*
   rules above set 32px header logo + 220px hero logo; we just inherit those
   sizing rules — the file path swap happens in landing.html. No CSS change
   needed for the asset swap; this comment documents the contract. */

/* Hero — restyle the tagline to the "The X." voice. The v3.27.17
   .landing-hero-tagline still wins on specificity because we layer the
   editorial overrides as .lp-hero-* below; the markup gets the new class. */
.lp-hero-title {
  /* The "The ruler of your collection." title — Source Serif 4, terminal
     period. Bigger than the v3.27.17 tagline; more confident composition. */
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: clamp(2.4rem, 5.4vw, 4rem);
  letter-spacing: -0.018em;
  line-height: 1.02;
  color: var(--text);
  margin: 0 0 1.25rem;
  /* Override the .brand-name letter-spacing if it lands on the same element */
  text-transform: none;
}

.lp-hero-lede {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 1.1rem;
  line-height: 1.55;
  max-width: 620px;
  margin: 0 auto 2rem;
  color: var(--text-dim);
}

/* Landing section titles — small-caps editorial eyebrows; the markup
   provides a chapter-eyebrow + the-x-title combination. */
.lp-section-title {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  letter-spacing: -0.012em;
  line-height: 1.05;
  text-align: left;
  color: var(--text);
  margin: 0 0 1.5rem;
}

/* Feature cards — composed editorial cards. Adds a roman-numeral marker
   above the feature title. */
.lp-feature {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.65rem 1.5rem 1.5rem;
  position: relative;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.18s ease;
}
.lp-feature:hover {
  border-color: var(--brand-gold-soft);
}
.lp-feature-marker {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  color: var(--brand-gold);
  margin: 0 0 0.35rem;
}
.lp-feature-title {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 0 0 0.55rem;
}
.lp-feature-body {
  font-family: "Newsreader", Georgia, serif;
  font-style: normal;
  font-weight: 400;
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--text-dim);
  margin: 0;
  max-width: 56ch;
}

/* Screenshot showcase — captured screenshots if available, deliberately
   styled placeholders if not (v3.29.4 §4 fallback contract). Each panel
   sits as an editorial card with a header strip identifying what will or
   does appear inside, and a 16:10 inner frame for the eventual image. */
.lp-showcase-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin-top: 1.25rem;
}
@media (max-width: 980px) {
  .lp-showcase-grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 600px) {
  .lp-showcase-grid {
    grid-template-columns: 1fr;
  }
}
.lp-showcase-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.18s ease;
}
.lp-showcase-panel:hover {
  border-color: var(--brand-gold-soft);
}
.lp-showcase-head {
  padding: 0.85rem 1rem 0.75rem;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}
.lp-showcase-eyebrow {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand-gold);
  margin: 0 0 0.2rem;
}
.lp-showcase-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.05rem;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 0;
}
.lp-showcase-frame {
  position: relative;
  aspect-ratio: 16 / 10;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.lp-showcase-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
  z-index: 1;
}
.lp-showcase-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.5rem;
  text-align: center;
  color: var(--muted);
}
.lp-showcase-placeholder-glyph {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 2.2rem;
  color: var(--brand-gold);
  line-height: 1;
  opacity: 0.55;
}
.lp-showcase-placeholder-text {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--text-dim);
  max-width: 32ch;
  margin: 0;
}
.lp-showcase-caption {
  padding: 0.7rem 1rem 0.9rem;
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.88rem;
  line-height: 1.4;
  color: var(--text-dim);
  border-top: 1px solid var(--border-soft);
}

/* Secondary row — restyle the existing .landing-secondary cards to
   match the editorial composition. */
.lp-secondary-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem 1.4rem 1.4rem;
  box-shadow: var(--shadow-sm);
}
.lp-secondary-eyebrow {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand-gold);
  margin: 0 0 0.35rem;
}
.lp-secondary-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.2rem;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 0 0 0.55rem;
}
.lp-secondary-body {
  font-family: "Newsreader", Georgia, serif;
  font-style: normal;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 0;
}

/* ---------------------------------------------------------------------------- */
/* Sets index editorial restyle (v3.29.4)                                       */
/* ---------------------------------------------------------------------------- */

.sets-grid-editorial {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-4);
}
.sets-card-editorial {
  /* Inherits .editorial-card composition (markup applies both classes). */
  padding: 1.15rem 1.2rem 1.25rem;
}
.sets-card-editorial .sets-card-eyebrow {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.3rem;
}
.sets-card-editorial .sets-card-code {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  color: var(--brand-gold);
  line-height: 1;
  margin: 0 0 0.5rem;
}
.sets-card-editorial .sets-card-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.05rem;
  letter-spacing: -0.005em;
  color: var(--text);
  margin: 0 0 0.85rem;
  line-height: 1.25;
}
.sets-card-editorial .sets-card-meta {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border-soft);
}
.sets-card-editorial .sets-card-meta-line {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.88rem;
  line-height: 1.35;
  color: var(--text-dim);
}
.sets-card-editorial .sets-card-meta-line strong {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  margin-right: 0.3rem;
}

/* ---------------------------------------------------------------------------- */
/* Tokens page editorial polish (v3.29.4)                                       */
/* ---------------------------------------------------------------------------- */

/* The intro panel for /tokens uses the shared .intro-panel composition.
   Filter bar + actions get small editorial polish; the table is the
   global <table> chrome with the .table-wrap + .col-priority-* rules
   from v3.27.15/v3.27.16 preserved verbatim. */

.tokens-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
}
.tokens-stat-line {
  font-family: "Newsreader", Georgia, serif;
  font-style: italic;
  font-size: 0.92rem;
  color: var(--text-dim);
  margin: 0;
}
.tokens-stat-line strong {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  margin-right: 0.2rem;
}
.tokens-controls {
  margin-bottom: var(--space-4);
}

/* The §6 verification gate notes Tokens stays a TABLE (not a card grid).
   No `.tokens-grid` / `.tokens-typecard` rules — those would be the
   deferred Tokens-analytics feature recorded in roadmap.md. */

/* ============================================================================
   END v3.29.4 editorial finish block
   ============================================================================ */

/* ============================================================================
   v3.30.0 — Goldfish playtester
   Session-only zone state UI. All rules namespaced .gf-*. Token reuse only.
   ============================================================================ */

.gf-hero .gf-hero-lede { max-width: 64ch; }

.gf-controls-panel {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.gf-controls-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.gf-control-group {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
}

.gf-control-label {
  font: 600 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.gf-input-num {
  width: 4.5rem;
  padding: 0.25rem 0.4rem;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-sm);
  font: 500 13px/1.2 "Montserrat", system-ui, sans-serif;
}

.gf-btn {
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--text);
  font: 500 13px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.gf-btn:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-btn-primary {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--text);
}
.gf-btn-primary:hover {
  background: var(--brand-gold);
  color: var(--brand-near-black);
}
.gf-btn-small { padding: 0.25rem 0.5rem; font-size: 12px; }

.gf-readout-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  padding-top: 0.25rem;
  border-top: 1px solid var(--border-soft, rgba(181,138,71,0.10));
}
.gf-readout {
  font: italic 400 13px/1.4 "Newsreader", Georgia, serif;
  color: var(--text-dim);
}
.gf-readout strong {
  font-style: normal;
  font-family: "Montserrat", system-ui, sans-serif;
  color: var(--text);
}

.gf-board-panel {
  display: grid;
  grid-template-columns: 1fr 240px;
  gap: 1rem;
  align-items: stretch;
}

.gf-board {
  display: grid;
  grid-template-columns: 160px 1fr 160px;
  gap: 0.75rem;
  min-height: 60vh;
}

.gf-rail {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.gf-zone {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.gf-zone-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  padding-bottom: 0.25rem;
  border-bottom: 1px solid var(--border-soft, rgba(181,138,71,0.10));
}
.gf-zone-name {
  font: italic 500 14px/1 "Newsreader", Georgia, serif;
  color: var(--text);
}
.gf-zone-count {
  font: 600 12px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.06em;
  color: var(--muted);
  background: var(--panel);
  padding: 0.2rem 0.5rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
}

.gf-pile {
  position: relative;
  min-height: 180px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.gf-pile-empty {
  font: italic 400 12px/1.4 "Newsreader", Georgia, serif;
  color: var(--muted);
}
.gf-pile-browse { margin-top: 0.25rem; }

.gf-card-back {
  width: 100px;
  height: 140px;
  border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--panel) 0%, var(--brand-near-black) 70%);
  border: 1px solid var(--brand-gold-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 0.12s, transform 0.12s;
}
.gf-card-back:hover {
  border-color: var(--brand-gold);
  transform: translateY(-2px);
}
.gf-card-back-glyph {
  font: italic 600 48px/1 "Newsreader", Georgia, serif;
  color: var(--brand-gold);
}

.gf-board-main {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.gf-battlefield {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-height: 360px;
}

.gf-battlefield-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: flex-start;
  padding: 0.4rem 0.4rem 0.4rem 5.5rem;
  min-height: 150px;
  border: 1px dashed transparent;
  border-radius: var(--radius-sm);
  transition: border-color 0.15s, background 0.15s;
}
.gf-row-lands { background: rgba(181,138,71,0.03); }
.gf-row-label {
  position: absolute;
  left: 0.4rem;
  top: 0.4rem;
  font: italic 500 13px/1 "Newsreader", Georgia, serif;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.gf-drop-hover {
  border-color: var(--brand-gold) !important;
  background: var(--brand-gold-soft) !important;
}

.gf-hand {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-height: 200px;
}
.gf-hand-head { border-bottom: 1px solid var(--border-soft, rgba(181,138,71,0.10)); }
.gf-hand-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: flex-start;
  padding: 0.25rem;
}

.gf-card {
  position: relative;
  width: 100px;
  height: 140px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: transform 0.12s, border-color 0.12s, box-shadow 0.12s;
  user-select: none;
}
.gf-card:hover {
  transform: translateY(-3px);
  border-color: var(--brand-gold);
  box-shadow: var(--shadow-sm);
}
.gf-card.gf-tapped {
  transform: rotate(90deg) translateX(8px);
}
.gf-card.gf-tapped:hover {
  transform: rotate(90deg) translateX(8px) translateY(-3px);
}
.gf-card.gf-dragging { opacity: 0.5; }
/* v3.30.14 — drag-attach host highlight. Applied via dragenter on
   battlefield cards when the dragged source is attachable (per
   isAttachable) AND the host is eligible (per canAttach mirroring
   attachInstance's rejection rules). Removed via dragleave / drop /
   dragend. Subtle outline + soft glow in the brand-gold register;
   token reuse only, zero parallel tokens. The outline goes outside
   the card border (outline doesn't reflow flex children) so the fan
   geometry from v3.30.6's .gf-card-group stays intact during a drag
   onto a host that already has children. */
.gf-card.gf-attach-target {
  outline: 2px solid var(--brand-gold);
  outline-offset: 2px;
  box-shadow: 0 0 12px var(--brand-gold-soft);
}

.gf-card-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.gf-card-fallback {
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  height: 100%;
  background: linear-gradient(170deg, var(--panel) 0%, var(--panel-2) 100%);
  color: var(--text);
  font: 500 10px/1.2 "Montserrat", system-ui, sans-serif;
  overflow: hidden;
}
.gf-card-fb-name { font-weight: 700; font-size: 11px; line-height: 1.2; }
.gf-card-fb-cost { color: var(--brand-gold); font-size: 10px; }
.gf-card-fb-type { color: var(--muted); font-size: 9px; font-style: italic; }
.gf-card-fb-oracle {
  font-size: 9px;
  line-height: 1.25;
  color: var(--text-dim);
  margin-top: 0.2rem;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
}

.gf-preview {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}
.gf-preview-frame {
  width: 100%;
  aspect-ratio: 5 / 7;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  overflow: hidden;
}
.gf-preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.gf-preview-empty {
  font: italic 400 12px/1.4 "Newsreader", Georgia, serif;
  color: var(--muted);
  padding: 1rem;
  text-align: center;
}

.gf-context-menu {
  position: fixed;
  z-index: 1000;
  min-width: 220px;
  background: var(--panel);
  border: 1px solid var(--brand-gold-soft);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow, 0 6px 18px rgba(0,0,0,0.4));
  padding: 0.25rem;
  display: flex;
  flex-direction: column;
}
.gf-menu-item {
  text-align: left;
  padding: 0.4rem 0.6rem;
  border: none;
  background: transparent;
  color: var(--text);
  font: 500 13px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.gf-menu-item:hover {
  background: var(--brand-gold-soft);
  color: var(--brand-gold);
}

.gf-modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1100;
}
.gf-modal[hidden] { display: none; }
.gf-modal-card {
  background: var(--panel);
  border: 1px solid var(--brand-gold-soft);
  border-radius: var(--radius);
  max-width: min(900px, 92vw);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.gf-modal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
}
.gf-modal-body {
  padding: 0.75rem 1rem;
  overflow-y: auto;
}
.gf-modal-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.gf-modal-empty {
  font: italic 400 13px/1.4 "Newsreader", Georgia, serif;
  color: var(--muted);
  padding: 1rem 0;
}

/* Responsive: collapse rails + sidebar at narrow widths */
@media (max-width: 980px) {
  .gf-board-panel {
    grid-template-columns: 1fr;
  }
  .gf-board {
    grid-template-columns: 1fr;
  }
  .gf-rail {
    flex-direction: row;
    flex-wrap: wrap;
  }
  .gf-zone {
    flex: 1 1 220px;
  }
  .gf-preview {
    order: 99;
  }
  .gf-preview-frame {
    max-width: 320px;
    margin: 0 auto;
  }
}

@media (max-width: 640px) {
  .gf-card { width: 84px; height: 118px; }
  .gf-card-back { width: 84px; height: 118px; }
}

/* ============================================================================
   v3.30.0 — Goldfish playtester, post-dev-feedback revision
   Fix 1–3 + Add 4–6. All new rules namespaced .gf-*. Token reuse only.
   ============================================================================ */

/* Defense: the legacy static <div id="gf-context-menu"> in the template is
   left in place (no-template constraint); ensure its [hidden] attribute
   actually hides it. Without this, the .gf-context-menu { display: flex }
   rule above would override the UA default [hidden] { display: none },
   which was the empty-menu-husk bug (Fix 2). The active context-menu now
   uses .gf-ctx (dynamic, removed on close); this rule covers the legacy
   element so it never surfaces. */
.gf-context-menu[hidden] { display: none !important; }

/* Dynamic context menu — built on open, removed on close (Fix 2). */
.gf-ctx {
  position: fixed;
  z-index: 1200; /* above .gf-modal (1100) — Fix 3 — so menus opened from
                    inside the browse modal sit above the overlay */
  min-width: 220px;
  background: var(--panel);
  border: 1px solid var(--brand-gold-soft);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  padding: 0.25rem;
  display: flex;
  flex-direction: column;
}
.gf-ctx-item {
  text-align: left;
  padding: 0.4rem 0.6rem;
  border: none;
  background: transparent;
  color: var(--text);
  font: 500 13px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.gf-ctx-item:hover {
  background: var(--brand-gold-soft);
  color: var(--brand-gold);
}

/* Touch-friendly kebab affordance on every card (Add 4). Discoverable
   path to the context menu for users who can't right-click. Hover-revealed
   on pointer-hover viewports; lower-opacity always-visible on touch. */
.gf-kebab {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 22px;
  height: 22px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text);
  border: 1px solid rgba(181, 138, 71, 0.35);
  border-radius: 50%;
  font: 700 14px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.1s, background 0.1s, border-color 0.1s, color 0.1s;
  z-index: 2;
}
.gf-card:hover .gf-kebab,
.gf-card:focus-within .gf-kebab {
  opacity: 1;
}
.gf-kebab:hover {
  background: var(--brand-gold);
  border-color: var(--brand-gold);
  color: var(--brand-near-black);
}
@media (hover: none) {
  .gf-kebab { opacity: 0.65; }
}

/* Cards: kill text-selection so touch long-press doesn't trigger a
   selection bubble before the menu opens (Add 4). */
.gf-card {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Modal overlay click-to-dismiss visual (Add 6 generalized browse). The
   dimmer carries cursor:pointer so the click affordance is discoverable;
   the inner card returns to default. */
.gf-modal { cursor: pointer; }
.gf-modal-card { cursor: auto; }

/* Mana pool widget (Add 5). Floating bottom-right; six pips + Clear. */
.gf-mana-pool {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 900; /* below modal (1100) and menu (1200), above page content */
  background: var(--panel);
  border: 1px solid var(--brand-gold-soft);
  border-radius: var(--radius);
  padding: 0.5rem 0.6rem;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  font: 500 12px/1.3 "Montserrat", system-ui, sans-serif;
  min-width: 240px;
}
.gf-mp-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.4rem;
  gap: 0.5rem;
}
.gf-mp-title {
  font: italic 500 13px/1 "Newsreader", Georgia, serif;
  color: var(--text);
}
.gf-mp-clear {
  padding: 0.15rem 0.4rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: 500 11px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
}
.gf-mp-clear:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-mp-pips {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0.3rem;
}
.gf-mp-pip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0.2rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.gf-mp-sym {
  font: 700 12px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.05em;
}
.gf-mp-pip.gf-mp-w .gf-mp-sym { color: #f5f1d8; }
.gf-mp-pip.gf-mp-u .gf-mp-sym { color: #5a9fff; }
.gf-mp-pip.gf-mp-b .gf-mp-sym { color: #b59cb6; }
.gf-mp-pip.gf-mp-r .gf-mp-sym { color: #ff6b5c; }
.gf-mp-pip.gf-mp-g .gf-mp-sym { color: #7bbf6b; }
.gf-mp-pip.gf-mp-c .gf-mp-sym { color: var(--muted); }
.gf-mp-btns {
  display: flex;
  align-items: center;
  gap: 0.2rem;
}
.gf-mp-val {
  font: 600 13px/1 "Montserrat", system-ui, sans-serif;
  color: var(--text);
  min-width: 1.25rem;
  text-align: center;
}
.gf-mp-pip button {
  width: 18px;
  height: 18px;
  padding: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: 600 12px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
}
.gf-mp-pip button:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}

/* Mana picker popover (opens on a land's untapped→tapped transition).
   Sits between modal (1100) and menu (1200) so a menu opened during
   picker prompt would still be above it (defense in depth). */
.gf-mana-picker {
  position: fixed;
  z-index: 1150;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}
.gf-mana-picker-prompt {
  font: italic 400 12px/1 "Newsreader", Georgia, serif;
  color: var(--muted);
  margin-right: 0.25rem;
}
.gf-mana-picker-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 50%;
  font: 700 13px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
}
.gf-mana-picker-btn:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-mana-picker-btn.gf-mp-w { background: #f5f1d8; color: #211810; border-color: #d4cb9f; }
.gf-mana-picker-btn.gf-mp-u { background: #2c4d8a; color: #f5f1d8; border-color: #5a9fff; }
.gf-mana-picker-btn.gf-mp-b { background: #2a1a2a; color: #f5f1d8; border-color: #b59cb6; }
.gf-mana-picker-btn.gf-mp-r { background: #5a1a1a; color: #f5f1d8; border-color: #ff6b5c; }
.gf-mana-picker-btn.gf-mp-g { background: #1f3f1f; color: #f5f1d8; border-color: #7bbf6b; }
.gf-mana-picker-btn.gf-mp-c { background: var(--panel); color: var(--text); border-color: var(--muted); }
.gf-mana-picker-cancel {
  width: 24px;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: 600 14px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  margin-left: 0.2rem;
}
.gf-mana-picker-cancel:hover {
  border-color: var(--danger);
  color: var(--danger);
}

/* Responsive: mana pool slides to the top on narrow widths so it doesn't
   cover the collapsed bottom rail. */
@media (max-width: 980px) {
  .gf-mana-pool {
    right: 0.5rem;
    bottom: auto;
    top: 0.5rem;
    min-width: 0;
  }
  .gf-mp-pips { grid-template-columns: repeat(6, auto); }
}

/* ============================================================================
   v3.30.1 — Goldfish playtester: fullscreen shell + type-based regions
   Change A — .gf-app overlay (mirrors the v3.28.13 game tracker pattern)
   Change B — five battlefield regions (Creatures / Lands / Artifacts &
              Enchantments / Planeswalkers & Battles / Other)
   All new rules namespaced .gf-*. Token reuse only.
   ============================================================================ */

/* Lock page scroll while the playtester is mounted. Parallel to the
   v3.28.13 tracker's `body.game-mode`. The .gf-app overlay covers the
   sidebar + topbar via position:fixed, so the rest of the app remains
   reachable only by navigating away (the ← back-to-deck chip). */
body.gf-mode { overflow: hidden; }

/* Full-viewport overlay — covers .app-shell, .app-sidebar, .app-topbar
   without modifying them. Same z-index tier as .game-app (500). */
.gf-app {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Ccircle cx='1' cy='1' r='0.6' fill='rgba(181,138,71,0.04)'/%3E%3C/svg%3E");
  background-size: 4px 4px;
  display: flex;
  flex-direction: column;
  color: var(--text);
}

/* Masthead — slim bar with back chip + deck title + Life/Turn stats +
   fullscreen toggle. Parallel to .game-masthead. */
.gf-app-mast {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  min-height: 52px;
}
.gf-mast-left {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}
.gf-mast-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
  font: 700 18px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.gf-mast-back:hover {
  background: var(--brand-gold);
  border-color: var(--brand-gold);
  color: var(--brand-near-black);
}
.gf-mast-title {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}
.gf-mast-eyebrow {
  font: 600 10px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-gold);
}
.gf-mast-deck {
  font: italic 500 16px/1.2 "Newsreader", Georgia, serif;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gf-mast-center {
  display: flex;
  gap: 1rem;
  align-items: center;
  justify-content: center;
}
.gf-mast-stat {
  font: 500 13px/1 "Montserrat", system-ui, sans-serif;
  color: var(--text-dim);
  white-space: nowrap;
}
.gf-mast-stat strong {
  color: var(--text);
  font-weight: 700;
}
/* v3.30.x — Opponent-life stat (B1). Oxblood tint sets it apart from the
   player's own Life so the two readouts are never confused at a glance. */
.gf-mast-stat-opp {
  color: var(--danger);
}
.gf-mast-stat-opp strong {
  color: var(--accent-2);
}
.gf-mast-right {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
}
.gf-mast-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: 700 16px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.gf-mast-btn:hover {
  background: var(--brand-gold-soft);
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}

/* Controls — slim under the masthead; v3.30.0 .gf-controls-panel rule
   styles the row, but we override its container chrome (was a .panel
   with v3.27.7 panel chrome). The fullscreen surface doesn't want a
   panel here; controls float on the overlay background. */
.gf-app .gf-controls-panel {
  flex: 0 0 auto;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  padding: 0.5rem 1rem;
  border-radius: 0;
}
.gf-app .gf-controls-row {
  gap: 0.4rem;
}
.gf-readout-inline {
  margin-left: auto;
  display: inline-flex;
  gap: 1rem;
  align-items: center;
  background: transparent;
  border: none;
  padding: 0;
}
.gf-readout-inline .gf-readout {
  font: italic 400 12px/1.2 "Newsreader", Georgia, serif;
  color: var(--text-dim);
}
.gf-readout-inline .gf-readout strong {
  font-style: normal;
  font-family: "Montserrat", system-ui, sans-serif;
  font-weight: 600;
  color: var(--text);
}

/* Board panel inside .gf-app — flex-grow to fill remaining height; the
   board itself uses CSS grid for the 3-column layout. v3.30.0's
   .gf-board-panel was a side-by-side board+preview grid in a normal
   panel; here we keep the same 1fr/240px columns but flex into the
   fullscreen frame. */
.gf-app .gf-board-panel {
  flex: 1 1 auto;
  background: transparent;
  padding: 0.75rem;
  border: none;
  border-radius: 0;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 240px;
  gap: 0.75rem;
  overflow: hidden;
}
.gf-app .gf-board {
  display: grid;
  grid-template-columns: 160px 1fr 160px;
  gap: 0.75rem;
  min-height: 0;
  height: 100%;
}
.gf-app .gf-board-main {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-height: 0;
}

/* Battlefield — five type-based regions on a CSS grid that gives
   Creatures the dominant area, Lands a wide secondary row, and the
   three smaller regions a 3-column bottom row. Override the v3.30.0
   .gf-battlefield flex-column rule with grid layout. */
.gf-app .gf-battlefield {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  grid-template-rows: 1.5fr 1fr;
  grid-template-areas:
    "creatures creatures pwbattle"
    "lands     artenc    other";
  gap: 0.5rem;
  min-height: 0;
  padding: 0.5rem;
}

.gf-bf-region {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: flex-start;
  align-content: flex-start;
  padding: 1.6rem 0.5rem 0.5rem;
  background: var(--panel-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  min-height: 140px;
  overflow-y: auto;
  transition: border-color 0.15s, background 0.15s;
}
.gf-bf-creatures { grid-area: creatures; }
.gf-bf-lands { grid-area: lands; background: rgba(181, 138, 71, 0.03); }
.gf-bf-artenc { grid-area: artenc; }
.gf-bf-pwbattle { grid-area: pwbattle; }
.gf-bf-other { grid-area: other; }

.gf-bf-region-label {
  position: absolute;
  top: 0.3rem;
  left: 0.5rem;
  font: italic 500 12px/1 "Newsreader", Georgia, serif;
  color: var(--muted);
  letter-spacing: 0.02em;
  user-select: none;
  pointer-events: none;
}
.gf-bf-region-empty .gf-bf-region-label {
  color: var(--text-dim);
}

/* Hand — flex-grow inside the fullscreen frame, dedicated row below
   battlefield. Style overrides keep v3.30.0 chrome but adjust height. */
.gf-app .gf-hand {
  flex: 0 0 auto;
  min-height: 170px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.5rem;
}

/* Rails take the full vertical track in the fullscreen frame. */
.gf-app .gf-rail {
  height: 100%;
  min-height: 0;
}
.gf-app .gf-zone {
  flex: 1 1 0;
  min-height: 0;
}

/* Responsive — at ≤980px the right preview rail drops below the board,
   and the battlefield grid collapses to a single column with regions
   stacked. The .gf-app keeps its overlay nature; only its interior
   reflows. */
@media (max-width: 980px) {
  .gf-app .gf-board-panel {
    grid-template-columns: 1fr;
  }
  .gf-app .gf-board {
    grid-template-columns: 1fr;
  }
  .gf-app .gf-rail {
    flex-direction: row;
    flex-wrap: wrap;
    height: auto;
  }
  .gf-app .gf-zone {
    flex: 1 1 220px;
  }
  .gf-app .gf-battlefield {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    grid-template-areas:
      "creatures"
      "lands"
      "artenc"
      "pwbattle"
      "other";
  }
  .gf-mast-center { display: none; }
  .gf-readout-inline { margin-left: 0; }
}

@media (max-width: 640px) {
  .gf-mast-deck { font-size: 14px; }
  .gf-app .gf-controls-row { flex-wrap: wrap; }
}

/* The legacy v3.30.0 .gf-hero panel + .gf-board-panel as a top-level
   panel are no longer rendered (the template now wraps everything in
   .gf-app and replaces .gf-hero with .gf-app-mast). The old class
   rules in style.css are preserved per the project's retired-class-
   family precedent — they sit dormant under the .gf-app overlay layer
   and have no DOM consumer. */

/* ============================================================================
   v3.30.2 — Collapsible sidebar with hover-expand overlay
   Desktop only (>768px). Mobile (≤768px) hides the sidebar entirely via the
   existing `.app-sidebar { display: none }` rule; this block scopes every
   new behavior inside `@media (min-width: 769px)` so none of it leaks into
   the mobile bottom-tab IA. Token reuse only — Folio palette + existing
   radius/spacing tokens; zero parallel tokens introduced.
   ============================================================================ */

/* Toggle button — sits at the top of the sidebar, above the .sidebar-nav.
   In the expanded state it reads "Collapse"; collapsed it shrinks to icon-
   only via the same "hide every .nav-item > span:not(.nav-badge)" rule
   below. Always discoverable: the user finds it at the top of the rail. */
.sidebar-toggle {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.45rem 0.7rem;
  margin-bottom: 0.5rem;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: 600 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  text-align: left;
  flex: 0 0 auto;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.sidebar-toggle:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.sidebar-toggle:focus-visible {
  outline: 2px solid var(--brand-gold);
  outline-offset: 2px;
}
.sidebar-toggle .nav-icon {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  transition: transform 0.18s ease;
}
.sidebar-toggle-label {
  white-space: nowrap;
}

/* Collapsed state — applies via the FOUC-prevention inline <script> in
   <head> setting `html.sidebar-collapsed` from localStorage before paint.
   The CSS-state cascade follows: html.sidebar-collapsed → .app-shell
   grid track shrinks to a narrow rail; the sidebar element width matches;
   nav-item text + group labels hide; icons center; the trade-pending
   badge stays visible as a small floating pip on its icon. Desktop only. */
@media (min-width: 769px) {
  html.sidebar-collapsed .app-shell {
    grid-template-columns: 60px 1fr;
  }
  html.sidebar-collapsed .app-sidebar {
    /* The grid track is 60px; the .app-sidebar element matches in the
       resting collapsed state. On hover the element grows to 236px past
       its grid track — see the hover-expand rule below — and the grid
       track stays at 60px (no layout reflow). overflow: visible lets
       the expanded panel paint past the rail's right edge; the elevation
       comes from the box-shadow on hover. */
    overflow: visible;
    padding: 1.25rem 0.4rem 1.5rem;
    transition: padding 0.18s ease;
  }
  /* Hide visible text labels (the SVG icon stays). The aria-label that
     the v3.30.2 boot JS sets on every .nav-item keeps screen readers
     correct without any visible-text DOM. */
  html.sidebar-collapsed .nav-label,
  html.sidebar-collapsed .nav-item > span:not(.nav-badge),
  html.sidebar-collapsed .nav-donate > span,
  html.sidebar-collapsed .nav-logout-button .nav-logout-suffix,
  html.sidebar-collapsed .sidebar-toggle-label {
    display: none;
  }
  /* Center the nav-item content around its icon when the label is hidden. */
  html.sidebar-collapsed .nav-item,
  html.sidebar-collapsed .nav-donate {
    justify-content: center;
    padding: 0.55rem 0.5rem;
    position: relative; /* anchor for the floating .nav-badge pip below */
  }
  html.sidebar-collapsed .sidebar-toggle {
    justify-content: center;
    padding: 0.5rem;
  }
  /* Chevron flip — points right (≫) when collapsed, indicating the
     expand affordance. */
  html.sidebar-collapsed .sidebar-toggle .nav-icon {
    transform: rotate(180deg);
  }
  /* Trade-pending badge: pull out of the flex flow and float as a small
     pip in the icon's top-right corner. Stays visible — a pending-action
     signal must survive collapse. */
  html.sidebar-collapsed .nav-badge {
    position: absolute;
    top: 2px;
    right: 4px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    margin-left: 0;
    font-size: 9px;
    line-height: 16px;
    border-radius: 8px;
  }
  /* Group spacing tightens in collapsed view (the .nav-label rows are
     gone, so the visual 1rem gap between groups is too generous). */
  html.sidebar-collapsed .sidebar-nav {
    gap: 0.5rem;
  }
  html.sidebar-collapsed .nav-group {
    gap: 2px;
  }

  /* Hover-expand — fires only while the rail is pinned-collapsed.
     The sidebar element widens from 60px to 236px AS AN OVERLAY: the
     .app-shell grid track stays at 60px, so the main content does NOT
     reflow. The wider sidebar element overflows its grid track to the
     right; z-index lifts it above the main column; the box-shadow
     gives the elevation cue. Both :hover and :focus-within trigger
     the expansion — keyboard parity. */
  html.sidebar-collapsed .app-sidebar:hover,
  html.sidebar-collapsed .app-sidebar:focus-within {
    width: 236px;
    padding: 1.25rem 0.75rem 1.5rem;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
    border-right-color: var(--brand-gold-soft);
    z-index: 60; /* above the topbar's z-index: 50 inside the main column;
                    sticky positioning keeps the sidebar BELOW the topbar
                    vertically (top: 72px), so this z-index governs the
                    horizontal overlay over .app-main only. */
    transition: width 0.18s ease, padding 0.18s ease, box-shadow 0.18s ease;
  }
  /* Re-show labels when the rail is hover-expanded. */
  html.sidebar-collapsed .app-sidebar:hover .nav-label,
  html.sidebar-collapsed .app-sidebar:hover .nav-item > span:not(.nav-badge),
  html.sidebar-collapsed .app-sidebar:hover .nav-donate > span,
  html.sidebar-collapsed .app-sidebar:hover .sidebar-toggle-label,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-label,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-item > span:not(.nav-badge),
  html.sidebar-collapsed .app-sidebar:focus-within .nav-donate > span,
  html.sidebar-collapsed .app-sidebar:focus-within .sidebar-toggle-label {
    display: inline;
  }
  /* Restore the nav-item flex-row layout on hover-expand. */
  html.sidebar-collapsed .app-sidebar:hover .nav-item,
  html.sidebar-collapsed .app-sidebar:hover .nav-donate,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-item,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-donate {
    justify-content: flex-start;
    padding: 0.5rem 0.7rem;
  }
  html.sidebar-collapsed .app-sidebar:hover .sidebar-toggle,
  html.sidebar-collapsed .app-sidebar:focus-within .sidebar-toggle {
    justify-content: flex-start;
    padding: 0.45rem 0.7rem;
  }
  /* Restore the inline-pill nav-badge styling on hover-expand. */
  html.sidebar-collapsed .app-sidebar:hover .nav-badge,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-badge {
    position: static;
    margin-left: auto;
    min-width: 0;
    height: auto;
    line-height: 1;
    /* Let the base .nav-badge rule's font-size, padding, border-radius
       resume; clearing the collapsed overrides above is enough. */
  }
  /* Group spacing restores on hover-expand. */
  html.sidebar-collapsed .app-sidebar:hover .sidebar-nav,
  html.sidebar-collapsed .app-sidebar:focus-within .sidebar-nav {
    gap: 1.1rem;
  }
  html.sidebar-collapsed .app-sidebar:hover .nav-group,
  html.sidebar-collapsed .app-sidebar:focus-within .nav-group {
    gap: 1px;
  }
}

/* End v3.30.2 collapsible-sidebar block. Mobile (≤768px) intentionally
   carries no rule here — the existing `.app-sidebar { display: none }`
   inside the @media (max-width: 768px) block covers mobile entirely, so
   any html.sidebar-collapsed state has no visible effect at narrow
   widths. The bottom-tab IA + More menu is unchanged. */

/* ============================================================================
   v3.30.3 — Goldfish "Draw on new turn" opt-in toggle
   Native checkbox + label sitting in .gf-controls-row adjacent to the
   New Turn button. Folio register; token reuse only.
   ============================================================================ */
.gf-autodraw {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--text-dim);
  font: 500 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.04em;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.12s, color 0.12s;
}
.gf-autodraw:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-autodraw-input {
  width: 14px;
  height: 14px;
  margin: 0;
  cursor: pointer;
  /* accent-color tints the native check to the Folio brass without
     having to render a custom checkbox; modern-browser-only and
     gracefully degrades to UA default. */
  accent-color: var(--brand-gold);
}
.gf-autodraw-input:focus-visible {
  outline: 2px solid var(--brand-gold);
  outline-offset: 2px;
}
.gf-autodraw-label {
  white-space: nowrap;
}

/* ============================================================================
   v3.30.4 — Goldfish hand-hover mana-cost overlay
   Pure desktop hover affordance — a small floating pill of styled mana
   pips that appears above any HAND card on mouseenter/focus. Touch has
   no hover; the side preview pane already serves touch. The overlay is
   a single body-level element repositioned via the card's bounding
   rect (see goldfish.js showHandManaCostOverlay). Token reuse only.
   Color pip palette mirrors the v3.30.0 .gf-mana-picker-btn.gf-mp-*
   treatment so the playtester's mana-symbol idiom stays consistent
   between the mana-pool widget, the mana picker, the text card-face
   fallback, and this overlay.
   ============================================================================ */
.gf-hand-cost-overlay {
  position: fixed;
  z-index: 1180; /* above the mana pool (900) + modal (1100), below
                    .gf-mana-picker (1150) and .gf-ctx menu (1200) */
  display: none;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius-pill);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  /* (left, top) on the element anchors to the card's top-center; the
     translate lifts the pill above the card. */
  transform: translate(-50%, -100%);
  white-space: nowrap;
}
.gf-hand-cost-overlay.gf-hand-cost-show {
  display: inline-flex;
}

/* Individual mana pip — small filled circle with the symbol glyph.
   Reuses the .gf-mana-picker-btn.gf-mp-* palette per the spec's "reuse
   the existing mana-symbol color treatment from .gf-mana-pool /
   .gf-mana-picker" instruction. */
.gf-cost-pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  font: 700 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.02em;
}
.gf-cost-pip.gf-cost-generic {
  background: var(--panel);
  color: var(--text);
  border-color: var(--border);
}
.gf-cost-pip.gf-cost-w { background: #f5f1d8; color: #211810; border-color: #d4cb9f; }
.gf-cost-pip.gf-cost-u { background: #2c4d8a; color: #f5f1d8; border-color: #5a9fff; }
.gf-cost-pip.gf-cost-b { background: #2a1a2a; color: #f5f1d8; border-color: #b59cb6; }
.gf-cost-pip.gf-cost-r { background: #5a1a1a; color: #f5f1d8; border-color: #ff6b5c; }
.gf-cost-pip.gf-cost-g { background: #1f3f1f; color: #f5f1d8; border-color: #7bbf6b; }
.gf-cost-pip.gf-cost-c { background: var(--panel); color: var(--text); border-color: var(--muted); }
.gf-cost-pip.gf-cost-hybrid {
  background: var(--panel-2);
  color: var(--brand-gold);
  border-color: var(--brand-gold);
  font-size: 9px;
}
/* Neutral pip — catches snow ({S}), phyrexian ({W/P}), half-mana,
   and any other rare/unrecognized symbol. Carries the raw text so
   the user can read what was meant; never crashes. */
.gf-cost-pip.gf-cost-n {
  background: var(--panel-2);
  color: var(--text-dim);
  border-color: var(--border);
  font-size: 9px;
}

/* Tweak: when renderManaCost is used inside the text card-face
   fallback (renderFallback's .gf-card-fb-cost line, v3.30.4 onward),
   the pips need a touch less prominence than the hand-hover overlay.
   Same color palette, slightly tighter sizing so they sit inside the
   100×140 card without crowding the type + oracle lines. */
.gf-card-fb-cost .gf-cost-pip {
  min-width: 14px;
  height: 14px;
  font-size: 9px;
  padding: 0 3px;
}

/* ============================================================================
   v3.30.5 — Goldfish counters on battlefield permanents
   Two pieces:
     1. .gf-counter-cluster — on-card pill cluster at the bottom of every
        battlefield card with a non-empty counters map (built by JS in
        buildCardEl, gated on isBattlefieldZone + non-empty counters).
     2. .gf-counter-panel — floating editor opened from the per-card
        context menu's "Counters…" item. Single panel state, anchored
        near the card, same lifecycle pattern as .gf-mana-picker.
   Both routed through adjustCounter() — single mutation site. Tracker
   styling is a REFERENCE only; this CSS does not import .tc-* rules.
   Token reuse only — Folio palette + existing radius/spacing scales;
   zero parallel tokens.
   ============================================================================ */

/* On-card pill cluster — sits inside the card's bottom strip (.gf-card
   has overflow: hidden from v3.30.0, so the cluster has to fit within
   the card bounds). Translucent dark backdrop keeps the chips legible
   over either the Scryfall art OR the text card-face fallback. */
.gf-counter-cluster {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px 4px;
  background: rgba(8, 19, 33, 0.78);
  /* z-index above the card art but below the kebab (which is at
     z-index: 2 per v3.30.0). Cluster is decorative; mouse events
     pass through to the card itself via pointer-events:none so the
     user can still drag / click / tap the card behind the chips. */
  z-index: 1;
  pointer-events: none;
}
.gf-counter-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px 5px;
  min-height: 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  font: 700 9px/1.1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
/* Distinct chip color for the two most-common Commander counters
   so a glance reads +1/+1 immediately. -1/-1 picks up the oxblood
   register so it's unmistakably a debuff. Other curated labels
   share the neutral chip. */
.gf-counter-chip.gf-counter-plus {
  background: rgba(106, 130, 83, 0.88); /* var(--brand-green) tinted */
  border-color: var(--brand-green);
  color: var(--text);
}
.gf-counter-chip.gf-counter-minus {
  background: rgba(138, 46, 34, 0.88); /* var(--danger) tinted */
  border-color: var(--danger);
  color: var(--text);
}
.gf-counter-chip.gf-counter-loy {
  background: rgba(181, 138, 71, 0.88); /* var(--brand-gold) tinted */
  border-color: var(--brand-gold);
  color: var(--brand-near-black);
}
/* v3.30.13 — pill-click-to-adjust fast path. Cluster wrapper still
   carries `pointer-events: none` so clicks on the bare backdrop
   between chips pass through to the card; per-chip override here
   makes the chip itself the click target. Subtle hover affordance
   tells the user it's interactive — outline-style border brighten
   keeps the chip's color tint intact (so a +1/+1 hover still reads
   green, a -1/-1 hover still reads oxblood). */
.gf-counter-chip {
  pointer-events: auto;
  cursor: pointer;
  transition: border-color 0.12s, filter 0.12s;
}
.gf-counter-chip:hover {
  filter: brightness(1.15);
}
.gf-counter-chip:focus-visible {
  outline: 2px solid var(--brand-gold);
  outline-offset: 1px;
}

/* Floating editor panel — opened by "Counters…" from the per-card
   context menu. Same lifecycle idiom as .gf-mana-picker (single
   panel state, body-level appended, document-click + Escape
   dismissed). */
.gf-counter-panel {
  position: fixed;
  z-index: 1190; /* above modal (1100) + .gf-hand-cost-overlay (1180),
                    below context menu (1200) — so the panel does NOT
                    block a context menu opened from inside it (none
                    of the panel's rows open another menu today, but
                    keep z-index sensible). */
  min-width: 240px;
  background: var(--panel);
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  padding: 0.45rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.gf-counter-panel-head {
  font: italic 500 12px/1 "Newsreader", Georgia, serif;
  color: var(--brand-gold);
  letter-spacing: 0.04em;
  padding: 0.1rem 0.2rem 0.25rem;
  border-bottom: 1px solid var(--border);
}
.gf-counter-panel-body {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.gf-counter-row {
  display: grid;
  grid-template-columns: 24px 1fr 28px 24px;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.1rem;
}
.gf-counter-row-label {
  font: 500 12px/1.2 "Montserrat", system-ui, sans-serif;
  color: var(--text);
}
.gf-counter-row-val {
  font: 700 13px/1 "Montserrat", system-ui, sans-serif;
  color: var(--text);
  text-align: center;
  min-width: 24px;
}
.gf-counter-btn {
  width: 22px;
  height: 22px;
  padding: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-sm);
  font: 700 13px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  transition: border-color 0.1s, color 0.1s, background 0.1s;
}
.gf-counter-btn:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-counter-btn:focus-visible {
  outline: 2px solid var(--brand-gold);
  outline-offset: 2px;
}
.gf-counter-custom-btn {
  margin-top: 0.2rem;
  padding: 0.35rem 0.55rem;
  background: var(--panel-2);
  border: 1px dashed var(--brand-gold-soft);
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  font: italic 500 12px/1 "Newsreader", Georgia, serif;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.gf-counter-custom-btn:hover {
  border-style: solid;
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}

/* ============================================================================
   v3.30.6 — Goldfish equipment/aura attachment fan
   `attachedTo` is a render pointer — children stay in their own classify-
   Region battlefield array. Render nests them under the host inside a
   .gf-card-group flex item. Host sits at the top of the group; children
   stack beneath with --gf-attach-index-driven offsets. CSS variables let
   the responsive breakpoint adjust the stride in ONE place. Token reuse
   only — Folio palette + existing radius/spacing scales.
   ============================================================================ */

/* Default-viewport card dimensions are 100w × 140h (v3.30.0 .gf-card).
   The fan stride is 32px — child N's top edge sits at 30px + 32*N, so
   each subsequent child shows a 32px sliver under the previous layer.
   Visual cap: ~9 children before the host's bottom strip is fully
   covered, but goldfishing rarely sees more than 2–3 auras on one
   permanent. */
.gf-card-group {
  position: relative;
  display: inline-block;
  width: 100px;
  /* min-height reserves space in the flex-wrap row so neighboring
     cards in the region don't overlap the fan. --gf-attach-count is
     set inline by the renderer; the default 0 keeps the group at the
     bare host height. */
  --gf-attach-count: 0;
  min-height: calc(140px + 32px * var(--gf-attach-count));
  vertical-align: top;
}
.gf-card-group .gf-card-host {
  position: relative;
  z-index: 10;
}
.gf-card-group .gf-attached-child {
  position: absolute;
  left: 0;
  /* --gf-attach-index is set inline per child (0, 1, 2, …); the
     30px base offset overlaps the host by 110px so the host card
     reads as the dominant element with the child's lower strip
     peeking out below. Each subsequent child adds 32px so the fan
     reads as a stacked deck of auras / equipment under the host. */
  --gf-attach-index: 0;
  top: calc(30px + 32px * var(--gf-attach-index));
  /* z-index is set inline (10 - i) so each successive child sits
     a layer further back. */
}

/* Responsive — match the v3.30.0 ≤640px card-size reduction
   (.gf-card → 84w × 118h). Fan stride scales proportionally so the
   visual offset reads the same on phone widths. */
@media (max-width: 640px) {
  .gf-card-group {
    width: 84px;
    min-height: calc(118px + 28px * var(--gf-attach-count));
  }
  .gf-card-group .gf-attached-child {
    top: calc(26px + 28px * var(--gf-attach-index));
  }
}

/* ============================================================================
   v3.30.7 — Goldfish custom token creation
   Two pieces:
     1. .gf-create-token-form — JS-built form inside the existing .gf-modal
        when state.modalContext.kind === "create-token". Reuses .gf-mp-*
        from v3.30.0 mana picker for the color toggle pills so the mana-
        symbol idiom stays consistent.
     2. .gf-pt-badge — small token-only pill in the card's top-left
        showing power/toughness; rendered by buildCardEl when isToken
        AND (card.power || card.toughness) is non-empty.
   Token reuse only — Folio palette + existing radius/spacing scales;
   zero parallel tokens.
   ============================================================================ */

/* Create-token form */
.gf-create-token-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 320px;
}
.gf-ctf-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.gf-ctf-label {
  flex: 0 0 90px;
  font: 500 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.gf-ctf-input {
  flex: 1 1 auto;
  padding: 0.35rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-sm);
  font: 500 13px/1.2 "Montserrat", system-ui, sans-serif;
  min-width: 0;
}
.gf-ctf-input:focus {
  outline: none;
  border-color: var(--brand-gold);
}
.gf-ctf-pt-row .gf-ctf-pt {
  flex: 0 0 56px;
  text-align: center;
}
.gf-ctf-pt-sep {
  color: var(--text-dim);
  font: 500 16px/1 "Newsreader", Georgia, serif;
}
.gf-ctf-colors-row .gf-ctf-colors {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
}
/* Color toggle pills — reuse .gf-mana-picker-btn.gf-mp-* palette
   verbatim from v3.30.0 so the mana-symbol idiom stays consistent.
   Default opacity 0.55 reads as "not selected"; aria-pressed=true +
   .gf-ctf-color-on lifts to full opacity + brass border. */
.gf-ctf-color {
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  font: 700 12px/1 "Montserrat", system-ui, sans-serif;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.12s, border-color 0.12s;
}
.gf-ctf-color.gf-mp-w { background: #f5f1d8; color: #211810; border-color: #d4cb9f; }
.gf-ctf-color.gf-mp-u { background: #2c4d8a; color: #f5f1d8; border-color: #5a9fff; }
.gf-ctf-color.gf-mp-b { background: #2a1a2a; color: #f5f1d8; border-color: #b59cb6; }
.gf-ctf-color.gf-mp-r { background: #5a1a1a; color: #f5f1d8; border-color: #ff6b5c; }
.gf-ctf-color.gf-mp-g { background: #1f3f1f; color: #f5f1d8; border-color: #7bbf6b; }
.gf-ctf-color.gf-mp-c { background: var(--panel); color: var(--text); border-color: var(--muted); }
.gf-ctf-color:hover { opacity: 0.85; }
.gf-ctf-color.gf-ctf-color-on {
  opacity: 1;
  border-color: var(--brand-gold);
  box-shadow: 0 0 0 1px var(--brand-gold);
}
.gf-ctf-oracle-row {
  align-items: flex-start;
}
.gf-ctf-oracle {
  resize: vertical;
  min-height: 44px;
  font-family: "Montserrat", system-ui, sans-serif;
}
.gf-ctf-actions {
  justify-content: flex-end;
  margin-top: 0.4rem;
  gap: 0.4rem;
}

/* Token-only P/T badge — sits absolute at the card's top-left, brass-
   bordered, monospace digits read at a glance. .gf-card already has
   `overflow: hidden` from v3.30.0; the badge stays inside the card. */
.gf-pt-badge {
  position: absolute;
  top: 2px;
  left: 2px;
  z-index: 3; /* above .gf-card-img (z-auto) and the v3.30.5 counter
                 cluster's pointer-events:none backdrop; below the
                 .gf-kebab (z-index: 2) — actually equal to kebab's
                 stacking context but they don't overlap (kebab is
                 top-right, badge is top-left). */
  min-width: 28px;
  padding: 2px 6px;
  background: rgba(8, 19, 33, 0.82);
  color: var(--text);
  border: 1px solid var(--brand-gold);
  border-radius: var(--radius-pill);
  font: 700 11px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.02em;
  text-align: center;
  pointer-events: none;
}

/* ============================================================================
   v3.30.8 — Goldfish deck-token quick-add buttons
   Surfaces the user's curated DeckTokenRequirement rows for the loaded deck
   as one-click "+ NAME × QTY" buttons next to the v3.30.7 Create token
   control. Each click feeds a v3.30.7-shaped spec into the EXISTING
   createToken() — single construction site preserved. Token reuse only.
   ============================================================================ */
.gf-quick-tokens {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem;
  padding-left: 0.5rem;
  border-left: 1px solid var(--border);
  margin-left: 0.2rem;
}
.gf-quick-tokens-label {
  font: italic 400 11px/1 "Newsreader", Georgia, serif;
  color: var(--text-dim);
  letter-spacing: 0.02em;
  padding-right: 0.2rem;
}
/* Quick-add pill — sized smaller than .gf-btn-primary, brass-soft border
   that lights up on hover. Reuses the .gf-btn base shape for letter-spacing
   + radius consistency with the surrounding controls; the .gf-quick-token-
   btn modifier overrides padding + accent. */
.gf-quick-token-btn {
  padding: 0.3rem 0.6rem;
  background: var(--panel-2);
  border: 1px solid var(--brand-gold-soft);
  color: var(--text);
  font: 600 12px/1 "Montserrat", system-ui, sans-serif;
  letter-spacing: 0.02em;
  white-space: nowrap;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.gf-quick-token-btn:hover {
  background: var(--panel);
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
.gf-quick-token-btn:focus-visible {
  outline: 2px solid var(--brand-gold);
  outline-offset: 2px;
}

/* ============================================================================
   v3.30.9 — Deck-detail "Tokens Needed" auto-token suggestions
   Read-side feature surfacing the per-deck panels-cache token list (the
   same data the v3.8.9 "Tokens" panel renders) as one-click "+ Track"
   suggestion rows inside the "Tokens Needed" panel. Pure presentation;
   no parallel design tokens.
   ============================================================================ */
.token-suggestions {
  margin-top: 0.6rem;
  padding: 0.6rem 0.75rem;
  background: var(--panel-2);
  border: 1px solid var(--brand-gold-soft);
  border-radius: var(--radius-sm);
}
.token-suggestions-label {
  font: italic 400 12px/1.4 "Newsreader", Georgia, serif;
  color: var(--text-dim);
  margin: 0 0 0.4rem 0;
}
.token-suggestions-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.token-suggestion-row {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.token-suggestion-name {
  font-weight: 600;
  color: var(--text);
}
.token-suggestion-type {
  font: italic 400 11px/1 "Newsreader", Georgia, serif;
  color: var(--muted);
}
.token-suggestion-form {
  display: inline-flex;
  margin: 0;
}
.token-suggestion-track {
  padding: 0.2rem 0.55rem;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.token-suggestion-track:hover {
  border-color: var(--brand-gold);
  color: var(--brand-gold);
}
