/* 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: var(--font-display);
  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);
}

/* Hardcoded to the DARK-mode hex before -- #4ade80/#f87171 read as illegible
   neon-on-white in light mode (the app's semantic vars already have a
   darker light-mode pair, application_layout.css.erb's [data-theme="light"]
   block: --accent-green:#16a34a / --accent-rose:#dc2626). Route through the
   vars like every other delta color in this file (.panel-pill below)
   already does, so both modes stay readable. */
.mkt-delta-pos { color: var(--accent-green); }
.mkt-delta-neg { color: var(--accent-rose); }
.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: var(--font-mono);
  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). Everything else
     matches the app-wide table-header convention (application_layout.css.erb
     th, dashboard's .spikes-table/#cards-table-view th) -- 11px uppercase
     muted, not the 14px plain-case market ran before (ahg-o6rs6). */
  padding: 8px 14px;
  text-align: left;
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.mkt-table th.mkt-num { text-align: right; }

.mkt-table td {
  padding: 11px 14px;
  font-size: 14px;
}
.mkt-table tbody tr:nth-child(even) { background: var(--zebra); }

.mkt-table td.mkt-num {
  font-family: var(--font-mono);
  text-align: right;
}

/* Market value is the size number the drill-in exists to surface — give it a
   touch more weight than the supporting columns without spending the amber. */
.mkt-table td.mkt-val {
  color: var(--text-primary);
  font-weight: 500;
}

.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); }
/* ahg-itmsv: the set roster is a responsive card grid -- the top cards by
   price shown as their actual art, auto-fitting from ~2 columns on a phone to
   as many as the width allows. */
.mkt-roster-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.mkt-roster-item { min-width: 0; }
.mkt-roster-link {
  display: flex;
  flex-direction: column;
  gap: 8px;
  height: 100%;
  padding: 10px;
  background: var(--bg-secondary);
  border-radius: var(--radius-lg, 10px);
  text-decoration: none;
  color: inherit;
}
.mkt-roster-link:hover { background: var(--s2); }
.mkt-roster-thumb {
  width: 100%;
  aspect-ratio: 5 / 7;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius-md, 6px);
  background: var(--bg-secondary);
}
.mkt-roster-thumb--empty { border: 1px solid var(--border-subtle); }
.mkt-roster-info { min-width: 0; }
.mkt-roster-name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 14px;
  line-height: 1.25;
  color: var(--text-primary);
}
.mkt-roster-meta { display: block; font-size: 12px; color: var(--text-muted); margin-top: 2px; }
/* Price anchored to the card's foot, so the number sits with its card and
   every cell's price lines up along the bottom row of the grid. */
.mkt-roster-price {
  margin-top: auto;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

/* /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); }

/* Header game switcher (ahg-ftqi4 follow-up) -- native <details> disclosure. */
.mkt-page .mkt-gameswitch { position: relative; }
.mkt-page .mkt-gameswitch-summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  user-select: none;
  white-space: nowrap;
}
.mkt-page .mkt-gameswitch-summary::-webkit-details-marker { display: none; }
.mkt-page .mkt-gameswitch-summary:hover { border-color: var(--accent-amber); }
.mkt-page .mkt-gameswitch-summary:focus-visible { outline: 2px solid var(--accent-amber); outline-offset: 1px; }
.mkt-page .mkt-gameswitch-caret { flex-shrink: 0; color: var(--text-muted); transition: transform 120ms ease; }
.mkt-page .mkt-gameswitch[open] .mkt-gameswitch-caret { transform: rotate(180deg); }
.mkt-page .mkt-gameswitch-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 60;
  min-width: 220px;
  padding: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
}
/* Type-to-filter field (ahg-frktq): pinned above the list, which owns the
   scroll so the field stays put while the games scroll under it. 16px font
   keeps iOS from zooming on focus. */
.mkt-page .mkt-gameswitch-search {
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 6px;
  padding: 8px 10px;
  font-size: 16px;
  font-family: var(--font-sans);
  color: var(--text-primary);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
}
.mkt-page .mkt-gameswitch-search:focus { outline: none; border-color: var(--accent-amber); }
.mkt-page .mkt-gameswitch-list { max-height: 52vh; overflow-y: auto; }
.mkt-page .mkt-gameswitch-empty { padding: 9px 10px; font-size: 13px; color: var(--text-muted); }
.mkt-page .mkt-gameswitch-item {
  display: block;
  padding: 9px 10px;
  border-radius: 6px;
  font-size: 14px;
  color: var(--text-primary);
  text-decoration: none;
  white-space: nowrap;
}
.mkt-page .mkt-gameswitch-item:hover { background: var(--bg-glass, rgba(255,255,255,0.04)); }
.mkt-page .mkt-gameswitch-item.is-current { color: var(--accent-amber); font-weight: 700; }

