/* Public /market explorer (P4, ahg-macqj). Dedicated stylesheet — same
   convention as dashboard.css/pull.css: a per-page-family CSS file linked
   via asset_path, not an inline <style> block (view-conventions ratchet).
   Vault Industrial palette per DESIGN.md; amber reserved for the primary
   series / key CTA, everything else defers. */

/* Full-bleed shell to match the dashboard app shell and /collect: the page
   spans the viewport (capped at 2000px, the same ceiling as the global
   `.container` and collect's `.qa-app`) so the logo, nav, and content align
   top-left instead of sitting indented inside a narrow 1100px column. The
   32px horizontal padding matches `.container`'s so the logo lands at the
   same left offset as every other signed-in page. */
.mkt-page {
  max-width: 2000px;
  margin: 0 auto;
  padding: 24px 32px 64px;
}

/* Zero the global 32px body padding (application.html.erb sets it) so it does
   NOT stack with `.mkt-page`'s own 32px -- that double gutter pushed the logo
   and content to 64px from the edge, far more inset than the dashboard and
   /collect. Collect does the same reset (`body.qa-collect { padding: 0 }`);
   now `.mkt-page` is the sole gutter, landing content at 32px like the app
   shell. */
body.mkt-body {
  padding: 0;
}

/* The app shell's global `.container` (application.html.erb) wraps every
   page in a padded, rounded, bordered card -- right for the dashboard's
   dense tool chrome, wrong for a public explorer that should read as a page
   on the body's own dark gradient, not a widget floating inside one more
   box. Scoped to `body.mkt-body` (set via content_for :body_class on every
   market/*.html.erb) so nothing outside this page family is affected. */
body.mkt-body .container {
  max-width: none;
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
  min-height: 0;
}

.mkt-subtitle {
  font-family: 'Cabinet Grotesk', sans-serif;
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 24px 0 12px;
}

.mkt-empty {
  font-size: 14px;
  color: var(--text-muted);
  padding: 12px 0;
}
/* Preorder notice on an unreleased set page -- a light amber-tinted status
   note (amber is the sanctioned accent), explaining why every product shows
   "—" and the index hasn't started. */
.mkt-preorder {
  font-size: 14px;
  color: var(--text-secondary);
  padding: 12px 14px;
  margin: 4px 0 0;
  background: rgba(245, 158, 11, 0.08);
  border: 1px solid rgba(245, 158, 11, 0.28);
  border-radius: var(--radius-md);
}

.mkt-delta-pos { color: #4ade80; }
.mkt-delta-neg { color: #f87171; }
.mkt-delta-flat { color: var(--text-muted); }

/* Fixed-scope drilldown stat strip (/market/:game, /market/:game/:set --
   ahg-85hzm). Server-rendered (Market::SeriesSummary is computed at request
   time, not client-fetched -- no reason to wait on JS for numbers the
   controller already has). A plain label/value row, not a boxed card grid:
   DESIGN.md's "tables are the hero, no widgets competing with data" applies
   here too -- these are headline numbers, not a dashboard tile, so no
   .mkt-card-style box is reintroduced for them. */
.mkt-page .stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin: 20px 0;
  padding: 16px 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}
.mkt-page .stat { min-width: 96px; }
.mkt-page .stat-label {
  font-size: 14px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 4px;
}
.mkt-page .stat-value {
  font-family: 'Geist Mono', monospace;
  font-size: 22px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
/* ahg-2npqv: "N of M priced" coverage caption under a stat value. */
.mkt-page .stat-sub {
  font-size: 14px;
  color: var(--text-muted);
  margin-top: 2px;
}
/* ahg-e8axv: logged-in seller's "Your position" panel (async), reuses .stats. */
.mkt-page .mkt-your-position { margin-top: 20px; }
.mkt-page .mkt-your-position .mkt-subtitle { margin-bottom: 8px; }

.mkt-table-wrap {
  overflow-x: auto;
  overflow-y: hidden;
}

.mkt-table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
}

.mkt-table th {
  /* 14px horizontal matches td so every header sits directly over its column;
     numeric headers right-align to meet their right-aligned numeric cells
     (they were left-aligned, floating left of the numbers). */
  padding-left: 14px;
  padding-right: 14px;
  text-align: left;
  font-size: 14px;
}
.mkt-table th.mkt-num { text-align: right; }

.mkt-table td {
  padding: 11px 14px;
  font-size: 14px;
  border-top: 1px solid var(--border-subtle);
}

.mkt-table td.mkt-num {
  font-family: 'Geist Mono', monospace;
  text-align: right;
}

.mkt-table a {
  color: var(--text-primary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}

/* Sets table search (ahg-85hzm follow-up): plain text input, filters
   already-rendered rows client-side via data-set-name -- no round trip. */
.mkt-sets-search { margin-bottom: 10px; }
.mkt-sets-search-input {
  width: 100%;
  max-width: 320px;
  min-height: 44px;
  padding: 0 14px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: inherit;
  font-size: 16px; /* iOS zoom floor */
}
.mkt-sets-search-input::placeholder { color: var(--text-muted); }

/* Set icon + name, one cell -- the icon is decorative (alt="") since the
   link text already names the set. Placeholder is a plain hairline-bordered
   swatch, not a Magic-only guessed URL: set_icon_url is null for every
   game besides Magic/Pokemon today (ahg-85hzm), so a missing icon is the
   COMMON case, not an edge case. */
.mkt-set-cell { display: flex; align-items: center; gap: 8px; }
.mkt-set-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  object-fit: contain;
}
.mkt-set-icon--placeholder {
  /* A filled tile, NOT a bordered empty square -- the old border + empty
     interior read as an unchecked checkbox and invited clicks that did
     nothing. This is a decorative "no set logo" placeholder (aria-hidden),
     so it must not look like a control. */
  border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--s2), var(--s3));
}