/* 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);
}
/* Set shortcut bar (ahg-ftqi4) -- ports the /collect set command bar
   (collect/index.html.erb .qa-selected-set-bar + .qa-set-neighbors) into the
   market page family so set-hopping looks the same in both contexts. Neighbor
   set icons, the current set, a "Change set" search picker, and a card-name
   filter over the roster. Filled by market_explorer.js#initSetNav. */
.mkt-page .mkt-setnav {
  display: flex;
  align-items: center;
  gap: 12px 16px;
  flex-wrap: wrap;
  margin: 0 0 20px;
}
.mkt-page .mkt-setnav-strip {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  position: relative;
}
.mkt-page .mkt-setnav-neighbors {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.mkt-page .mkt-setnav-neighbors:empty { display: none; }
.mkt-page .mkt-setnav-neighbor {
  height: 30px;
  width: 30px;
  border-radius: 6px;
  border: 1.5px solid var(--border-subtle);
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px;
  flex-shrink: 0;
  transition: border-color 100ms, transform 80ms;
}
.mkt-page .mkt-setnav-neighbor:hover { border-color: var(--accent-amber); transform: scale(1.1); }
.mkt-page .mkt-setnav-neighbor:focus-visible { outline: 2px solid var(--accent-amber); outline-offset: 1px; }
.mkt-page .mkt-setnav-neighbor img { width: 20px; height: 20px; object-fit: contain; }
.mkt-page .mkt-setnav-initials {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  line-height: 1;
}
[data-theme="dark"] .mkt-page .mkt-setnav-neighbor img:not(.mkt-setnav--card) { filter: invert(0.7); }
.mkt-page .mkt-setnav-logo { height: 30px; max-width: 160px; object-fit: contain; flex-shrink: 0; }
.mkt-page .mkt-setnav-icon { width: 30px; height: 30px; object-fit: contain; flex-shrink: 0; }
[data-theme="dark"] .mkt-page .mkt-setnav-icon { filter: invert(1); }
.mkt-page .mkt-setnav-icon--placeholder {
  border-radius: 6px;
  background: var(--surface-2, var(--bg-secondary));
  border: 1px solid var(--border-subtle);
  filter: none;
}
.mkt-page .mkt-setnav-name {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.mkt-page .mkt-setnav-change {
  padding: 5px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  font-size: 13px;
  font-family: var(--font-sans);
  cursor: pointer;
  flex-shrink: 0;
  min-height: 32px;
  transition: border-color 100ms, color 100ms;
}
.mkt-page .mkt-setnav-change:hover { border-color: var(--accent-amber); color: var(--text-primary); }
/* Change-set picker: an absolutely-positioned popover under the strip, same
   fetch-once/filter-client-side autocomplete as .mkt-compare-picker. */
.mkt-page .mkt-setnav-picker {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  width: min(340px, 90vw);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
  padding: 8px;
}
.mkt-page .mkt-setnav-picker-input {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 14px;
  padding: 8px 10px;
  min-height: 40px;
}
.mkt-page .mkt-setnav-results {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  max-height: 260px;
  overflow-y: auto;
}
.mkt-page .mkt-setnav-result {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 14px;
  padding: 9px 8px;
  border-radius: 6px;
  cursor: pointer;
}
.mkt-page .mkt-setnav-result:hover { background: var(--bg-glass, rgba(255,255,255,0.04)); color: var(--accent-amber); }
.mkt-page .mkt-setnav-noresults { color: var(--text-muted); font-size: 13px; padding: 9px 8px; }
.mkt-page .mkt-setnav-filter {
  flex: 0 1 300px;
  min-width: 180px;
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 16px;
  padding: 8px 12px;
  min-height: 40px;
}
.mkt-page .mkt-setnav-filter:focus { outline: none; border-color: var(--accent-amber); }
@media (max-width: 768px) {
  .mkt-page .mkt-setnav { gap: 10px; margin-bottom: 14px; }
  .mkt-page .mkt-setnav-filter { flex-basis: 100%; min-height: 44px; }
  /* Hard 44px touch targets on the interactive controls (mobile minimum). */
  .mkt-page .mkt-setnav-change { min-height: 44px; padding: 5px 14px; }
  .mkt-page .mkt-setnav-neighbor { height: 38px; width: 38px; }
  .mkt-page .mkt-setnav-picker-input { min-height: 44px; }
}
@media (max-width: 560px) {
  /* Neighbour icons are a desktop quick-hop nicety; on a phone six of them plus
     the set name and "Change set" overflow the row. Hide them and let "Change
     set" carry set switching (the picker lists every set in the game). */
  .mkt-page .mkt-setnav-neighbors { display: none; }
}

.mkt-page .bar-title {
  font-family: var(--font-display);
  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: var(--font-mono);
}
/* ahg-pebhh: set release-date indicator, sits just after "As of ...". */
.mkt-page .bar-release {
  order: 10;
  font-size: 14px;
  color: var(--text-muted);
  white-space: nowrap;
  font-family: var(--font-mono);
}
.mkt-page .bar-release::before { content: '·'; margin: 0 8px; color: var(--border-strong, #cbd5e1); }
.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-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-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;
  color: inherit;
  font: inherit;
  text-align: left;
  transition: background 100ms ease-in-out;
}
.mkt-page .pick-row:nth-child(even) { background: var(--zebra); }
.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: var(--font-mono);
  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-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 {
  /* Matches the app's one canonical "big number" spec (dashboard's
     .today-stat-card .today-stat-value) instead of running its own bigger,
     bolder size -- ahg-o6rs6. */
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.15;
  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: var(--font-mono);
  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;
}
.mkt-page .panel-stat:first-child { padding-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: var(--font-mono);
  font-size: 17px;
  font-weight: 600;
  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: var(--font-mono);
  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; }
/* ahg-pebhh: set-release markers on the index chart's time axis. */
.mkt-page svg.chart .mkt-rel-line { stroke: var(--border-strong, #cbd5e1); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.5; pointer-events: none; }
.mkt-page svg.chart .mkt-rel-icon { opacity: 0.85; }
.mkt-page svg.chart .mkt-rel-dot { fill: var(--text-muted); }
/* Scryfall/most set symbols are black glyphs -- invert so they read on the dark canvas. */
[data-theme="dark"] .mkt-page svg.chart .mkt-rel-icon { filter: invert(1); }
.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);
  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: var(--font-mono); 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; }

/* Public-explorer footer (ahg-o9isl, ahg-pgvq2). The market attribution
   sentence sits above the shared layouts/_footer, whose column/link styles
   live inline in the landing and features layouts -- those layouts are not
   used here, so .mkt-page restates them against the app shell's tokens. */
.mkt-page .mkt-footer {
  margin-top: 40px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}
.mkt-page .mkt-footer-attrib {
  margin: 0;
  font-size: 14px;
  color: var(--text-muted);
  max-width: 78ch;
}
.mkt-page .site-footer {
  border-top: none;
  padding: 0;
  margin: 0;
  max-width: none;
}
.mkt-page .footer-inner {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 32px;
  margin: 28px 0 24px;
}
.mkt-page .footer-heading {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
}
.mkt-page .footer-link {
  display: block;
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 5px 0;
}
.mkt-page .footer-link:hover { color: var(--text-primary); }
.mkt-page .footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 24px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 16px;
}
.mkt-page .footer-bottom span { font-size: 13px; color: var(--text-muted); }

@media (max-width: 960px) {
  .mkt-page .footer-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
}

@media (max-width: 768px) {
  .mkt-page .footer-link { display: flex; align-items: center; min-height: 44px; padding: 0; }
}

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

@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; }
}