.mkt-buy-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 6px;
  background: var(--accent-amber, #f59e0b);
  color: #0c0a09;
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
}

/* /market/:game/:set card roster (ahg-i62pa) -- the forward door into each
   card's own /cards/:product_id page. Mobile-first list rows; the whole row
   is the tap target (min 44px). */
.mkt-roster { margin: 24px 0 8px; }
.mkt-roster-hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.mkt-roster-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}
.mkt-roster-note { font-size: 13px; color: var(--text-muted); }
.mkt-roster-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border-subtle);
}
.mkt-roster-item { border-bottom: 1px solid var(--border-subtle); }
.mkt-roster-link {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 8px 4px;
  text-decoration: none;
  color: inherit;
}
.mkt-roster-link:hover { background: var(--bg-secondary); }
.mkt-roster-thumb {
  width: 32px;
  height: 44px;
  object-fit: cover;
  border-radius: 4px;
  flex-shrink: 0;
  background: var(--bg-secondary);
}
.mkt-roster-thumb--empty { border: 1px solid var(--border-subtle); }
.mkt-roster-info { flex: 1; min-width: 0; }
.mkt-roster-name {
  display: block;
  font-size: 14px;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mkt-roster-meta { display: block; font-size: 12px; color: var(--text-muted); }
.mkt-roster-price {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  flex-shrink: 0;
  padding-left: 8px;
}

/* /market/:game/:set "Compare to" picker (ahg-85hzm follow-up) -- same
   search/dropdown/pick-row shape as /market/compare's rail (.rail-search,
   .mkt-compare-search-input, .mkt-compare-results, .pick/.pick-row are
   already unscoped or reused below), just a smaller inline block instead
   of a full rail column. */
.mkt-compare-picker { margin: 20px 0 16px; max-width: 420px; }
.mkt-compare-label {
  display: block;
  font-size: 14px;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.mkt-compare-picker .rail-search { position: relative; margin-bottom: 8px; }
/* Same 3-column shape as .mkt-page.mkt-compare .pick-row (name + remove,
   no rank column -- this is "what you're comparing to", not a ranked
   leaderboard) without requiring the whole page to carry a .mkt-compare
   class it doesn't otherwise need. */
.mkt-compare-picker .pick { border-top: 0; }
.mkt-compare-picker .pick-row {
  grid-template-columns: 16px 1fr auto;
  cursor: default;
}
.mkt-compare-picker a.pick-name {
  align-self: stretch;
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-primary);
  text-decoration: none;
  cursor: pointer;
}
.mkt-compare-picker a.pick-name:hover { text-decoration: underline; }
.mkt-compare-picker .pick-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.mkt-compare-picker .pick-remove:hover { color: var(--text-primary); }

@media (max-width: 768px) {
  .mkt-page { padding: 20px 16px 48px; }
}

@media (max-width: 480px) {
  .mkt-page .stats { gap: 16px 20px; }
}

/* ---- P2 split-brain overview (ahg-3f3pl): .bar / .seg / .pick / .panel /
   .chart / .legend / .note / .split, lifted from the approved variant-c
   wireframe (~/.gstack/projects/tryhoard-hoard/designs/market-leaderboard-
   20260804/_shared.css + variant-c-splitbrain.html). Token names translated
   to this app's existing :root custom properties (app/views/layouts/
   application.html.erb) instead of redefining duplicate tokens under the
   wireframe's names -- --surface-1/2/3 == --bg-secondary/--s2/--s3,
   --hairline == --border-subtle, --accent == --accent-amber, --success/
   error/info == --accent-green/rose/blue: every one an exact hex/rgba
   match, confirmed against DESIGN.md before porting. ahg-85hzm moved
   /market/:game and /market/:game/:set onto this same .bar/.panel/.chart
   family (they used to run the P4 .mkt-card/.mkt-chart-wrap/.mkt-stats/
   .mkt-grid look with a fixed-viewBox chart) -- only the .mkt-empty/
   .mkt-subtitle/.mkt-table/.mkt-buy-btn/.mkt-compare-form rules above are
   still theirs, none of it a chart. Scoped under .mkt-page so nothing here
   leaks past the /market page family (market.css only loads there, via
   head_extra). */

/* Minimal cross-page header (ahg-lhae1, market/_header partial): logo + a
   way back to the dashboard for signed-in users who land on /market from
   the app. Logged-out visitors see only the logo -- shared/_logo is already
   auth-aware and routes to '/' for them, so it doubles as their way home.
   Sits above .bar so it never competes with the page's own toolbar. */
.mkt-page .mkt-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 16px;
  margin-bottom: 24px;
}
.mkt-page .mkt-topbar-left {
  display: flex;
  align-items: center;
  gap: 16px;
}
/* Both the logo anchor (rendered by shared/_logo, no class of its own) and
   the back link are direct children -- one rule gives both a real 44px tap
   target without reaching into the shared partial. */