/* Hoard attribution line (ahg-99x5k, shared/_hoard_attribution partial) --
   small factual "priced and tracked with Hoard" strip on public /cards and
   /market pages. 14px meets the DESIGN.md text-size floor exactly; the
   dotted-underline / arrow-suffix split follows DESIGN.md's Links
   convention (internal action = dotted underline, external destination =
   trailing arrow, both text-primary, no separate color register). */
.hoard-attribution {
  font-size: 14px;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 20px 0;
}
.hoard-attribution a {
  color: var(--text-primary);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.hoard-attribution a:hover { border-bottom-color: var(--text-secondary); }
.hoard-attribution a.hoard-attribution-external {
  border-bottom: none;
}
.hoard-attribution a.hoard-attribution-external::after {
  content: " \2197";
}

/* One-sentence factual summary on the market index/game/set pages -- the
   exact same string embedded in that page's JSON-LD `description`, so the
   visible copy and the structured data can never drift apart. */
.mkt-summary {
  font-size: 15px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 8px 0 20px;
}

/* Page tab bar linking a game's movers/market-report/most-valuable pages
   (ahg-o6rs6). Ported from the dashboard's .subtab-bar/.subtab-btn pattern
   (dashboard.css.erb, the Today tab's Overview/Suggestions/Sync Log/Rollback
   strip) rather than reusing .seg -- .seg is what the rest of /market uses
   for a SECONDARY in-panel toggle (product track, chart range); this is
   primary page-level navigation, so it gets the same underline-tab
   treatment as the app's other page-level tab strip. market.css doesn't
   load dashboard.css.erb (each page-family stylesheet is self-contained),
   so the values are copied here rather than shared. */
.mkt-subnav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 4px 0 20px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-subtle);
}
.mkt-subnav-tab {
  position: relative;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: var(--radius-md);
  transition: color 100ms ease-out, background 100ms ease-out;
}
.mkt-subnav-tab:hover {
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.04);
}
.mkt-subnav-tab[aria-current="page"] {
  color: var(--accent-amber);
  font-weight: 600;
}
.mkt-subnav-tab[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: -9px;
  height: 2px;
  background: var(--accent-amber);
  border-radius: 999px;
}
/* Dashboard's mobile .subtab-btn shrinks below the 44px tap-target floor
   (an accepted tradeoff for an authenticated, mostly-desktop surface) --
   /market is public and skews mobile, so it keeps a tap-safe height here
   instead of copying that shrink. */