.mkt-page .mkt-topbar-left > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.mkt-page .mkt-topbar-back {
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.mkt-page .mkt-topbar-back:hover { color: var(--text-primary); }

/* Right-hand cluster (Condition guide / Compare sets / theme toggle):
   pushed to the far edge by the parent's justify-content: space-between --
   one visual cluster ("utility nav") distinct from the "way back" links on
   the left, instead of 5 items packed into one row. */
.mkt-page .mkt-topbar-nav {
  display: flex;
  align-items: center;
  gap: 20px;
}
.mkt-page .mkt-topbar-link {
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-amber);
  text-decoration: none;
}
.mkt-page .mkt-topbar-link:hover { color: #fbbf24; }
.mkt-page .mkt-topbar-nav .theme-toggle { flex-shrink: 0; }

@media (max-width: 768px) {
  .mkt-page .mkt-topbar { margin-bottom: 14px; row-gap: 10px; }
}

.mkt-page .bar {
  display: flex;
  align-items: center;
  gap: 14px 24px;
  flex-wrap: wrap;
  min-height: 44px;
  padding-bottom: 14px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.mkt-page .bar-title {
  font-family: 'Cabinet Grotesk', sans-serif;
  font-weight: 700;
  font-size: 21px;
  line-height: 1.1;
  color: var(--text-primary);
  margin: 0;
  letter-spacing: -0.01em;
  /* No nowrap here (ahg-85hzm): overview/compare's short "Market" / "Market
     / Compare" titles never wrapped anyway, but the game/set drilldown
     breadcrumb ("Market / <game> / <set>") can run long -- a set name alone
     can exceed a 375px viewport, and nowrap would force horizontal overflow
     on the whole page instead of just wrapping the heading. */
  overflow-wrap: break-word;
}
/* Breadcrumb drill-up links ("Market / Pokemon / <set>" -- Market and
   Pokemon are real links, the final segment is the page you're on and
   stays plain). Without this they inherited .bar-title's bold heading
   color with no hover cue, so a clickable segment looked identical to the
   inert one. Same dotted-underline/muted-to-primary treatment as
   .mkt-topbar-back and .bar-link, so every "step up a level" affordance in
   this page family reads the same way. */
.mkt-page .bar-up {
  color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.mkt-page .bar-up:hover { color: var(--text-primary); border-bottom-color: var(--text-secondary); }
.mkt-page .bar-asof {
  order: 9;
  margin-left: auto;
  font-size: 14px;
  color: var(--text-muted);
  white-space: nowrap;
  font-family: 'Geist Mono', monospace;
}
.mkt-page .bar-break { display: none; }

/* Discoverability link from the overview to /market/compare (ahg-eqatd) --
   same minimal underline treatment as .mkt-breadcrumb a, but sized as a
   real 44px tap target for mobile (project rule: no sub-44px targets). */
.mkt-page .bar-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.mkt-page .bar-link:hover { color: var(--text-primary); }

/* segmented controls -- shared by the toolbar's track switch (server-round-
   trip links) and the chart panel's range switch (buttons, static in P2). */
.mkt-page .seg {
  display: inline-flex;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 3px;
  gap: 2px;
}
.mkt-page .seg a,
.mkt-page .seg button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 14px;
  padding: 0 16px;
  min-height: 44px; /* DESIGN.md tap-target floor, not 38 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: background 100ms ease-in-out, color 100ms ease-in-out;
}
.mkt-page .seg a:hover,
.mkt-page .seg button:hover { background: var(--s2); }
.mkt-page .seg a[aria-pressed='true'],
.mkt-page .seg button[aria-pressed='true'] {
  background: var(--accent-amber);
  color: #1c1310;
  font-weight: 600;
}
/* Range control (30d/90d/1y): an amber-TINT active state -- a deliberate brand
   touch on the chart panel (DESIGN.md: segmented controls carry amber), kept
   quieter than the primary track control's full amber fill so amber stays
   rationed. Zero amber on the panel was the design-review deviation. */
.mkt-page .seg--quiet a[aria-pressed='true'],
.mkt-page .seg--quiet button[aria-pressed='true'] {
  background: var(--amber-tint, rgba(245, 158, 11, 0.14));
  color: var(--accent-amber);
  font-weight: 600;
}
.mkt-page .bar .seg { padding: 2px; }
.mkt-page .bar .seg a,
.mkt-page .bar .seg button { min-height: 40px; padding: 0 14px; }

/* master-detail split: leaderboard rail + chart panel */
.mkt-page .split {
  display: grid;
  grid-template-columns: 380px 1fr;
  gap: 24px;
  align-items: start;
  margin-bottom: 24px;
}

/* left rail: leaderboard doubles as the chart series picker (toggle wiring
   is Phase 3 -- P2 renders the correct static aria-pressed/swatch state). */
/* Frame the rail as a card matching the chart .panel, so the leaderboard and
   the chart read as one matched pair instead of two unrelated components
   (design review: rail had no container, just floating hairlines). */
.mkt-page .pick {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.mkt-page .pick-row {
  display: grid;
  /* minmax(0,1fr) on the name lets it shrink so the value/delta column always
     keeps its width -- a plain 1fr has a min-content floor that pushed a long
     "$398,995.34" past the framed rail's clipped edge on a narrow column. */
  grid-template-columns: 44px 22px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px;
  width: 100%;
  min-height: 52px;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  color: inherit;
  font: inherit;
  text-align: left;
  transition: background 100ms ease-in-out;
}
.mkt-page .pick-row:hover { background: var(--bg-secondary); }
.mkt-page .pick-row[aria-pressed='true'] { background: var(--s2); }
.mkt-page .pick-row--empty { color: var(--text-muted); }

/* Chart on/off toggle -- the checkbox-style control (the swatch reads as a
   checked/unchecked box). 44px hit target per DESIGN.md; the 12px swatch
   stays centered inside it. */
.mkt-page .pick-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  min-height: 44px;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: inherit;
}
.mkt-page .pick-toggle:disabled { cursor: default; }
.mkt-page .pick-toggle:disabled .swatch { opacity: 0.5; }

/* Drill-in name link: goes to the game's set list (/market/:game). The
   trailing chevron is a persistent "opens a page" affordance (not hover-only)
   -- mirrors the icon-presence-signals-navigation rule in DESIGN.md. */
/* The name is the drill-in tap target -- stretch it to the full row height so
   the hit area clears the 44px minimum (the row is ~52px; a baseline inline
   <a> was only ~24px tall, failing the mobile tap-target rule). */
.mkt-page a.pick-name {
  display: flex;
  align-items: center;
  align-self: stretch;
  min-width: 0; /* allow the name column to shrink so the value never clips */
  min-height: 44px;
  text-decoration: none;
  cursor: pointer;
}
.mkt-page a.pick-name::after {
  content: "\203A"; /* › -- single right angle quote */
  margin-left: 5px;
  color: var(--text-muted);
  font-weight: 400;
}
.mkt-page a.pick-name:hover { text-decoration: underline; }
.mkt-page .pick-row--empty a.pick-name { color: var(--text-muted); }
/* Keyboard focus lands on the two real controls (toggle + drill-in name), not
   the row -- give each its own amber focus ring (was outline:none territory). */
.mkt-page .pick-toggle:focus-visible,
.mkt-page a.pick-name:focus-visible {
  outline: 2px solid var(--accent-amber);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}
.mkt-page .swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1.5px solid var(--text-muted);
  background: transparent;
}
/* Categorical series palette -- the dataviz reference order, validated for
   adjacent-pair CVD + normal-vision separation on BOTH --bg-secondary
   surfaces (#1c1917 dark / #f5f5f4 light) in both modes. Dark steps are the
   default (dark is the app default); [data-theme="light"] swaps in the light
   steps. Kept as custom properties so the chart lines (.mkt-s0..7 -> stroke
   currentColor), legend dots, crosshair dots, and rail swatches all resolve
   ONE source and theme-swap together. App chrome (range tabs, buttons) stays
   amber; only the data series wear these hues. The light-mode <3:1 contrast
   WARN on some steps is relieved by the always-present legend labels +
   crosshair tooltip -- identity is never color-alone. */
.mkt-page {
  --series-0: #3987e5; --series-1: #d95926; --series-2: #199e70; --series-3: #c98500;
  --series-4: #d55181; --series-5: #008300; --series-6: #9085e9; --series-7: #e66767;
}
[data-theme="light"] .mkt-page {
  --series-0: #2a78d6; --series-1: #eb6834; --series-2: #1baf7a; --series-3: #eda100;
  --series-4: #e87ba4; --series-5: #008300; --series-6: #4a3aa7; --series-7: #e34948;
}
.mkt-page .mkt-s0 { color: var(--series-0); }
.mkt-page .mkt-s1 { color: var(--series-1); }
.mkt-page .mkt-s2 { color: var(--series-2); }
.mkt-page .mkt-s3 { color: var(--series-3); }
.mkt-page .mkt-s4 { color: var(--series-4); }
.mkt-page .mkt-s5 { color: var(--series-5); }
.mkt-page .mkt-s6 { color: var(--series-6); }
.mkt-page .mkt-s7 { color: var(--series-7); }
.mkt-page .swatch--0 { background: var(--series-0); border-color: transparent; }
.mkt-page .swatch--1 { background: var(--series-1); border-color: transparent; }
.mkt-page .swatch--2 { background: var(--series-2); border-color: transparent; }
.mkt-page .swatch--3 { background: var(--series-3); border-color: transparent; }
.mkt-page .swatch--4 { background: var(--series-4); border-color: transparent; }
.mkt-page .swatch--5 { background: var(--series-5); border-color: transparent; }
.mkt-page .swatch--6 { background: var(--series-6); border-color: transparent; }
.mkt-page .swatch--7 { background: var(--series-7); border-color: transparent; }
.mkt-page .pick-rank,
.mkt-page .pick-val,
.mkt-page .pick-d30 {
  font-family: 'Geist Mono', monospace;
  font-variant-numeric: tabular-nums; /* DESIGN.md: numeric cells align */
}
.mkt-page .pick-rank { color: var(--text-muted); font-size: 14px; }
.mkt-page .pick-name { color: var(--text-primary); font-size: 15px; }
/* min-height reserves room for the 2-line case (value + 30d change) so an
   insufficient-data row's single "Not enough market data" line is
   vertically centered to match instead of coming out shorter than every
   row around it (ahg-nbi70). */
.mkt-page .pick-right {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 44px;
  text-align: right;
}
.mkt-page .pick-val,
.mkt-page .pick-d30 { display: block; font-size: 14px; }
.mkt-page .pick-empty { font-size: 14px; color: var(--text-muted); }
/* ahg-nbi70: reverted ahg-2npqv's "N% of products priced" from visible
   .pick-cov text back to hover-only (the row's title= attribute) -- an
   unexplained percentage only some rows had read as clutter, not a useful
   caveat, and was the root cause of the jagged rail this bead's min-height
   fix above addresses more directly. .pick-cov itself is gone; the game/set
   drill-down's "Tracked products... of M priced" caption (_tracked_products
   _stat.html.erb) is a separate, more contextual disclosure and is untouched. */

/* right: the chart is the point */
.mkt-page .panel {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px;
  position: relative; /* offset anchor for the absolutely-positioned .chart-tip */
}
.mkt-page .panel-hd { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.mkt-page .panel-title { color: var(--text-primary); font-size: 17px; font-weight: 500; margin: 0; }
.mkt-page .panel-note { color: var(--text-muted); font-size: 14px; font-weight: 400; margin-left: 8px; }
.mkt-page .panel-sub { color: var(--text-muted); font-size: 14px; margin: 4px 0 0; }

/* Drilldown headline (/market/:game, /market/:game/:set): eyebrow + the ONE
   number that matters (index level) + its change pill, promoted into the
   card header -- amber is rationed to the active range tab below, never
   spent on the eyebrow label too. */
.mkt-page .panel-headline { min-width: 0; }
.mkt-page .panel-eyebrow {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.mkt-page .panel-value-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mkt-page .panel-value {
  font-family: 'Geist Mono', monospace;
  font-size: 34px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.mkt-page .panel-pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: var(--radius-lg);
  border: 1px solid transparent;
  font-family: 'Geist Mono', monospace;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.mkt-page .panel-pill.mkt-delta-pos { background: var(--green-tint); border-color: rgba(74, 222, 128, 0.28); color: var(--accent-green); }
.mkt-page .panel-pill.mkt-delta-neg { background: var(--rose-tint); border-color: rgba(248, 113, 113, 0.28); color: var(--accent-rose); }
.mkt-page .panel-pill.mkt-delta-flat { background: var(--s2); border-color: var(--border-subtle); color: var(--text-muted); }

/* Secondary metrics: one hairline-divided strip inside the card, never a
   wrapping grid of equal-weight boxes (no card chrome per item). */
.mkt-page .panel-stats {
  display: flex;
  flex-wrap: wrap;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border-subtle);
}
.mkt-page .panel-stat {
  flex: 1 1 0;
  min-width: 110px;
  padding: 0 18px;
  border-left: 1px solid var(--border-subtle);
}
.mkt-page .panel-stat:first-child { padding-left: 0; border-left: 0; }
.mkt-page .panel-stat-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.mkt-page .panel-stat-value {
  font-family: 'Geist Mono', monospace;
  font-size: 17px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.mkt-page .panel-stat-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/* Explanatory prose is a footnote, not a subtitle wedged between the title
   and the chart -- lives OUTSIDE the card, muted and small. */
.mkt-page .panel-footnote {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--text-muted);
  max-width: 78ch;
}
.mkt-page svg.chart { width: 100%; height: 300px; margin-top: 12px; display: block; }
.mkt-page svg.chart .grid-line { stroke: var(--border-subtle); stroke-width: 1; }
/* The 100 = flat index reference -- stronger than the faint gridlines so
   "above = rising, below = falling" reads without reading the axis. */
.mkt-page svg.chart .grid-baseline { stroke: var(--text-muted); stroke-width: 1; stroke-opacity: 0.55; }
/* 14px floor applies to axis labels too -- the uppercase-label exception in
   DESIGN.md covers letterspaced caps, not chart scales (GATE FIX). */
.mkt-page svg.chart .axis-lbl {
  fill: var(--text-muted);
  font-family: 'Geist Mono', monospace;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
/* The 100 axis label rides the emphasized baseline -- lift it out of the faint
   muted tier so the break-even reference is legible. */
.mkt-page svg.chart .axis-lbl--base { fill: var(--text-secondary); }
.mkt-page .legend { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-subtle); }
.mkt-page .legend-item { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.mkt-page .legend-dot { width: 10px; height: 10px; border-radius: 2px; background: currentColor; }

/* Crosshair layer (market_explorer.js bindChartInteraction): a vertical rule
   + a focus dot per series, both hidden until the pointer enters the chart.
   The dot inherits its series color via .mkt-sN (color -> currentColor); a
   surface-colored ring lifts it off an overlapping line (dataviz marks-and-
   anatomy: 2px surface ring on overlapping marks). */
.mkt-page svg.chart .chart-cursor-line { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 3; pointer-events: none; }
.mkt-page svg.chart .chart-cursor-dot { stroke: var(--bg-secondary); stroke-width: 2; pointer-events: none; }

/* Crosshair tooltip: absolutely positioned within .panel (position:relative),
   follows the nearest date column, lists every series present there. */
.mkt-page .chart-tip {
  position: absolute;
  z-index: 5;
  min-width: 132px;
  max-width: 240px;
  padding: 8px 10px;
  background: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
  pointer-events: none;
  font-size: 14px; /* DESIGN.md 14px floor -- hierarchy via color, not size */
}
.mkt-page .chart-tip[hidden] { display: none; }
.mkt-page .chart-tip-date { color: var(--text-muted); margin-bottom: 6px; font-variant-numeric: tabular-nums; }
.mkt-page .chart-tip-row { display: flex; align-items: center; gap: 8px; line-height: 1.7; }
.mkt-page .chart-tip-dot { width: 9px; height: 9px; border-radius: 2px; flex: none; background: currentColor; }
.mkt-page .chart-tip-name { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-page .chart-tip-val { color: var(--text-primary); margin-left: auto; }

/* Visually-hidden but readable by screen readers -- same pattern as
   dashboard.css.erb's .sr-only (that stylesheet isn't loaded on /market). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Client empty-state (findings 4 & 6, ahg-iyofn): the `hidden` attribute
   already yields display:none via the UA stylesheet, but svg.chart's own
   `display: block` rule below has equal specificity on the element itself
   -- the attribute selector here wins deterministically instead of relying
   on source order. */
.mkt-page svg.chart[hidden] { display: none; }
.mkt-page .panel-empty { margin: 12px 0 0; }

/* Fetch-failure banner (finding 2): aria-pressed/URL already flipped
   optimistically on tap, so a failed request needs a visible, retryable
   contradiction-breaker rather than staying silent. */
/* Same specificity trap as svg.chart[hidden] above: this `display: flex`
   is author-origin and would otherwise beat the UA `[hidden]{display:none}`,
   leaving the banner permanently visible. Explicit override keeps it hidden
   until showFetchError() clears the attribute (QA finding, ahg-iyofn). */
.mkt-page .panel-error[hidden] { display: none; }
.mkt-page .panel-error {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--s2);
  border: 1px solid var(--border-subtle);
  font-size: 14px;
  color: var(--text-secondary);
}
.mkt-page .panel-error-retry {
  appearance: none;
  border: 1px solid var(--border-subtle);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  font-weight: 600;
  padding: 0 14px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.mkt-page .panel-error-retry:hover { background: var(--s3); }

/* CARRIED FROM P2 REVIEW (ahg-pnypl notes): legend numbers render in
   <span class="num"> but nothing set the mono face -- they fell back to
   body Geist instead of Geist Mono, a typographic drift from the approved
   wireframe. */
.mkt-page .num { font-family: "Geist Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums; }

/* P3 (ahg-pnypl): a toggle/range change refetches before redrawing -- the
   PRIOR chart line stays on screen (never blanks) while the request is in
   flight; this just dims it slightly so the update doesn't look inert. */
.mkt-page svg.chart { transition: opacity 150ms ease-in-out; }
.mkt-page .panel--loading svg.chart { opacity: 0.55; }

/* Cap-at-3 nudge: clicking a 4th rail row while 3 are already charted must
   never fire the request that 422s (API MAX_SCOPES=3) -- this is the "brief
   visual nudge" that tells the tap something happened without changing
   selection or state. */
@keyframes mkt-pick-nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}
.mkt-page .pick-row--nudge { animation: mkt-pick-nudge 220ms ease-in-out; }
@media (prefers-reduced-motion: reduce) {
  .mkt-page .pick-row--nudge { animation: none; }
}

.mkt-page .note {
  margin-top: 24px;
  font-size: 14px;
  color: var(--text-muted);
  border-top: 1px solid var(--border-subtle);
  padding-top: 16px;
  max-width: 78ch;
}
.mkt-page .note b { color: var(--text-secondary); font-weight: 500; }

/* Minimal public-explorer footer (ahg-o9isl) -- attribution + copyright, no
   marketing columns. Mirrors the layout footer-bottom region; link treatment
   matches .mkt-topbar-back (dotted, muted, 44px tap target). */
.mkt-page .mkt-footer {
  margin-top: 40px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}
.mkt-page .mkt-footer-attrib {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--text-muted);
  max-width: 78ch;
}
.mkt-page .mkt-footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 20px;
  font-size: 14px;
  color: var(--text-muted);
}
.mkt-page .mkt-footer-links {
  display: flex;
  gap: 20px;
  margin-left: auto;
}
.mkt-page .mkt-footer-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.mkt-page .mkt-footer-link:hover { color: var(--text-primary); }

@media (max-width: 768px) {
  .mkt-page .mkt-footer-links { margin-left: 0; }
}

@media (max-width: 960px) {
  .mkt-page .split { grid-template-columns: 1fr; }
  .mkt-page svg.chart { height: 240px; }
}

@media (max-width: 768px) {
  .mkt-page .bar { gap: 8px 10px; min-height: 0; padding-bottom: 10px; margin-bottom: 12px; }
  .mkt-page .bar-title { font-size: 19px; flex: 1 1 auto; width: auto; }
  .mkt-page .bar-asof { order: 0; margin-left: auto; }
  .mkt-page .bar-break { display: block; flex-basis: 100%; height: 0; margin: 0; }
  .mkt-page .bar .seg { flex: 1; }
  .mkt-page .bar .seg a,
  .mkt-page .bar .seg button { min-height: 44px; padding: 0 10px; flex: 1 1 0; min-width: 0; }

  /* Tapping a rail row must show its effect. With the chart below a
     nine-row list it lands off-screen and the tap reads as broken -- pin
     the chart to the top and let the rail scroll under it (ahg-q0r0r
     MOBILE, non-negotiable). No gap under the pinned panel: a transparent
     gap lets rows scroll through it and reads as a rendering glitch. */
  .mkt-page .split { display: flex; flex-direction: column; gap: 0; }
  .mkt-page .split > .panel { order: 1; position: sticky; top: 0; z-index: 2; }
  .mkt-page .split > div:not(.panel) { order: 2; }
  .mkt-page .panel {
    padding: 12px 14px;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    border-top: 0;
    margin: 0 -16px; /* full-bleed against .mkt-page's 16px mobile padding */
    background: var(--bg-secondary);
  }
  .mkt-page .panel-title { font-size: 18px; }
  .mkt-page .panel-note,
  .mkt-page .panel-sub { display: none; }
  .mkt-page svg.chart { height: 170px; margin-top: 10px; }
  .mkt-page .legend { margin-top: 10px; padding-top: 10px; gap: 6px 16px; }
  .mkt-page .pick-row { min-height: 56px; }

  /* On the 170px phone chart a floating tooltip blankets the whole plot it
     annotates. Dock it below the chart as a compact wrapping strip instead
     (the JS-set absolute left/top are ignored under position:static). */
  .mkt-page .chart-tip {
    position: static;
    min-width: 0;
    max-width: none;
    margin: 10px 0 0;
    padding: 10px 0 0;
    background: transparent;
    border: 0;
    border-top: 1px solid var(--border-subtle);
    border-radius: 0;
    box-shadow: none;
  }
  .mkt-page .chart-tip-date { display: inline-block; margin: 0 12px 6px 0; }
  .mkt-page .chart-tip-row { display: inline-flex; width: auto; margin: 0 14px 6px 0; }
  .mkt-page .chart-tip-val { margin-left: 6px; }

  /* The range control belongs to the chart, not the page -- the base
     mobile rule above gives every .seg width:100%, which would make it
     claim its own row. Scope it back to content width. */
  .mkt-page .panel-hd { flex-wrap: nowrap; align-items: center; gap: 10px; }
  .mkt-page .panel-hd .panel-title { min-width: 0; }
  .mkt-page .panel-hd .seg { width: auto; flex: none; }
  .mkt-page .panel-hd .seg button { min-height: 44px; padding: 0 10px; flex: none; }

  /* Drilldown headline shrinks to fit next to the range tabs on one row. */
  .mkt-page .panel-headline { min-width: 0; }
  .mkt-page .panel-eyebrow { font-size: 11px; margin-bottom: 4px; }
  .mkt-page .panel-value { font-size: 24px; }
  .mkt-page .panel-pill { font-size: 12px; padding: 2px 8px; }
  .mkt-page .panel-stats { margin-top: 12px; padding-top: 10px; }
}

@media (max-width: 560px) {
  /* Below ~3 items per row a hairline-divided single strip wraps into a
     ragged half-row -- a clean 2-column grid (vertical divider down the
     middle, horizontal hairlines between rows) reads better than that. */
  .mkt-page .panel-stats { flex-wrap: wrap; }
  .mkt-page .panel-stat {
    flex: 1 1 50%;
    min-width: 0;
    border-left: 0;
    padding: 10px 0 0;
    margin-top: 10px;
    border-top: 1px solid var(--border-subtle);
  }
  .mkt-page .panel-stat:nth-child(-n+2) { margin-top: 0; padding-top: 0; border-top: 0; }
  .mkt-page .panel-stat:nth-child(odd) { padding-right: 12px; }
  .mkt-page .panel-stat:nth-child(even) { padding-left: 12px; border-left: 1px solid var(--border-subtle); }
}

/* ---- /market/compare (ahg-eqatd) -- reuses every .bar/.seg/.split/.pick/
   .pick-row/.swatch/.panel/svg.chart/.legend/.panel-empty/.panel-error rule
   above VERBATIM. Only new here: the game selector + search + results
   dropdown (the autocomplete), and dropping the leaderboard-only rank
   column from .pick-row. */

/* No .pick-rank on this surface -- the rail here is "what you picked", not a
   ranked leaderboard, so the column disappears rather than going blank. */
.mkt-page.mkt-compare .pick-row { grid-template-columns: 16px 1fr auto; cursor: default; }
/* The name is a drill-in link filling its cell so the whole set label is a
   44px tap target into the set's own page. */
.mkt-page.mkt-compare a.pick-name {
  align-self: stretch;
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-primary);
  text-decoration: none;
  cursor: pointer;
}
.mkt-page.mkt-compare a.pick-name:hover { text-decoration: underline; }
/* Real <button>, not a decorative span -- reset its chrome and give it a
   44px square hit area for the remove action. */
.mkt-page.mkt-compare .pick-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.mkt-page.mkt-compare .pick-remove:hover { color: var(--text-primary); }

.mkt-compare-toolbar { margin-bottom: 10px; }
.mkt-compare-game-select {
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: inherit;
  font-size: 16px; /* iOS zoom floor -- applies to selects too */
}

.mkt-compare .rail-search { position: relative; margin-bottom: 12px; }
.mkt-compare-search-input {
  width: 100%;
  min-height: 48px;
  padding: 0 14px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: inherit;
  font-size: 16px; /* iOS zoom floor */
}
.mkt-compare-search-input::placeholder { color: var(--text-muted); }
.mkt-compare-search-input:focus-visible { outline: 2px solid var(--accent-amber); outline-offset: -1px; }

.mkt-compare-results {
  position: absolute;
  z-index: 4;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  max-height: 320px;
  overflow-y: auto;
}
.mkt-compare-result {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: inherit;
  font-size: 15px;
  text-align: left;
  cursor: pointer;
}
.mkt-compare-result:hover { background: var(--s3); }
.mkt-compare-noresults {
  padding: 10px 12px;
  color: var(--text-muted);
  font-size: 14px;
}

@media (max-width: 768px) {
  .mkt-page.mkt-compare .pick-row { min-height: 52px; }
}