@media (max-width: 768px) {
  .mkt-subnav-tab {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: 0 12px;
  }
}

/* "See all X" link under a market-report excerpt table (ahg-o6rs6). */
.mkt-excerpt-more {
  font-size: 14px;
  margin: 8px 0 28px;
}

/* Boxes the "Common questions" FAQ on the movers/market-report/most-valuable
   pages so it reads as a section of the app, not a wall of prose dropped
   below the tables (ahg-o6rs6 follow-up). Deliberately its own class, not
   .panel -- .panel carries sticky/mobile drill-down overrides (the chart
   panel) that don't apply to a plain FAQ box. guides/_common_questions
   itself is unchanged (still the shared h2/h3/p + FAQPage JSON-LD partial),
   this only wraps it. */
.mkt-faq {
  background: var(--bg-secondary);
  border-radius: var(--radius-lg);
  padding: 20px 24px;
  margin: 8px 0 28px;
}
.mkt-faq h2 { margin-top: 0; }
.mkt-faq h3 { font-size: 15px; }

/* Quick clarifying caption on /market/:game/movers (ahg-o6rs6 follow-up) --
   states up front that ranking is set-level, not per-card, instead of
   leaving it to be inferred from the table rows. */
.mkt-note {
  font-size: 14px;
  color: var(--text-muted);
  margin: 0 0 16px;
}

/* Gainers/losers side by side (ahg-o6rs6 follow-up) -- stacked full tables
   put losers a full scroll below gainers, easy to miss entirely. Matches
   the dashboard's own gainers/losers layout (.movers-columns in
   dashboard.css.erb); market.css doesn't load that file, so the values are
   copied here rather than shared. */
.mkt-movers-columns {
  display: flex;
  gap: 24px;
  align-items: flex-start;
}
.mkt-movers-col { flex: 1 1 0; min-width: 0; }
@media (max-width: 960px) {
  /* align-items: flex-start (base rule) sizes a row's cross-axis items to
     their own content -- fine side by side, but once stacked (flex-direction:
     column) that same "flex-start" governs the now-HORIZONTAL cross axis, so
     each column shrank to its table's min-content width (560px) instead of
     the viewport. stretch forces full width so .mkt-table-wrap's own
     overflow-x:auto contains the table, not the page. */
  .mkt-movers-columns { flex-direction: column; gap: 0; align-items: stretch; }
}
