/* ============================================================================
   CROWN VAULT -- THE VITRINE
   ----------------------------------------------------------------------------
   THESIS: A collection is a set of valuable objects, each one displayed. Refuses
     both the dark glowing tracker this app was and the retail price-tag wall that
     replaced it -- a shop sells stock, a vitrine presents holdings.
   OWN-WORLD: Cold mineral ground with one directional light from the top left.
     Cards sit IN recessed wells, not on the surface, each casting a real shadow.
     One clean grotesque across a wide scale carries every level of hierarchy.
     Warm brass appears on the crown mark and the active state, nowhere else.
   STORY: The owner sees their best card presented as an object under light with
     the valuation beside it, then the rest of the collection displayed the same
     way, and trusts the number enough to act on it.
   FIRST VIEWPORT: Quiet chrome. Then the vitrine: the top holding in a lit well,
     the estimated value set very large and light beside it, the counts beneath a
     hairline. Then the holdings, large, in wells, generously spaced.
   FORM: The Vitrine, derived after the Card Shop Wall was rejected as cheap --
     the failure was thinness (one type size, hairline structure, small cards),
     not lightness, so this build spends everything on scale, material and space.
   FINISH: unreviewed and undocumented is unfinished; this build ends with the
     finish review, the verdict, and DESIGN.md
   ============================================================================ */

/* ---------------------------------------------------------------- 1. TYPE ---
   Geist, self-hosted. One clean grotesque doing every job: hierarchy comes from
   size, weight and tracking rather than a decorative face. A didone was tried
   here first and read as costume on a data tool. Self-hosted because a price the
   user is asked to trust should not wait on a third-party font server. */
@font-face{
  font-family:'Geist';font-style:normal;font-weight:200 700;font-display:swap;
  src:url('/static/fonts/geist-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'Geist';font-style:normal;font-weight:200 700;font-display:swap;
  src:url('/static/fonts/geist-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* -------------------------------------------------------------- 2. TOKENS ---
   Cold stone, lit from one direction, with a warm metal reserved for almost
   nothing. Light was kept from the brief; the previous build proved the problem
   was never lightness but the absence of weight, material and scale. */
:root{
  --stone-0:#F7F9FB; --stone-1:#E9EDF2; --stone-2:#D6DDE5; --stone-3:#C2CBD5;
  --graphite:#14171C; --slate:#57626F; --slate-2:#616C7A;
  --brass:#9C7B41; --brass-lo:#7A5F31;
  --up:#1B6B47; --up-tint:#E7F1EC; --down:#A33228; --down-tint:#F8EBE9;

  /* names the rest of the sheet still speaks */
  --wall-hi:var(--stone-0); --wall:var(--stone-1); --wall-lo:var(--stone-2); --wall-edge:var(--stone-3);
  --stock:#FFFFFF; --edge:var(--stone-3); --edge-soft:#DDE3EA; --edge-hard:#AEB8C4;
  --ink:var(--graphite); --ink-2:var(--slate); --ink-3:var(--slate); --ink-on-dark:#F7F9FB;
  --tag:var(--brass-lo); --tag-lo:var(--brass-lo); --tag-tint:#F5EFE4;
  --blue:#2E5C8A; --blue-lo:#1F4368; --blue-tint:#E8EEF5;
  --bg:var(--stone-1); --panel:var(--stock); --panel2:var(--stone-2);
  --border:var(--stone-3); --text:var(--graphite); --muted:var(--slate);
  --accent:var(--brass-lo); --accent2:var(--brass-lo); --accent-bright:var(--brass-lo);
  --glow:rgba(156,123,65,.16);
  --sell:var(--up); --watch:#2E5C8A; --hold:var(--slate); --danger:var(--down); --red:var(--down);
  --gold:var(--brass-lo); --gold-bright:var(--brass-lo); --gold-glow:rgba(156,123,65,.18);

  /* Depth ladder. Four levels, every shadow derived from one of them, all lit
     from the same top-left source as the page. */
  --lift-1:0 1px 2px rgba(20,30,45,.07), 0 2px 6px rgba(20,30,45,.05);
  --lift-2:0 2px 4px rgba(20,30,45,.14), 0 12px 26px -10px rgba(20,30,45,.36);
  --lift-3:0 3px 6px rgba(20,30,45,.16), 0 22px 40px -14px rgba(20,30,45,.44);
  --lift-4:0 8px 16px rgba(20,30,45,.16), 0 44px 84px -26px rgba(20,30,45,.46);
  /* recessed: the well a card is set into */
  --sink-1:inset 0 1px 3px rgba(30,45,65,.14), inset 0 -1px 0 rgba(255,255,255,.7);
  --sink-2:inset 0 2px 5px rgba(30,45,65,.20), inset 0 -1px 0 rgba(255,255,255,.85);

  --font-display:'Geist',system-ui,-apple-system,sans-serif;
  --font-mono:'Geist',ui-monospace,SFMono-Regular,Menlo,monospace;
  --font-pixel:'Geist',system-ui,sans-serif;

  --chrome-bg:rgba(233,237,242,.92);
  --rail:clamp(20px,4.5vw,64px);
  --tag-r:5px;
}

/* Micro-labels. Uppercase and tracked, but at normal width and a modest weight:
   the previous build set every label in heavy condensed caps, which is signage,
   and signage on every element is what made the page shout while saying nothing. */
.sign,.rpg-panel-title,.vault-header,.chart-panel-title,.eyebrow{
  font-family:var(--font-display);font-weight:600;font-size:10.5px;
  text-transform:uppercase;letter-spacing:.14em;color:var(--slate-2);
}
h3{font-family:var(--font-display);font-weight:550;font-size:17px;letter-spacing:-.012em;margin:0 0 8px;color:var(--graphite);}
h3+p,h3+.sub{margin-top:0;}

/* Figures are the product. Tabular everywhere so columns align and a changing
   price never reflows its row, and negatively tracked at large sizes. */
.num,.tile-price,.holding-value,.hero-value,.stat .value,.chart-readout-price,
.set-tile-stat,.game-tile-stat,.rpg-spec-val,.fact b,td,th{
  font-variant-numeric:tabular-nums lining-nums;font-feature-settings:"tnum" 1;
}

*{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;}
body{
  margin:0;min-height:100vh;min-height:100dvh;color:var(--graphite);
  font-family:var(--font-display);font-weight:400;
  font-size:15px;line-height:1.55;background:var(--stone-1);
  -webkit-font-smoothing:antialiased;
}

/* -------------------------------------------------------------- 3. GROUND ---
   One light source, top left, falling across the whole surface. Everything on
   the page is lit consistently with it, which is what lets recessed wells and
   cast shadows read as physical rather than as decoration. */
.bg-scene{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(120% 80% at 12% -10%, rgba(255,255,255,.95), transparent 60%),
    radial-gradient(90% 70% at 100% 110%, rgba(120,140,165,.16), transparent 65%),
    var(--stone-1);
}
.bg-grain{
  position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.5;mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}
:root,body[data-bg]{--site-bg-img:none;}

main{position:relative;z-index:1;padding:0 var(--rail) 120px;max-width:1500px;margin:0 auto;}

/* ------------------------------------------------------------ 4. UTILITIES --- */
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;}
.muted{color:var(--ink-3);}
.sub{color:var(--ink-3);font-size:13.5px;line-height:1.55;}
.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(158px,1fr));gap:18px 14px;margin-top:14px;}
.short{max-width:520px;}
.pos{color:var(--up);}
.neg{color:var(--down);}
.neutral{color:var(--ink-3);}
h3{font-size:15px;margin:0 0 12px;}
.eyebrow{font-size:10.5px;color:var(--ink-3);letter-spacing:.14em;}
a{color:var(--brass-lo);text-underline-offset:2px;}
a.plain{color:var(--brass-lo);text-decoration:none;font-weight:600;}
a.plain:hover{text-decoration:underline;}
::selection{background:var(--blue);color:#fff;}
:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:2px;}

/* -------------------------------------------------------------- 5. CHROME ---
   As quiet as it can be while still being navigable. The previous build printed
   the active tab as a solid ink block, which is shelf signage; here the active
   state is a brass hairline and a weight change, and the brand mark is the only
   place colour appears. No backdrop-filter under position:sticky -- it repaints
   as solid black rectangles in Chromium during ordinary scrolling. */
header{
  position:sticky;top:0;z-index:40;padding:0 var(--rail);
  background:var(--chrome-bg);border-bottom:1px solid var(--stone-3);
}
.nav-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;
  gap:18px;min-height:62px;padding-top:0;
}
.brand{display:flex;align-items:center;justify-content:center;padding:0;}
.brand-logo{
  display:inline-flex;align-items:center;gap:10px;background:none;border:0;padding:0;
  cursor:pointer;height:auto;
}
.brand-logo:hover{background:none;opacity:.7;}
.brand-mark{width:20px;height:16px;color:var(--brass);flex-shrink:0;}
.cardvault-wordmark{
  font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.24em;
  color:var(--graphite);line-height:1;
}
.navtabs{display:flex;align-items:center;gap:26px;}
.navtabs-right{justify-content:flex-end;}
.tab{
  font-size:13px;font-weight:450;color:var(--slate);letter-spacing:-.002em;
  padding:4px 0 3px;cursor:pointer;user-select:none;white-space:nowrap;
  border-bottom:1.5px solid transparent;background:none;
  transition:color .15s,border-color .15s;
}
.tab:hover{color:var(--graphite);background:none;}
.tab.active{color:var(--graphite);font-weight:550;border-bottom-color:var(--brass);background:none;}
.navtabs .tab{display:none !important;}
.navtabs .tab[data-public="true"]{display:block !important;}
body.logged-in .navtabs .tab{display:block !important;}
.navtabs .tab[data-tab="vault"],body.logged-in .navtabs .tab[data-tab="vault"],
.header-level-wrap,.bnav-item[data-tab="vault"]{display:none !important;}

/* Counter staff: currency and account, top right of the board. */
.account-menu-wrap{position:absolute;top:10px;right:var(--rail);z-index:41;display:flex;align-items:center;gap:8px;}
.account-menu-trigger{
  width:32px;height:32px;padding:0;display:grid;place-items:center;cursor:pointer;
  background:var(--stock);border:1px solid var(--edge);border-radius:2px;
}
.account-menu-trigger:hover{border-color:var(--ink);}
.account-menu-trigger img{width:20px;height:20px;object-fit:contain;}
.account-menu-dropdown{
  display:none;position:absolute;top:38px;right:0;min-width:190px;
  background:var(--stock);border:1px solid var(--edge);border-top:2px solid var(--ink);
  border-radius:0;box-shadow:var(--lift-3);padding:4px;z-index:42;
}
.account-menu-dropdown.active{display:block;}
.account-menu-status{
  font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.1em;color:var(--ink-3);padding:8px 10px 7px;border-bottom:1px solid var(--edge-soft);
}
.account-menu-item{
  display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  font-family:inherit;font-size:13.5px;color:var(--ink);padding:9px 10px;
}
.account-menu-item:hover{background:var(--wall);}

.currency-select-wrap{position:relative;}
.currency-select-trigger{
  display:flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;
  background:var(--stock);border:1px solid var(--edge);border-radius:2px;
  color:var(--ink);padding:7px 9px;font-size:12px;font-weight:700;letter-spacing:.03em;transition:border-color .12s;
}
.currency-select-trigger:hover{border-color:var(--ink);}
.currency-select-trigger .caret{color:var(--ink-3);font-size:10px;}
.currency-select-dropdown{
  display:none;position:absolute;top:38px;right:0;min-width:224px;max-height:320px;
  overflow-y:auto;background:var(--stock);border:1px solid var(--edge);
  border-top:2px solid var(--ink);box-shadow:var(--lift-3);padding:4px;z-index:42;
}
.currency-select-dropdown.active{display:block;}
.currency-option{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;cursor:pointer;
  background:none;border:0;color:var(--ink);padding:8px 9px;font-family:inherit;font-size:13px;
}
.currency-option:hover{background:var(--wall);}
.currency-option.active{background:var(--blue-tint);color:var(--blue-lo);font-weight:700;}
.currency-option-flag{font-size:15px;line-height:1;}
.currency-option-code{font-weight:700;min-width:34px;font-variant-numeric:tabular-nums;}
.currency-option-name{color:var(--ink-3);font-size:12px;}
.currency-option.active .currency-option-name{color:inherit;}

/* Generic pill-that-opens-a-dropdown, shared by the Collection sort and view
   switches. Same stock-and-hairline language as the counter controls above. */
.dropdown-pill{position:relative;}
.dropdown-pill-trigger{
  display:flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;
  background:var(--stock);border:1px solid var(--edge);border-radius:2px;
  color:var(--ink);padding:7px 10px;font-size:12px;font-weight:700;letter-spacing:.03em;white-space:nowrap;transition:border-color .12s;
}
.dropdown-pill-trigger:hover{border-color:var(--ink);}
.dropdown-pill-menu{
  display:none;position:absolute;top:36px;left:0;min-width:210px;z-index:39;
  background:var(--stock);border:1px solid var(--edge);border-top:2px solid var(--ink);
  box-shadow:var(--lift-3);padding:4px;max-height:340px;overflow-y:auto;
}
.dropdown-pill.active .dropdown-pill-menu,.dropdown-pill-menu.active{display:block;}
.dropdown-pill-menu-wide{min-width:290px;left:auto;right:0;}
.dropdown-pill-menu{max-width:calc(100vw - 24px);}
.dropdown-pill-option{
  display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer;
  background:none;border:0;color:var(--ink);padding:8px 9px;font-family:inherit;font-size:13px;
}
.dropdown-pill-option:hover{background:var(--wall);}
.dropdown-pill-option.active,.dropdown-pill-option.selected{background:var(--blue-tint);color:var(--blue-lo);font-weight:700;}
.dropdown-pill-divider{height:1px;background:var(--edge-soft);margin:4px 0;}
.dropdown-pill-section-label{
  font-size:10px;font-weight:600;text-transform:uppercase;
  letter-spacing:.12em;color:var(--ink-3);padding:8px 9px 5px;
}
/* The row, not the box. This selector names a <label> wrapping a checkbox plus its
   text, so sizing it 14px square (as it once was) crushed the label and threw the
   words out over the top of the rows below it. */
.dropdown-pill-checkbox{
  display:flex;align-items:center;gap:8px;width:100%;cursor:pointer;
  padding:8px 9px;font-size:13px;color:var(--ink);line-height:1.35;
  /* The global label rule sets small caps with wide tracking, which is right for a
     field label above an input but makes these options read as headings -- "GRADED"
     ends up looking like a sibling of the "GRADING" header it sits under. */
  text-transform:none;letter-spacing:0;font-weight:500;
}
.dropdown-pill-checkbox:hover{background:var(--wall);}
.dropdown-pill-checkbox input[type="checkbox"]{
  accent-color:var(--blue);width:14px;height:14px;flex:0 0 auto;margin:0;cursor:pointer;
}
.dropdown-pill-range-row{display:flex;align-items:center;gap:6px;padding:4px 9px 8px;}
.dropdown-pill-range-row input{width:76px;}

/* Notice strip: tucked under the board, ink rule top and bottom. */
.verify-banner{
  position:relative;z-index:1;display:flex;align-items:center;justify-content:center;
  gap:12px;flex-wrap:wrap;padding:9px var(--rail);
  background:var(--tag-tint);border-bottom:1px solid var(--tag);
  color:var(--tag-lo);font-size:13px;font-weight:600;
}

/* Phone: the board narrows to wordmark plus counter, and the sections move to a
   fixed rail at the bottom where a thumb can reach them. */
.bottom-nav{
  display:none;position:fixed;left:0;right:0;bottom:0;z-index:45;
  background:var(--stock);border-top:2px solid var(--ink);
  box-shadow:0 -6px 20px -12px rgba(16,28,42,.35);
  padding-bottom:env(safe-area-inset-bottom);
}
.bnav-item{
  flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:8px 0 7px;background:none;border:0;cursor:pointer;
  font-family:inherit;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);
  border-top:2px solid transparent;margin-top:-2px;
  /* Six labels share the width of a phone, so the longest one ("How to play") has
     about 62px to live in. Left alone it wrapped to two lines and overran the bar
     while its five neighbours sat on one, which just looked broken. Shrinking the
     type a touch and dropping the tracking fits it, which beats renaming a section
     to suit a layout. */
  white-space:nowrap;font-size:clamp(8.2px,2.2vw,10.5px);letter-spacing:0;
}

.bnav-item svg{width:19px;height:19px;stroke-width:1.7;}
.bnav-item.active{color:var(--ink);border-top-color:var(--tag);}

/* ----------------------------------------------------------- 6. CONTROLS ---
   Refined rather than shouted. The previous build set every button as a solid
   ink block in heavy uppercase, which is shop signage; here the primary action
   is still ink but at a readable weight and sentence case, and secondaries are
   nearly invisible until you need them. */
button:not(.secondary):not(.plain):not(.danger):not(.pill):not(.chart-range-pill):not(.brand-logo):not(.variant-pill):not(.data-source-tab):not(.tab):not(.bnav-item):not(.account-menu-item):not(.currency-option):not(.dropdown-pill-option):not(.dropdown-pill-trigger):not(.currency-select-trigger):not(.account-menu-trigger):not(.ebay-listings-toggle):not(.tile-favourite-btn):not(.tile-listed-btn):not(.ice-icon-btn):not(.ground-opt):not(.crown-opt):not(.market-star):not(.market-yn):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta):not(.share-card):not(.share-btn):not(.share-theme):not(.share-otab):not(.bundle-lang-btn):not(.ice-opt):not(.mode-toggle):not(.mode-opt):not(.detail-back):not(.help-btn):not(.bundle-run):not(.bundle-tick):not(.bundle-selectall):not(.bundle-setopt):not(.bundle-var):not(.bundle-var-opt):not(.bundle-review-close):not(.cen-h):not(.cen-nudge-grid button):not(.load-failure button):not(.bundle-opt):not(.bundle-row-pick):not(.bundle-row-main):not(.scan-choice):not(.scan-capture-fallback):not(.scan-flip):not(.scan-close):not(.scan-torch):not(.bundle-row-x):not(.bundle-chip):not(.bundle-done):not(.result-card button):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta),.rpg-btn-solid{
  font-family:inherit;font-size:13px;font-weight:550;letter-spacing:-.004em;
  /* --ink and --ink-on-dark are a contrasting PAIR in every ground: dark ink on a
     light ground, light ink on a dark one, and --ink-on-dark is always whatever
     reads against --ink. Using the pair keeps a primary button legible in both
     modes. It used to take its background from --graphite while hardcoding white
     text, and the dark grounds redefine --graphite to a near-white -- so on those
     the Search button was white text on a white fill, measured at 1.12:1. */
  background:var(--ink);color:var(--ink-on-dark);
  border:1px solid var(--ink);border-radius:6px;
  padding:9px 16px;cursor:pointer;white-space:nowrap;
  transition:background .15s,border-color .15s,color .15s,box-shadow .15s;
}
button:not(.secondary):not(.plain):not(.danger):not(.pill):not(.chart-range-pill):not(.brand-logo):not(.variant-pill):not(.data-source-tab):not(.tab):not(.bnav-item):not(.account-menu-item):not(.currency-option):not(.dropdown-pill-option):not(.dropdown-pill-trigger):not(.currency-select-trigger):not(.account-menu-trigger):not(.ebay-listings-toggle):not(.tile-favourite-btn):not(.tile-listed-btn):not(.ice-icon-btn):not(.ground-opt):not(.crown-opt):not(.market-star):not(.market-yn):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta):not(.share-card):not(.share-btn):not(.share-theme):not(.share-otab):not(.bundle-lang-btn):not(.ice-opt):not(.mode-toggle):not(.mode-opt):not(.detail-back):not(.help-btn):not(.bundle-run):not(.bundle-tick):not(.bundle-selectall):not(.bundle-setopt):not(.bundle-var):not(.bundle-var-opt):not(.bundle-review-close):not(.cen-h):not(.cen-nudge-grid button):not(.load-failure button):not(.bundle-opt):not(.bundle-row-pick):not(.bundle-row-main):not(.scan-choice):not(.scan-capture-fallback):not(.scan-flip):not(.scan-close):not(.scan-torch):not(.bundle-row-x):not(.bundle-chip):not(.bundle-done):not(.result-card button):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta):hover,.rpg-btn-solid:hover{background:var(--graphite);border-color:var(--graphite);}
button:active{transform:translateY(.5px);}
button:not(.secondary):not(.plain):not(.danger):not(.pill):not(.chart-range-pill):not(.brand-logo):not(.variant-pill):not(.data-source-tab):not(.tab):not(.bnav-item):not(.account-menu-item):not(.currency-option):not(.dropdown-pill-option):not(.dropdown-pill-trigger):not(.currency-select-trigger):not(.account-menu-trigger):not(.ebay-listings-toggle):not(.tile-favourite-btn):not(.tile-listed-btn):not(.ice-icon-btn):not(.ground-opt):not(.crown-opt):not(.market-star):not(.market-yn):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta):not(.share-card):not(.share-btn):not(.share-theme):not(.share-otab):not(.bundle-lang-btn):not(.ice-opt):not(.mode-toggle):not(.mode-opt):not(.detail-back):not(.help-btn):not(.bundle-run):not(.bundle-tick):not(.bundle-selectall):not(.bundle-setopt):not(.bundle-var):not(.bundle-var-opt):not(.bundle-review-close):not(.cen-h):not(.cen-nudge-grid button):not(.load-failure button):not(.bundle-opt):not(.bundle-row-pick):not(.bundle-row-main):not(.scan-choice):not(.scan-capture-fallback):not(.scan-flip):not(.scan-close):not(.scan-torch):not(.bundle-row-x):not(.bundle-chip):not(.bundle-done):not(.result-card button):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta):disabled{background:var(--stone-2);border-color:var(--stone-3);color:var(--slate-2);cursor:not-allowed;transform:none;}
/* The :not() chain above strips the primary FILL from these -- but it strips the
   shared SHAPE with it, because both lived in the same rule. So .secondary,
   .danger and .plain were falling through to the browser's own button styling and
   picking up its padding and its square corners, while the one button not on the
   exclusion list kept the 6px radius. In a row of four that read as four
   different buttons: Sell, Delete Card and Close square, Save rounded.
   Geometry is declared once here for the whole family; only colour differs. */
button.secondary,.secondary,button.danger,.danger,button.plain,.rpg-btn{
  font-family:inherit;font-size:13px;font-weight:550;letter-spacing:-.004em;
  border-radius:6px;border-width:1px;border-style:solid;
  padding:9px 16px;cursor:pointer;white-space:nowrap;
  transition:background .15s,border-color .15s,color .15s,box-shadow .15s;
}
button.secondary,.secondary,.rpg-btn{
  background:#fff;color:var(--graphite);border-color:var(--stone-3);box-shadow:var(--lift-1);
}
button.secondary:hover,.secondary:hover,.rpg-btn:hover{background:#fff;border-color:var(--slate-2);color:var(--graphite);}
button.danger,.danger{background:#fff;color:var(--down);border-color:rgba(163,50,40,.35);box-shadow:none;}
button.danger:hover,.danger:hover{background:var(--down);color:#fff;border-color:var(--down);}
button.plain{background:none;border-color:transparent;color:var(--brass-lo);box-shadow:none;}
button.plain:hover{background:rgba(156,123,65,.08);border-color:transparent;}
.ice-icon-btn{
  width:32px;height:32px;padding:0;display:grid;place-items:center;border-radius:6px;
  background:#fff;color:var(--slate);border:1px solid var(--stone-3);box-shadow:var(--lift-1);
}
.ice-icon-btn:hover{background:#fff;color:var(--graphite);border-color:var(--slate-2);}
.ice-icon-btn svg{width:15px;height:15px;}

label{
  display:block;font-size:10.5px;font-weight:600;text-transform:uppercase;
  letter-spacing:.14em;color:var(--slate-2);margin:14px 0 6px;
}
input[type=text],input[type=email],input[type=password],input[type=number],
input[type=date],input[type=search],select,textarea{
  width:100%;font-family:inherit;font-size:14px;color:var(--graphite);
  background:#fff;border:1px solid var(--stone-3);border-radius:6px;
  padding:10px 12px;transition:border-color .15s,box-shadow .15s;
}
input:focus,select:focus,textarea:focus{
  outline:none;border-color:var(--slate-2);box-shadow:0 0 0 3px rgba(87,98,111,.10);
}
input::placeholder,textarea::placeholder{color:var(--slate-2);}
select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237C8794' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;padding-right:32px;}
input[type=checkbox],input[type=radio]{width:auto;accent-color:var(--brass);}
input[type=file]{
  width:100%;font-family:inherit;font-size:13px;color:var(--slate);
  background:#fff;border:1px dashed var(--stone-3);border-radius:6px;padding:11px 13px;cursor:pointer;
}
input[type=file]:hover{border-color:var(--slate-2);color:var(--graphite);}
input[type=file]::file-selector-button{
  font-family:inherit;font-size:12.5px;font-weight:550;background:var(--graphite);color:#fff;
  border:0;border-radius:5px;padding:7px 13px;margin-right:12px;cursor:pointer;
}

/* Segmented controls read as text with one selected, not as a row of chips. */
.pill-group{
  display:inline-flex;flex-wrap:nowrap;max-width:100%;gap:0;
  border:1px solid var(--stone-3);border-radius:7px;background:#fff;
  overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-webkit-overflow-scrolling:touch;
  box-shadow:var(--lift-1);
}
.pill-group::-webkit-scrollbar{display:none;}
.pill,.chart-range-pill{
  flex-shrink:0;white-space:nowrap;font-family:inherit;font-size:12.5px;font-weight:500;
  background:none;border:0;color:var(--slate);padding:7px 13px;cursor:pointer;border-radius:0;
  transition:color .15s,background .15s;
}
.pill:hover,.chart-range-pill:hover{background:var(--stone-1);color:var(--graphite);}
.pill.active,.chart-range-pill.active,.pill.selected{background:var(--graphite);color:#fff;font-weight:550;}
.pill:first-child{border-radius:6px 0 0 6px;} .pill:last-child{border-radius:0 6px 6px 0;}

.variant-pill-row,.data-source-tab-row,.type-tags-row,.fact-pills-row{display:flex;flex-wrap:wrap;gap:7px;}
.variant-pill,.data-source-tab{
  font-family:inherit;font-size:12px;font-weight:500;letter-spacing:-.002em;
  background:#fff;border:1px solid var(--stone-3);color:var(--slate);
  padding:6px 12px;border-radius:6px;cursor:pointer;box-shadow:none;
}
.variant-pill:hover,.data-source-tab:hover{border-color:var(--slate-2);color:var(--graphite);}
.variant-pill.active,.variant-pill.selected,.data-source-tab.active{
  background:var(--graphite);border-color:var(--graphite);color:#fff;font-weight:550;
}
.data-source-tab:disabled{background:var(--stone-1);color:var(--edge-hard);border-color:var(--stone-3);cursor:not-allowed;}

.badge,.fact-pill,.type-tag-box,.unverified-tag,.tile-qty-chip{
  display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:550;
  letter-spacing:.01em;padding:4px 9px;border-radius:5px;
  background:var(--stone-1);border:1px solid var(--stone-3);color:var(--slate);
}
.fact-pill-grey{background:var(--stone-1);color:var(--slate-2);}
.fact-pill-grade,.graded{background:var(--tag-tint);border-color:rgba(156,123,65,.4);color:var(--brass-lo);}
.unverified-tag{background:var(--down-tint);border-color:rgba(163,50,40,.3);color:var(--down);}
.badge.sell{background:var(--up-tint);border-color:rgba(27,107,71,.3);color:var(--up);}
.badge.watch{background:var(--blue-tint);border-color:rgba(46,92,138,.3);color:var(--blue-lo);}
.badge.hold{background:var(--stone-1);border-color:var(--stone-3);color:var(--slate);}
.badge.listed,.listed{background:var(--up-tint);border-color:rgba(27,107,71,.3);color:var(--up);}

/* Panels: a clean sheet lifted just off the ground. Soft radius, hairline, and
   a shadow from the same light source as everything else. */
.panel,.rpg-panel,.chart-panel,.info-tip-box,.slab-case,.card-box{
  background:#fff;border:1px solid var(--stone-3);border-radius:var(--tag-r);box-shadow:var(--lift-1);
}
.panel{padding:26px;margin:24px 0;}
.panel[id^="panel-"]{display:none;}
.panel[id^="panel-"].active{display:block;}

.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;}
table,.table{width:100%;border-collapse:collapse;font-size:13.5px;}
th{
  text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;
  letter-spacing:.14em;color:var(--slate-2);
  padding:11px 12px;border-bottom:1px solid var(--stone-3);white-space:nowrap;
}
td{padding:13px 12px;border-bottom:1px solid var(--edge-soft);vertical-align:middle;}
tbody tr:hover{background:var(--stone-1);}
.cell-sub{display:block;font-size:11.5px;color:var(--slate-2);margin-top:2px;}

.modal-bg{
  display:none;position:fixed;inset:0;z-index:60;
  background:rgba(20,30,45,.42);
  align-items:flex-start;justify-content:center;padding:32px 18px;overflow-y:auto;
  /* Keeps a scroll gesture inside the dialog. Without it the swipe runs to the end
     of the dialog and then carries on into the page underneath, which reads as the
     card refusing to scroll while the whole site slides about behind it. */
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
}
/* Belt and braces: the page behind an open dialog should not scroll at all. */
body:has(.modal-bg.active){overflow:hidden;}
.modal-bg.active{display:flex;}
.modal{
  background:#fff;border:1px solid var(--stone-3);border-radius:10px;
  box-shadow:var(--lift-4);padding:26px;width:min(520px,100%);margin:auto;
}
.modal-wide{width:min(1080px,100%);}
/* A way out that doesn't need scrolling past a whole card to reach. The Close in the
   footer is still there for the desktop layout, where it's already on screen. */
.detail-topbar{
  display:none;position:sticky;top:0;z-index:3;align-items:center;
  margin:-26px -26px 14px;padding:10px 16px;
  background:var(--stock);border-bottom:1px solid var(--edge-soft);
}
.detail-back{
  padding:7px 13px;border-radius:8px;font-family:inherit;font-size:14px;font-weight:600;
  background:none;border:none;color:var(--ink);cursor:pointer;
}
.detail-back:hover{background:var(--wall);}
.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:22px;flex-wrap:wrap;}

/* Figures. Not a stat-card template: a label above a number, on the sheet itself. */
.stat-grid,.settings-grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));}
.stat .label,.stat-grid .label{
  font-size:10px;font-weight:600;text-transform:uppercase;
  letter-spacing:.12em;color:var(--ink-3);margin:0 0 3px;
}
.stat .value,.value{font-size:24px;font-weight:700;color:var(--ink);line-height:1.1;}
/* Every element carrying this class writes plain TEXT into itself -- "Searching
   Pokemon...", "Loading cards...", "Could not load threads", the scanner's verdict,
   all ten of them. It was styled as a 5px progress BAR with overflow:hidden, so
   every one of those messages was clipped out of existence. Nothing has ever put a
   child fill element inside one; there was no bar to be a bar for.

   This is why the Sets view read as broken when it was merely loading: the
   "Loading cards..." it was dutifully setting could not be seen. Same for every
   search, every refresh, every failed scan.

   Now what it always was: a line of status text. The bar styling is kept below
   under its own name in case something ever genuinely needs one. */
.progress{
  min-height:1.2em;font-size:13px;line-height:1.5;color:var(--slate);
  background:none;border-radius:0;overflow:visible;margin:6px 0 0;
}
.progress:empty{display:none;margin:0;}   /* no stray gap when there is nothing to say */
.progress-bar{height:5px;background:var(--wall-lo);border-radius:99px;overflow:hidden;}
.progress-bar>*{height:100%;background:var(--blue);}

.info-tip{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:15px;height:15px;border-radius:50%;border:1px solid var(--edge-hard);
  color:var(--ink-3);font-size:10.5px;font-weight:600;cursor:help;flex-shrink:0;
}
.info-tip:hover{border-color:var(--ink);color:var(--ink);}
.info-tip-box{
  display:none;position:absolute;bottom:calc(100% + 8px);left:50%;
  transform:translateX(-50%);z-index:50;width:242px;padding:10px 12px;
  font-size:11.5px;line-height:1.5;color:var(--ink-2);text-align:left;
  font-weight:400;text-transform:none;letter-spacing:0;box-shadow:var(--lift-3);
}
.info-tip:hover .info-tip-box,.info-tip:focus-visible .info-tip-box{display:block;}
.info-tip-box b{color:var(--ink);}

/* States. An empty wall says what to hang on it. */
.empty-state{text-align:center;padding:52px 20px;color:var(--ink-3);}
.empty-state svg{width:40px;height:40px;stroke:var(--edge-hard);margin-bottom:14px;}
.empty-state-title{font-size:14px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2);margin-bottom:6px;}
.empty-state-sub{font-size:13px;max-width:380px;margin:0 auto;line-height:1.6;}
.skeleton-block,.skeleton-line,.skeleton-art,.img-placeholder,.tile-placeholder{
  background:linear-gradient(90deg,var(--wall-lo) 0%,var(--wall) 45%,var(--wall-lo) 90%);
  background-size:220% 100%;animation:stockShift 1.5s linear infinite;border-radius:var(--tag-r);
}
.skeleton-line{height:11px;margin:6px 0;}
.skeleton-card{border:1px solid var(--edge-soft);padding:8px;border-radius:var(--tag-r);}
.skeleton-art{aspect-ratio:63/88;}
@keyframes stockShift{to{background-position:-220% 0;}}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;}
}

/* ======================================================= 7. THE HOLDINGS ====
   Each card is set INTO the ground in a shallow well, not pinned onto it. The
   well is what gives the surface material: an inset shadow above, a highlight
   below, and the card casting its own shadow down into it. Cards are large on
   purpose -- the previous build ran them at 158px with a price tag underneath
   and the whole page read as discount retail. */
.collection-grid{
  display:grid;gap:clamp(30px,3.4vw,52px) clamp(22px,2.4vw,36px);
  grid-template-columns:repeat(auto-fill,minmax(206px,1fr));
  padding:6px 0 8px;
}
.collection-tile{
  position:relative;background:none;border:0;padding:0;margin:0;
  display:block;cursor:pointer;font-family:inherit;text-align:left;color:inherit;
  /* A 1,100-card collection is a 197,000px tall grid of 37,000 nodes, and the
     browser was laying out and painting every one of them on every frame -- a
     forced layout measured 763ms against the 16ms a smooth scroll gets. This tells
     it to skip the work for anything off screen entirely. `auto` on the intrinsic
     size means the placeholder is replaced by each tile's real height once it has
     been rendered, so the scrollbar stops guessing after the first pass. */
  content-visibility:auto;
  contain-intrinsic-size:auto 320px;
}
/* content-visibility:auto brings paint containment with it, which clips anything
   drawn outside the tile -- and the hover lifts and scales the card past its own
   edges, so the top of it was being sliced off. The hovered tile drops containment;
   it is on screen and already laid out, so there is nothing to regain by containing
   it, and every other tile keeps the saving. */
.collection-tile:hover,.collection-tile:focus-within{
  content-visibility:visible;
  z-index:3;
}
/* the well */
.tile-thumb,.holo-tilt{
  position:relative;display:block;width:100%;aspect-ratio:63/88;
  border-radius:5px;overflow:hidden;background:var(--stone-3);
  box-shadow:var(--lift-2);
  transform-style:preserve-3d;
  transition:transform .45s cubic-bezier(.16,1,.3,1),box-shadow .45s cubic-bezier(.16,1,.3,1);
}
.tile-thumb img,.holo-tilt img{width:100%;height:100%;object-fit:cover;display:block;}
.collection-tile>.tile-thumb{
  /* the card sits inside a padded recess drawn on the ground itself */
  margin:0;
}
/* No well on the grid. A recess presents ONE object; drawn 130 times it stops
   being a presentation and becomes wallpaper, and the grey frame competes with
   the card art it is supposed to serve. The vitrine keeps its well because there
   is only ever one card in it. Compared both side by side before cutting this. */
.collection-tile{isolation:isolate;}
.collection-tile:hover .tile-thumb,.collection-tile:focus-visible .tile-thumb{
  transform:translateY(-7px) scale(1.012);box-shadow:var(--lift-3);
}
/* Foil catches the light as the card turns. One sweep, bounded. */
.holo-foil::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .3s;
  background:linear-gradient(var(--ang,110deg),transparent 34%,rgba(255,255,255,.34) 47%,
    rgba(200,226,255,.18) 55%,transparent 69%);
}
.collection-tile:hover .holo-foil::after,.holo-active.holo-foil::after{opacity:1;}

/* The card's own line: name, set, value. No tag, no box, no rule -- just space. */
.tile-body{
  position:relative;background:none;border:0;box-shadow:none;border-radius:0;
  padding:17px 3px 0;display:flex;flex-direction:column;gap:5px;min-width:0;
}
.tile-name{
  font-size:14.5px;font-weight:550;letter-spacing:-.008em;color:var(--graphite);
  line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  /* Ellipsis only truncates once the box is allowed to be narrower than the text.
     As a grid/flex child the default min-width:auto forbids exactly that, which is
     how one long card name used to push the whole phone layout sideways. */
  display:block;min-width:0;max-width:100%;
}
.tile-set,.tile-meta{
  font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.13em;
  color:var(--slate-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tile-meta{display:flex;align-items:center;gap:6px;}
.tile-price-row{display:flex;align-items:baseline;gap:10px;margin-top:8px;}
.tile-price{
  font-size:21px;font-weight:400;color:var(--graphite);letter-spacing:-.032em;line-height:1;
}
.tile-delta{
  display:inline-flex;align-items:center;gap:3px;flex-shrink:0;
  font-size:11.5px;font-weight:600;font-variant-numeric:tabular-nums;
}
.tile-delta svg{width:9px;height:9px;}
.tile-delta.pos{color:var(--up);}
.tile-delta.neg{color:var(--down);}
.tile-delta.neutral{color:var(--slate-2);}
/* At three or four cards across there is no room for price + cash + percent on one
   line, and the row was overflowing the tile and clipping numbers mid-digit. The
   percentage stays, the cash goes, and nothing is cut. */
@media (max-width:520px){
  .tile-price-row{gap:6px;}
  .tile-price{font-size:17px;}
  .tile-delta{font-size:11px;}
  .tile-delta-cash{display:none;}
}

/* Chips on the card itself. */
/* The quantity is authored inline in the meta line, where it belongs -- it is a
   fact about the holding, not a sticker. The absolute positioning here was mine
   and it threw the chip onto the card art, straight over the name and HP on most
   Pokemon cards. */
.tile-qty-chip{
  display:inline-flex;align-items:center;padding:1px 6px;border-radius:4px;
  background:var(--stone-2);border:1px solid var(--stone-3);color:var(--slate);
  font-size:10.5px;font-weight:600;letter-spacing:.02em;
  font-variant-numeric:tabular-nums;margin-left:2px;
}
.tile-favourite-btn,.tile-listed-btn{
  position:absolute;top:7px;right:7px;z-index:2;
  width:28px;height:28px;padding:0;display:grid;place-items:center;
  background:rgba(255,255,255,.94);border:0;color:var(--slate);
  border-radius:4px;box-shadow:var(--lift-1);opacity:0;
  transition:opacity .18s,color .15s;
}
.tile-listed-btn{right:41px;}
/* Hover (or keyboard focus) only, including on cards already marked. Leaving them
   lit on active cards meant a shelf of favourites carried a permanent row of chrome
   over the artwork, which is the one thing the tile exists to show.
   Trade-off worth naming: which cards are favourited or listed is no longer readable
   at a glance from the grid. */
.collection-tile:hover .tile-favourite-btn,.collection-tile:hover .tile-listed-btn,
.tile-favourite-btn:focus-visible,.tile-listed-btn:focus-visible{opacity:1;}
.tile-favourite-btn svg,.tile-listed-btn svg{width:14px;height:14px;}
.tile-favourite-btn.active{color:var(--brass-lo);}
.tile-listed-btn.active{color:var(--up);}
.tile-flash-gold{animation:valueLift 1.6s ease-out 1;}
@keyframes valueLift{0%{color:var(--up)}100%{color:var(--graphite)}}

/* Graded cards name their cert quietly under the card, not on a coloured strip. */
.tile-slab-strip{
  display:flex;align-items:center;gap:6px;margin-top:2px;
  font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.12em;color:var(--brass-lo);
}
.tile-slab-company{color:var(--slate-2);}
.tile-slab-grade{font-variant-numeric:tabular-nums;}

/* Section labels. Quiet: a hairline and a small tracked label, nothing shouted. */
.set-row,.set-top{
  display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  margin:52px 0 22px;padding-bottom:0;border-bottom:0;
}
.set-row::after,.set-top::after{content:'';flex:1;height:1px;background:var(--stone-3);}
.set-row:first-child,.set-top:first-child{margin-top:8px;}
.set-name,.set{
  font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.18em;color:var(--slate-2);
}
.set-count{
  font-size:11px;font-weight:500;color:var(--slate-2);order:9;
  font-variant-numeric:tabular-nums;letter-spacing:.04em;
}
.set-logo-img{height:20px;max-width:110px;width:auto;object-fit:contain;opacity:.8;}

/* The filter is the shop's own index card: wide, plain, always reachable. */
.collection-filter-input{max-width:340px;}
.collection{display:block;}

/* Tracked holdings: the counter's stock list, not a second wall. Each row is a
   small thumb with the card beside it and its figure underneath -- compact, so a
   dashboard can carry a dozen without becoming a grid of its own. */
.holdings-grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(268px,1fr));}
.holding-tile{
  display:flex;flex-direction:column;gap:9px;cursor:pointer;text-align:left;
  font-family:inherit;color:inherit;padding:11px 12px;
  background:var(--stock);border:1px solid var(--edge);border-radius:var(--tag-r);
  box-shadow:var(--lift-1);transition:box-shadow .18s,border-color .18s;
}
.holding-tile:hover{box-shadow:var(--lift-2);border-color:var(--ink);}
.holding-head{display:flex;align-items:center;gap:10px;min-width:0;}
.holding-thumb{
  width:38px;height:53px;flex-shrink:0;border-radius:3px;overflow:hidden;
  background:var(--wall-lo);box-shadow:0 1px 3px rgba(16,28,42,.18);
}
.holding-thumb img{width:100%;height:100%;object-fit:cover;display:block;}
.holding-head>div:not(.holding-thumb){min-width:0;flex:1;}
.holding-name{font-size:12.5px;font-weight:700;color:var(--ink);
  line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.holding-set{font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.08em;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.holding-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;
  padding-top:8px;border-top:1px solid var(--edge-soft);}
.holding-foot>div{display:flex;flex-direction:column;gap:1px;min-width:0;}
.holding-value{font-size:15px;font-weight:600;color:var(--ink);line-height:1.1;}
.holding-value-label{font-size:10px;font-weight:600;text-transform:uppercase;
  letter-spacing:.1em;color:var(--ink-3);}
.holding-change{font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;flex-shrink:0;}
.holding-grade{flex-shrink:0;font-size:10px;font-weight:600;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);
  background:var(--wall);border:1px solid var(--edge);padding:2px 5px;border-radius:var(--tag-r);}

/* ====================================================== 8. THE SHOWCASE ====
   Opening a card is the vitrine again at full size: the card lifted out onto its
   own lit plinth, everything else deferring to it. The left column is a stage,
   not a sidebar -- it gets a real light source, the card gets a long cast shadow,
   and one raking sheen crosses the glass. */
.detail-modal{
  width:min(1140px,100%);padding:0;border-radius:12px;
  background:#fff;border:1px solid var(--stone-3);
  /* Capped and scrollable, in the SAME rule as everything else about this element.
     It used to be an inline style, which beat the `overflow:hidden` that was sitting
     here; moved into its own rule earlier in the sheet it lost to it instead, and a
     card taller than the window had 1,180px of itself clipped off with no way to
     reach it. Same specificity, later wins -- so there is now only one rule. */
  max-height:85vh;overflow-y:auto;overflow-x:hidden;
}

/* Phone: the card goes full screen and the BACKDROP does the scrolling. Has to sit
   after the rule above -- media queries add no specificity, so at equal specificity
   whichever comes last wins, and placed earlier this was silently overridden by the
   desktop cap on the very phones it was written for. */
@media (max-width:760px){
  /* One scroller, not two. A dialog that scrolls inside a backdrop that also scrolls
     is two momentum-scroll surfaces fighting on iOS, which is what made opening a
     card feel like it was coming apart. */
  .modal-bg:has(.detail-modal){padding:0;}
  .detail-modal{
    max-height:none;overflow:visible;width:100%;min-height:100%;height:auto;flex:0 0 auto;
    margin:0;border-radius:0;border-left:0;border-right:0;padding:0 16px 24px;
  }
  .detail-topbar{display:flex;margin:0 -16px 14px;}
}
.detail-layout{display:grid;grid-template-columns:minmax(250px,352px) 1fr;gap:0;align-items:stretch;}

.detail-image-col{
  position:relative;padding:38px 32px 40px;
  background:
    radial-gradient(120% 70% at 26% -8%, rgba(255,255,255,1), transparent 62%),
    linear-gradient(168deg,var(--stone-0) 0%,var(--stone-1) 52%,var(--stone-2) 100%);
  border-right:1px solid var(--stone-3);
  display:flex;flex-direction:column;gap:20px;
}
.detail-image-col::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(108deg,transparent 40%,rgba(255,255,255,.42) 48%,transparent 57%);
}
.detail-image-col>*{position:relative;z-index:1;}
.detail-image-col .holo-tilt,.detail-image-col>img{
  width:100%;border-radius:9px;background:var(--stone-3);
  box-shadow:0 4px 8px rgba(20,30,45,.14), 0 30px 56px -18px rgba(20,30,45,.44),
             0 60px 100px -40px rgba(20,30,45,.30);
}

.detail-info-col{padding:32px 34px 36px;min-width:0;background:#fff;}
.detail-title-actions{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;flex-wrap:wrap;}
#detailTitle{font-size:26px !important;font-weight:500;letter-spacing:-.024em;line-height:1.15;}
.detail-list-btn{display:inline-flex;align-items:center;gap:7px;}
.detail-list-btn svg,.detail-list-icon{width:14px;height:14px;}
.detail-list-btn.active{background:var(--up);border-color:var(--up);color:#fff;}

/* Pricing leads; the card's own rules text is reference and sits below it. */
.card-info-section{margin-top:30px;padding-top:24px;border-top:1px solid var(--stone-3);}
.card-text{font-size:14px;line-height:1.65;color:var(--slate);margin:10px 0 0;max-width:68ch;}
.attack-dmg{font-weight:600;color:var(--graphite);}
.card-box{padding:15px 16px;margin:12px 0;background:var(--stone-0);border-color:var(--edge-soft);box-shadow:none;}
.name{font-weight:600;color:var(--graphite);}

.spec-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:0;
  border-top:1px solid var(--stone-3);margin-top:18px;}
.spec-item{
  padding:16px 18px 16px 0;font-size:14px;color:var(--graphite);
  border-bottom:1px solid var(--edge-soft);
}
.spec-key{display:block;font-size:10.5px;font-weight:600;text-transform:uppercase;
  letter-spacing:.14em;color:var(--slate-2);margin-bottom:5px;}

/* Price activity: one ink line on a clean sheet, figures at real size. */
.chart-panel{padding:20px 22px 16px;margin:20px 0;background:var(--stone-0);border-color:var(--edge-soft);box-shadow:none;}
.chart-panel-header{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:14px;}
.chart-panel-title{font-size:10.5px;letter-spacing:.16em;}
.chart-range-row{display:flex;border:1px solid var(--stone-3);border-radius:7px;overflow:hidden;background:#fff;}
.chart-readout{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:12px;}
.chart-readout-price{font-size:34px;font-weight:300;letter-spacing:-.035em;color:var(--graphite);line-height:1;}
.chart-readout-delta{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;}
.chart-readout-date{font-size:11.5px;font-weight:500;color:var(--slate-2);letter-spacing:.02em;}
.chart-readout-chip{font-size:10.5px;font-weight:600;color:var(--slate-2);
  border:1px solid var(--stone-3);border-radius:5px;padding:3px 8px;background:#fff;}

.ebay-listings{margin-top:18px;border-top:1px solid var(--stone-3);padding-top:16px;}
.ebay-listings-toggle{
  display:inline-flex;align-items:center;gap:8px;background:none;border:0;padding:4px 0;box-shadow:none;
  color:var(--brass-lo);font-size:13px;font-weight:550;cursor:pointer;
}
.ebay-listings-toggle:hover{background:none;text-decoration:underline;}
.ebay-listings-caret{transition:transform .18s;display:inline-flex;}
.ebay-listings-caret svg{width:10px;height:10px;}
.ebay-listings.open .ebay-listings-caret{transform:rotate(90deg);}
.ebay-listings-rows{display:none;margin-top:10px;}
.ebay-listings.open .ebay-listings-rows{display:block;}
.ebay-listing-row{
  display:flex;align-items:center;gap:13px;padding:11px 0;
  border-bottom:1px solid var(--edge-soft);text-decoration:none;color:inherit;
}
.ebay-listing-row:hover{background:var(--stone-0);}
.ebay-listing-row img,.ebay-listing-noimg{
  width:42px;height:42px;flex-shrink:0;object-fit:cover;border-radius:5px;
  background:var(--stone-2);border:1px solid var(--edge-soft);
}
.ebay-listing-title{flex:1;min-width:0;font-size:13px;color:var(--slate);line-height:1.4;
  overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;}
.ebay-listing-price{font-size:15px;font-weight:500;color:var(--graphite);
  font-variant-numeric:tabular-nums;flex-shrink:0;letter-spacing:-.02em;}
.ebay-price-tag{font-variant-numeric:tabular-nums;font-weight:550;}
.ebay-wordmark{font-weight:600;letter-spacing:-.02em;}

.listing-title-row{display:flex;gap:9px;align-items:stretch;margin-top:10px;}
.listing-title-input{flex:1;font-size:13.5px;background:var(--stone-0);}
.listing-copy-btn{flex-shrink:0;}
.listing-copy-btn.copied{background:var(--up);border-color:var(--up);color:#fff;}
.listing-title-hint{font-size:12px;color:var(--slate-2);margin-top:7px;}

@media (max-width:900px){
  .detail-layout{grid-template-columns:1fr;}
  .detail-image-col{border-right:0;border-bottom:1px solid var(--stone-3);
    padding:26px 26px 28px;align-items:center;}
  .detail-image-col .holo-tilt,.detail-image-col>img{max-width:300px;margin:0 auto;}
  .detail-info-col{padding:24px 22px 28px;}
  #detailTitle{font-size:22px !important;}
}

/* ------------------------------------------------------------- 9. SLABS ---
   A graded card is presented as what it physically is: a sealed case with a
   printed label across the top. Ink label, white window, cert set in tabular
   figures -- the same object the grading companies ship. */
.slab-case{padding:0;overflow:hidden;background:var(--stock);}
.slab-label{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:9px;
  padding:8px 11px;background:var(--ink);color:var(--ink-on-dark);
}
.slab-label-grid{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;}
.slab-logo-wrap{display:flex;align-items:center;gap:6px;}
.slab-logo-img{height:22px;width:auto;object-fit:contain;display:block;}
.slab-company-wordmark{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;}
.slab-l1,.slab-l2,.slab-l3,.slab-r1,.slab-r2,.slab-r3{
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.slab-l1,.slab-r1{font-size:10px;font-weight:600;}
.slab-r1,.slab-r2,.slab-r3{text-align:right;opacity:.8;}
.slab-grade-number{font-size:20px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1;}
.slab-cert{font-size:10px;font-variant-numeric:tabular-nums;letter-spacing:.06em;opacity:.75;}
.slab-window{padding:12px;background:linear-gradient(180deg,#fff,var(--wall));}
.slab-window img{width:100%;display:block;border-radius:4px;box-shadow:var(--lift-2);}
.slab-label-bottom{padding:6px 11px;background:var(--wall-lo);border-top:1px solid var(--edge);
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);}

/* Hover preview of a card's art, lifted off the wall. */
/* Row thumbnail: a card at reading size beside its name, not a second copy of the
   artwork. Fixed box with the real 63:88 card ratio so rows stay a uniform height
   whatever the source image happens to measure. */
.card-thumb{
  width:32px;height:45px;object-fit:cover;border-radius:2px;flex:0 0 auto;
  background:var(--stone-2);box-shadow:0 1px 2px rgba(20,23,28,.18);
}
.card-thumb-empty{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:45px;border-radius:2px;flex:0 0 auto;
  background:var(--stone-2);color:var(--slate-2);font-size:15px;
}
/* The name cell is picture-then-text, so it needs to be a row rather than relying on
   inline flow, which was letting the image sit on its own line. */
#collectionTable tbody td:nth-child(2),
.holding-name-cell{display:flex;align-items:center;gap:10px;}

/* Notes sit last and read as an aside, not another price field. */
.detail-notes-row{margin-top:18px;padding-top:16px;border-top:1px solid var(--edge-soft);}
.detail-notes-row label{color:var(--slate-2);}

/* A tab that names something not built yet. Struck through and dimmed so it reads as
   a placeholder at a glance, but still hoverable and clickable so it can say why. */
.tab-soon{
  position:relative;color:var(--slate-2);text-decoration:line-through;
  text-decoration-thickness:1px;text-decoration-color:var(--stone-3);cursor:default;
}
.tab-soon:hover{color:var(--slate);}
.tab-soon::after{
  content:'Coming soon';position:absolute;left:50%;top:calc(100% + 6px);
  transform:translateX(-50%) translateY(-3px);white-space:nowrap;pointer-events:none;
  background:var(--graphite);color:var(--stone-0);font-size:10.5px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;text-decoration:none;
  padding:5px 9px;border-radius:3px;opacity:0;transition:opacity .14s,transform .14s;
  box-shadow:var(--lift-2);z-index:50;
}
.tab-soon:hover::after,.tab-soon:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0);}
@media (prefers-reduced-motion:reduce){.tab-soon::after{transition:none;}}

.cv-toast{
  position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(10px);
  z-index:120;max-width:min(420px,calc(100vw - 32px));
  background:var(--graphite);color:var(--stone-0);
  font-size:13.5px;font-weight:500;line-height:1.45;
  padding:11px 18px;border-radius:4px;box-shadow:var(--lift-4);
  opacity:0;pointer-events:none;transition:opacity .18s,transform .18s;
}
.cv-toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
/* Clear of the phone's bottom bar, which is fixed over the same corner. */
@media (max-width:900px){.cv-toast{bottom:calc(70px + env(safe-area-inset-bottom));}}
@media (prefers-reduced-motion:reduce){.cv-toast{transition:none;}}

/* ------------------------------------------------------ SETS / CHASE VIEW ----
   Three levels: game, set, cards. The cards level leads with "the hits" -- the few
   worth opening packs for -- because that is the question being asked, and burying
   it under 180 commons in set order would answer a different one. */
.chase-back{align-self:flex-start;}
#chaseSetFilter{margin:10px 0 4px;}
.chase-set-list{display:flex;flex-direction:column;margin-top:10px;}
.chase-set-row{
  display:flex;align-items:center;gap:18px;width:100%;text-align:left;cursor:pointer;
  background:none;border:0;border-bottom:1px solid var(--edge-soft);
  padding:14px 6px;font-family:inherit;color:var(--ink);
}
.chase-set-row:hover{background:var(--wall);}
/* Set logos are the thing people actually recognise a set by, and at 54x34 they were
   a footnote beside the name. Given real room they do the identifying and the text
   becomes the confirmation. */
.chase-set-logo{width:104px;height:58px;flex:0 0 auto;display:grid;place-items:center;}
.chase-set-logo img{max-width:100%;max-height:58px;object-fit:contain;}
.chase-set-text{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;}
.chase-set-name{font-size:15px;font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.chase-set-meta{font-size:11.5px;color:var(--slate-2);}
.chase-set-caret{width:12px;height:12px;flex:0 0 auto;color:var(--slate-2);transform:rotate(-90deg);}

.chase-hits{margin:18px 0 6px;}
.chase-hits-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:10px;}
.chase-hits-note{font-size:11.5px;color:var(--slate-2);}
/* A scrolling shelf, not a grid: the hits are a short ranked list and reading them
   left to right in order keeps the ranking legible on any width. */
.chase-hits-row{display:flex;gap:14px;overflow-x:auto;padding:2px 2px 10px;scroll-snap-type:x proximity;}
.chase-hit{flex:0 0 116px;cursor:pointer;scroll-snap-align:start;}
.chase-hit-art{position:relative;border-radius:4px;overflow:hidden;background:var(--stone-2);
  aspect-ratio:63/88;box-shadow:var(--lift-2);transition:transform .18s,box-shadow .18s;}
.chase-hit:hover .chase-hit-art{transform:translateY(-3px);box-shadow:var(--lift-3);}
.chase-hit-art img{width:100%;height:100%;object-fit:cover;display:block;}
.chase-hit-have{
  position:absolute;left:5px;top:5px;background:var(--up);color:#fff;
  font-size:9px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  padding:3px 6px;border-radius:2px;
}
.chase-hit-name{margin-top:7px;font-size:11.5px;font-weight:550;line-height:1.3;
  display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.chase-hit-price{font-size:13px;font-weight:600;color:var(--brass-lo);font-variant-numeric:tabular-nums;}

.chase-controls{display:flex;align-items:center;justify-content:space-between;
  gap:12px;flex-wrap:wrap;margin:16px 0 12px;}
.chase-owned-toggle{display:flex;align-items:center;gap:7px;cursor:pointer;
  font-size:12.5px;font-weight:500;color:var(--ink);text-transform:none;letter-spacing:0;}
.chase-owned-toggle input{accent-color:var(--blue);width:14px;height:14px;margin:0;flex:0 0 auto;}

/* A rarer printing of the same card, named and priced beside the card's own price
   rather than replacing it. */
/* Label and price are separate cells so only the label ever truncates. The price is
   the reason the pill is there; an ellipsis through "$93.2..." would be worse than
   no pill at all. */
.chase-variant-note{
  display:flex;align-items:baseline;justify-content:space-between;gap:4px;
  margin-top:2px;font-size:9px;font-weight:600;letter-spacing:.01em;
  border:1px solid var(--edge);border-radius:2px;padding:2px 4px;
  background:var(--wall);color:var(--slate);
}
.chase-variant-note .cvn-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.chase-variant-note .cvn-price{flex:0 0 auto;white-space:nowrap;font-variant-numeric:tabular-nums;}
/* Carries the same colour language as the variant pills in the card view, so a
   Master Ball print reads purple wherever it appears. */
.chase-variant-note.pill-masterball{background:#EFE6F8;border-color:#8B5FBF;color:#4C2A75;}
.chase-variant-note.pill-pokeball{background:#FBE7E6;border-color:#CE5B52;color:#8A2B23;}
.chase-variant-note.pill-loveball{background:#FCE7F0;border-color:#CE6796;color:#8A2A55;}
.chase-variant-note.pill-friendball{background:#E4F3E4;border-color:#68A868;color:#265A26;}
.chase-variant-note.pill-cosmos{background:#E7E9F8;border-color:#7276C4;color:#33377C;}
.chase-variant-note.pill-holo{background:#E1F1FB;border-color:#63AEDA;color:#175A83;}
.chase-variant-note.pill-reverse{background:#E2ECF9;border-color:#6C93CC;color:#1F4477;}
.chase-variant-note.pill-firsted{background:#F8F0D8;border-color:#C6A340;color:#6A5008;}
.chase-variant-note.pill-stamp{background:#F2EFE7;border-color:#A99B7E;color:#5A4F38;}
.chase-variant-note.pill-stamp-play{background:#E8F0F7;border-color:#6389B0;color:#26466B;}
.chase-variant-note.pill-stamp-ebgames{background:#EAF1E8;border-color:#6E9A62;color:#2F4C27;}
.chase-variant-note.pill-stamp-center{background:#FBEAEA;border-color:#C36A6A;color:#7C2B2B;}
.chase-variant-note.pill-stamp-prerelease{background:#EDECF6;border-color:#8886B8;color:#413E70;}
.chase-variant-note.pill-stamp-staff{background:#F6EBF2;border-color:#B172A0;color:#6D2B58;}
.chase-source-note{
  margin-top:22px;padding-top:14px;border-top:1px solid var(--edge-soft);
  font-size:12px;line-height:1.6;color:var(--slate-2);max-width:70ch;
}
.chase-card-foot{display:flex;align-items:baseline;justify-content:space-between;gap:6px;}
.chase-card-price{font-size:10.5px;font-weight:700;color:var(--brass-lo);
  font-variant-numeric:tabular-nums;white-space:nowrap;}
/* A hit inside the full grid keeps a brass edge so it stays findable when the list
   is sorted back into set order. */
.chase-card.is-hit img{box-shadow:0 0 0 2px var(--brass);}
@media (prefers-reduced-motion:reduce){.chase-hit-art{transition:none;}}
@media (max-width:620px){
  .chase-hit{flex-basis:96px;}
  .chase-set-logo{width:42px;}
}

/* In flow, not fixed. Fixed meant the photo you just took stayed pinned to the same
   spot on screen while you scrolled the results underneath it, sitting on top of the
   cards you were trying to choose between. It belongs with the scan panel, and it
   should scroll away like everything else. */
.thumb-preview{
  display:block;width:104px;margin:12px 0 0;border-radius:7px;
  box-shadow:var(--lift-1);border:1px solid var(--edge);background:var(--stock);
}

/* ==================================================== 10. THE VITRINE ======
   The signature. The collection's strongest holding presented as an object in a
   lit well, with the valuation beside it at full scale. This is the thesis made
   literal: every card here is worth something, so one is shown as worth looking
   at. It replaces the stat-card dashboard the old build opened with. */
.rpg-dash{padding:0;background:none;border:0;box-shadow:none;margin:0;}
.rpg-dash-grid{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:clamp(24px,3vw,44px);align-items:start;}
.rpg-order-2,#rpgCharacterPanel,.rpg-quest-list,
.rpg-char-row,.rpg-avatar,.rpg-coins-row,.rpg-lvl-chip,.rpg-xp-row,.rpg-scanline,
.flame-text-canvas,.flame-fx-canvas,.hero-value-flame-wrap canvas{display:none !important;}
@media (max-width:1080px){.rpg-dash-grid{grid-template-columns:1fr;}}
.rpg-col{min-width:0;display:flex;flex-direction:column;gap:20px;}
.rpg-panel{padding:22px 24px;background:#fff;border:1px solid var(--stone-3);border-radius:var(--tag-r);box-shadow:var(--lift-1);}
.rpg-panel-title{margin-bottom:14px;}

.vitrine{
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:clamp(30px,5vw,80px);align-items:center;
  padding:clamp(44px,6.5vw,92px) 0 clamp(40px,5vw,72px);
}
.vitrine-well{
  position:relative;padding:26px;border-radius:6px;flex-shrink:0;
  background:linear-gradient(160deg,var(--stone-2),var(--stone-1) 55%,var(--stone-2));
  box-shadow:var(--sink-2),0 1px 0 rgba(255,255,255,.9);
}
.vitrine-card{
  position:relative;display:block;width:clamp(178px,19vw,252px);aspect-ratio:63/88;
  border-radius:8px;overflow:hidden;background:var(--stone-3);
  transform-style:preserve-3d;transition:transform .5s cubic-bezier(.16,1,.3,1);
  box-shadow:var(--lift-4);cursor:pointer;
}
.vitrine-card img{width:100%;height:100%;object-fit:cover;display:block;}
.vitrine-card::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:.42;
  background:linear-gradient(var(--ang,105deg),transparent 36%,rgba(255,255,255,.30) 48%,
    rgba(255,255,255,.10) 56%,transparent 68%);
}
.vitrine-body{min-width:0;}

/* The valuation. Real scale, light weight, tight tracking, and air around it.
   This is where the whole page's confidence lives. */
.hero-stat{padding:0;border:0;}
.hero-stat .eyebrow{display:block;margin-bottom:0;}
.hero-value,.hero-value-flame-wrap{
  display:flex;align-items:flex-start;flex-wrap:nowrap;gap:0;
  font-size:clamp(52px,8.6vw,112px);font-weight:300;line-height:.9;
  letter-spacing:-.045em;color:var(--graphite);margin-top:16px;
}
/* Sized in em so it stays in proportion on the big portfolio figure, with a hard
   floor so it doesn't vanish everywhere else -- on a 15px line .18em works out to
   about 3px, which is a smudge rather than a currency code. */
.balance-currency{
  font-size:max(10.5px, .18em);font-weight:600;letter-spacing:.1em;color:var(--slate-2);
  line-height:1;margin:.42em 0 0 max(4px, .26em);white-space:nowrap;flex-shrink:0;
}
/* The privacy toggle is a control, not part of the figure -- it sits clear of it
   and never inherits the display size. */
.hero-value .ice-icon-btn{
  font-size:14px;align-self:center;margin:0 0 0 24px;flex-shrink:0;
  letter-spacing:normal;font-weight:400;
}
.portfolio-history-change{font-size:13.5px;font-weight:550;font-variant-numeric:tabular-nums;}

.vitrine-facts{display:flex;margin-top:36px;border-top:1px solid var(--stone-3);}
.vitrine-fact{padding:20px 30px 0 0;margin-right:30px;border-right:1px solid var(--stone-3);}
.vitrine-fact:last-child{border-right:0;margin-right:0;}
.vitrine-fact b{display:block;font-size:25px;font-weight:400;line-height:1;letter-spacing:-.03em;font-variant-numeric:tabular-nums;}
.vitrine-fact span{display:block;margin-top:8px;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.14em;color:var(--slate-2);}
.vitrine-lede{margin-top:26px;font-size:14.5px;line-height:1.6;color:var(--slate);max-width:46ch;}
.vitrine-lede b{font-weight:600;color:var(--graphite);}

.rpg-spec-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--edge-soft);font-size:12.5px;color:var(--slate);}
.rpg-spec-row:last-child{border-bottom:0;}
.rpg-spec-val{font-weight:600;color:var(--graphite);}
[class*="tier-"]{color:var(--graphite);}

@media (max-width:860px){
  .vitrine{grid-template-columns:minmax(0,1fr);gap:32px;padding-top:32px;}
  .vitrine-card{width:min(58vw,220px);}
  .vitrine-facts{flex-wrap:wrap;}
}
@media (max-width:620px){
  /* Three facts, three columns. Wrapping dropped the third onto a row of its own
     beside an empty cell, which reads as a rendering fault rather than a layout --
     and the desktop gutters (30px padding plus 30px margin per fact) were most of
     the width once the screen was only 335px across. */
  .vitrine-facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;margin-top:26px;}
  .vitrine-fact{padding:16px 12px 0 0;margin-right:12px;}
  .vitrine-fact b{font-size:20px;}
  .vitrine-fact span{font-size:9px;letter-spacing:.09em;margin-top:6px;}
  /* The card was taking 94% of the first screen, so the portfolio value -- the one
     number anyone opens this app to see -- began below the fold. */
  .vitrine{padding-top:20px;gap:22px;}
  .vitrine-card{width:min(46vw,178px);}
  /* The well is a grid item and was stretching to the full column, so on a phone it
     drew 69px of dark frame either side of a 175px card while the padding was only
     16px. Desktop was always right because the card is wide enough to fill it. */
  .vitrine-well{padding:16px;width:fit-content;justify-self:center;}
  .vitrine-lede{margin-top:18px;font-size:13.5px;}
}

/* ------------------------------------------------- 11. ADD CARDS / SCAN --- */
.result-card{
  display:flex;flex-direction:column;gap:9px;cursor:pointer;
  background:none;border:0;padding:0;text-align:left;font-family:inherit;color:inherit;
}
.result-card img{width:100%;aspect-ratio:63/88;object-fit:cover;border-radius:5px;
  box-shadow:var(--lift-2);transition:box-shadow .3s cubic-bezier(.16,1,.3,1);background:var(--wall-lo);}
.result-card:hover img{box-shadow:var(--lift-3);}
.result-card.selected img{outline:2px solid var(--ink);outline-offset:2px;}
.upload{
  border:1px dashed var(--edge-hard);border-radius:var(--tag-r);background:var(--stock);
  padding:26px 20px;text-align:center;color:var(--ink-3);cursor:pointer;transition:border-color .15s,background .15s;
}
.upload:hover,.upload.detecting{border-color:var(--ink);background:var(--wall);color:var(--ink-2);}

.camera-wrap{position:relative;border-radius:var(--tag-r);overflow:hidden;background:var(--ink);}
.camera,.camera-wrap video{width:100%;display:block;}
.camera-toggle{display:flex;gap:8px;margin-bottom:12px;}
.scan-overlay{position:fixed;inset:0;z-index:65;background:#0b0f14;display:none;}
.scan-overlay.active,.scan-overlay.show{display:block;}
.scan-overlay video{width:100%;height:100%;object-fit:cover;}
/* The capture frame: four corner marks, nothing else over the card. */
.scan-guide{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:min(76vw,330px);aspect-ratio:63/88;pointer-events:none;
  border-radius:8px;box-shadow:0 0 0 100vmax rgba(8,12,18,.62);
}
.scan-guide::before,.scan-guide::after{
  content:'';position:absolute;inset:0;border:2px solid #fff;border-radius:8px;
  clip-path:polygon(0 0,32% 0,32% 2.4%,2.4% 2.4%,2.4% 20%,0 20%,
    0 80%,2.4% 80%,2.4% 97.6%,32% 97.6%,32% 100%,0 100%);
}
.scan-guide::after{transform:scaleX(-1);}
.scan-hint,.scan-status,.scan-working-line{
  position:absolute;left:0;right:0;text-align:center;color:#fff;
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;
}
.scan-hint{bottom:132px;opacity:.8;}
.scan-status{top:26px;}
.scan-working{
  position:absolute;left:50%;bottom:108px;transform:translateX(-50%);
  display:flex;align-items:center;gap:9px;padding:9px 16px;border-radius:999px;
  background:rgba(8,12,18,.7);border:1px solid rgba(255,255,255,.2);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);pointer-events:none;
}
.scan-working p{margin:0;}
/* Was an empty div inheriting text styles, so it drew nothing. Now it is the bit
   that actually moves, which is the whole job: proof the scan is still running. */
.scan-working-line{
  position:static;margin:0;width:13px;height:13px;flex:none;border-radius:50%;
  border:2px solid rgba(255,255,255,.3);border-top-color:#fff;
  animation:scanSpin .7s linear infinite;
}
@keyframes scanSpin{to{transform:rotate(360deg);}}
@media (prefers-reduced-motion:reduce){ .scan-working-line{animation:none;} }
.scan-controls-phone,.scan-controls-desktop{
  position:absolute;bottom:26px;left:0;right:0;display:flex;align-items:center;justify-content:center;gap:22px;
}
.scan-capture-fallback,.scan-close,.scan-flip,.scan-torch{
  width:44px;height:44px;display:grid;place-items:center;border-radius:50%;
  background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.34);color:#fff;padding:0;
}
.scan-capture-fallback{width:66px;height:66px;background:#fff;border:4px solid rgba(255,255,255,.45);}
.scan-close{position:absolute;top:18px;right:18px;bottom:auto;}
.scan-flip svg,.scan-torch svg,.scan-close svg{width:19px;height:19px;}

/* --- Compact panel headings (Add tab) ------------------------------------------
   These panels were opening on two paragraphs of explanation, which on a phone is
   most of a screen spent before you can do anything. The controls lead now and the
   explanation is one tap away, which is also where it belongs -- it is read once. */
.box-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:11px;}
.box-head h3{margin:0;font-size:15.5px;}
.help-btn{
  width:22px;height:22px;flex:none;padding:0;display:grid;place-items:center;
  border-radius:50%;font-family:inherit;font-size:12px;font-weight:700;line-height:1;
  background:none;border:1px solid var(--edge);color:var(--ink-3);cursor:pointer;
  transition:background .15s,border-color .15s,color .15s;
}
.help-btn:hover,.help-btn[aria-expanded="true"]{
  background:var(--ink);border-color:var(--ink);color:var(--ink-on-dark);
}
.box-help{
  margin:0 0 12px;padding:11px 13px;border-radius:8px;
  background:var(--wall);border:1px solid var(--edge-soft);
}
.box-help p{margin:0;font-size:13px;line-height:1.55;color:var(--ink-3);}
.box-help p + p{margin-top:8px;}

.session-tally{
  display:inline-flex;align-items:baseline;gap:7px;margin-bottom:10px;
  font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);
}
.session-tally strong{
  font-size:14px;font-weight:600;letter-spacing:0;color:var(--accent);
  font-variant-numeric:tabular-nums;
}

/* --- Centering ------------------------------------------------------------------
   Two frames over the photo with the border bands between them tinted, so the answer
   is visible before you read the number. The previous build drew eight separate lines
   with a grip dot in the middle of each, which put eight dots across the card face
   with no way to tell what any of them did. */
.cen-layout{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:20px;align-items:start;}
.cen-stage{
  position:relative;background:var(--wall);border:1px solid var(--edge);border-radius:8px;
  overflow:hidden;touch-action:none;min-height:220px;display:grid;place-items:center;
  user-select:none;
}
.cen-stage img{display:block;width:100%;height:auto;pointer-events:none;}
.cen-empty{padding:30px 20px;text-align:center;color:var(--ink-3);font-size:13px;}
.cen-empty p{margin:0 auto 12px;max-width:44ch;}
.cen-empty-why{font-size:12px;opacity:.8;}

.cen-band{
  position:absolute;pointer-events:none;
  background:rgba(224,169,63,.30);
  box-shadow:inset 0 0 0 1px rgba(224,169,63,.55);
  display:grid;
}
/* Each label sits on the ART side of its band, never centred in it. A border band is
   only a few percent wide, so a centred label overflows the card and the stage clips
   it -- the right-hand measurement was being cut off mid-number. */
#cenBandL{place-items:center end;} #cenBandR{place-items:center start;}
#cenBandT{place-items:end center;} #cenBandB{place-items:start center;}
.cen-mm{
  font-size:10px;font-weight:700;color:#fff;background:rgba(20,23,28,.82);
  padding:1px 5px;border-radius:4px;white-space:nowrap;font-variant-numeric:tabular-nums;
  margin:3px;
}

.cen-box{position:absolute;pointer-events:none;}
.cen-box-outer{outline:2px solid rgba(255,255,255,.95);outline-offset:-1px;
  box-shadow:0 0 0 1px rgba(0,0,0,.55), 0 0 0 100vmax rgba(10,14,20,.45);}
.cen-box-inner{outline:2px dashed #E0A93F;outline-offset:-1px;}
.cen-tag{
  position:absolute;top:-8px;left:5px;padding:0 5px;border-radius:3px;
  font-size:8.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  line-height:15px;
}
.cen-box-outer .cen-tag{background:#fff;color:#14171C;}
.cen-box-inner .cen-tag{background:#E0A93F;color:#14171C;top:auto;bottom:-9px;left:auto;right:6px;}

/* Corner handles: on the frame, never over the middle of the art, and big enough for
   a finger without hiding what is underneath. */
.cen-h{
  position:absolute;width:30px;height:30px;padding:0;border:0;background:none;
  pointer-events:auto;cursor:grab;touch-action:none;
}
.cen-h:active{cursor:grabbing;}
.cen-h::after{
  content:'';position:absolute;left:50%;top:50%;width:15px;height:15px;
  margin:-7.5px 0 0 -7.5px;border-radius:50%;
  background:#fff;box-shadow:0 0 0 2px rgba(20,23,28,.65);
}
.cen-box-inner .cen-h::after{background:#E0A93F;}
.cen-h-tl{left:-15px;top:-15px;} .cen-h-tr{right:-15px;top:-15px;}
.cen-h-bl{left:-15px;bottom:-15px;} .cen-h-br{right:-15px;bottom:-15px;}

.cen-side{display:flex;flex-direction:column;gap:12px;}
.cen-readout{border:1px solid var(--edge);border-radius:8px;overflow:hidden;}
.cen-axis{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:9px 12px;font-size:13px;color:var(--ink-3);
  border-bottom:1px solid var(--edge-soft);
}
.cen-axis:last-child{border-bottom:none;}
.cen-axis span{display:flex;flex-direction:column;gap:1px;}
.cen-axis em{font-style:normal;font-size:11px;opacity:.75;font-variant-numeric:tabular-nums;}
.cen-axis strong{font-size:19px;color:var(--text);font-variant-numeric:tabular-nums;}
.cen-axis-worst{background:var(--wall);color:var(--text);font-weight:500;}

.cen-nudge{font-size:11.5px;color:var(--ink-3);}
.cen-nudge-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin-top:5px;}
.cen-nudge-grid button{
  padding:6px 0;border-radius:6px;font-size:11px;font-weight:600;
  background:var(--stock);border:1px solid var(--edge);color:var(--text);cursor:pointer;
}
.cen-nudge-grid button:hover{border-color:var(--ink);}

.cen-verdict{margin:0;font-size:13.5px;line-height:1.55;color:var(--text);}
.cen-grades{display:flex;flex-direction:column;gap:2px;}
.cen-grade{
  display:grid;grid-template-columns:42px minmax(0,1fr) auto 18px;align-items:center;gap:8px;
  padding:6px 9px;border-radius:6px;font-size:12.5px;
}
.cen-grade.pass{background:var(--up-tint);color:var(--up);}
.cen-grade.fail{color:var(--ink-3);opacity:.55;}
.cen-grade-co{font-weight:700;}
.cen-grade-need{font-variant-numeric:tabular-nums;opacity:.8;}
.cen-grade-mark{text-align:right;font-weight:700;}
.cen-caveat{margin:0;font-size:11.5px;line-height:1.5;color:var(--ink-3);}
@media (max-width:860px){
  .cen-layout{grid-template-columns:minmax(0,1fr);}
}

/* --- Bundle run HUD ------------------------------------------------------------
   Everything here sits over a live camera feed, so it keeps to the edges: the
   middle of the screen is where the card goes, and anything floating there is one
   more thing to aim past. Hidden entirely unless the overlay is in bundle mode. */
.bundle-hud{display:none;}
.scan-overlay.bundle .bundle-hud{display:block;}
/* Done replaces the plain X in bundle mode -- finishing a run and closing the
   scanner are the same action, and two controls for it invites the wrong one. */
.scan-overlay.bundle .scan-close{display:none;}
.scan-overlay.bundle .scan-status{display:none;}
.scan-overlay.bundle .scan-hint{
  bottom:206px;left:16px;right:16px;font-size:12px;font-weight:500;
  text-transform:none;letter-spacing:0;opacity:.82;
}
.scan-overlay.bundle.has-cards .scan-hint{display:none;}

.bundle-tally{
  position:absolute;top:16px;left:16px;z-index:2;
  padding:9px 13px;border-radius:12px;
  background:rgba(8,12,18,.62);border:1px solid rgba(255,255,255,.2);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;
}
.bundle-tally-count{display:flex;align-items:baseline;gap:5px;
  font-size:26px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums;}
.bundle-tally-count small{font-size:10px;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;opacity:.62;}
.bundle-tally-value{margin-top:3px;font-size:13px;font-weight:500;
  font-variant-numeric:tabular-nums;color:#EBC77E;}
.bundle-tally-value .balance-currency{font-size:9px;margin-left:3px;opacity:.7;}
.bundle-tally-scope{margin-top:3px;font-size:10px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;opacity:.55;}
.bundle-setup-pills{display:flex;flex-wrap:wrap;max-width:100%;}
.bundle-tally-pending{margin-top:2px;font-size:10px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;opacity:.66;min-height:12px;}

/* In bundle mode the shutter is gone: there is nothing to press per card. This is
   the only run control, and the flip button moves aside to make room for it. */
.scan-overlay.bundle .scan-capture-fallback,
.scan-overlay.bundle #cameraCaptureDesktop{display:none;}
.scan-overlay.bundle .scan-controls-phone{justify-content:flex-start;padding-left:30px;}
.bundle-run{
  position:absolute;left:50%;bottom:24px;transform:translateX(-50%);z-index:3;
  min-width:130px;padding:15px 26px;border-radius:999px;white-space:nowrap;
  font-family:inherit;font-size:14px;font-weight:600;
  background:#fff;color:#14171C;border:none;cursor:pointer;
}
.bundle-run.paused{background:#EBC77E;}
/* The guide stops pulsing when the run is stopped, so the feed itself says so. */
.scan-guide.paused{opacity:.35;}

.bundle-done{
  position:absolute;top:16px;right:16px;z-index:2;
  padding:11px 18px;border-radius:999px;white-space:nowrap;
  background:#fff;color:#14171C;border:none;
  font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;
}

.bundle-last{
  position:absolute;left:16px;right:16px;bottom:158px;z-index:2;
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:9px 14px;border-radius:10px;
  background:rgba(8,12,18,.62);border:1px solid rgba(255,255,255,.18);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;
}
.bundle-last-name{font-size:13px;font-weight:500;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.bundle-last-name.muted{opacity:.55;font-weight:400;}
.bundle-last-price{font-size:13px;font-weight:600;color:#EBC77E;
  font-variant-numeric:tabular-nums;flex:none;}

/* Newest on the left, so the card you just did is where your eye already is. */
.bundle-strip{
  position:absolute;left:0;right:0;bottom:100px;z-index:2;
  display:flex;gap:7px;overflow-x:auto;padding:0 16px 2px;
  scrollbar-width:none;
}
.bundle-strip::-webkit-scrollbar{display:none;}
.bundle-chip{
  position:relative;flex:none;width:42px;height:59px;padding:0;
  border-radius:5px;overflow:hidden;border:1px solid rgba(255,255,255,.3);
  background:rgba(8,12,18,.5);
}
.bundle-chip img{width:100%;height:100%;object-fit:cover;display:block;}
.bundle-chip.unsure{border-color:#E0A93F;box-shadow:0 0 0 1px #E0A93F;}
.bundle-chip-qty,.bundle-chip-flag{
  position:absolute;bottom:0;right:0;min-width:16px;height:15px;padding:0 3px;
  display:grid;place-items:center;font-size:10px;font-weight:700;line-height:1;
  background:rgba(8,12,18,.85);color:#fff;border-top-left-radius:5px;
}
.bundle-chip-flag{top:0;bottom:auto;left:0;right:auto;
  background:#E0A93F;color:#14171C;border-radius:0 0 5px 0;}

/* A card landing needs to register without pulling your eyes off the stack, so it
   is a pulse at the edge of vision rather than anything centred. */
.bundle-flash{position:absolute;inset:0;pointer-events:none;z-index:1;
  box-shadow:inset 0 0 0 0 rgba(235,199,126,0);}
.bundle-flash.show{animation:bundlePulse .42s ease-out;}
.bundle-flash.miss.show{animation:bundlePulseMiss .42s ease-out;}
@keyframes bundlePulse{
  0%{box-shadow:inset 0 0 0 4px rgba(235,199,126,.92);}
  100%{box-shadow:inset 0 0 0 4px rgba(235,199,126,0);}
}
@keyframes bundlePulseMiss{
  0%{box-shadow:inset 0 0 0 4px rgba(196,72,58,.92);}
  100%{box-shadow:inset 0 0 0 4px rgba(196,72,58,0);}
}
@media (prefers-reduced-motion:reduce){
  .bundle-flash.show,.bundle-flash.miss.show{animation:none;}
}

/* --- Bundle review sheet -------------------------------------------------------
   An ordinary modal on the page, so it takes its colours from the theme rather
   than the camera overlay's fixed dark. */
.bundle-review-summary{
  display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;
  margin:0 0 14px;padding-bottom:12px;border-bottom:1px solid var(--edge-soft);
  font-size:14px;color:var(--text);font-variant-numeric:tabular-nums;
}
.bundle-review-summary strong{font-size:19px;font-weight:600;}
.bundle-warn{
  padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;
  background:var(--down-tint);color:var(--down);
}
.bundle-row{
  display:grid;grid-template-columns:26px 38px minmax(0,1fr) auto auto 28px;
  grid-template-areas:'tick img main qty price x' '. var var var var var';
  align-items:center;gap:4px 11px;padding:9px 0;border-bottom:1px solid var(--edge-soft);
}
.bundle-row-pick{grid-area:img;align-self:start;position:relative;display:block;}
.bundle-row img{display:block;}
/* Ticked rows are the ones that will actually be added, so they read as chosen. */
.bundle-row.picked{background:var(--tag-tint);}
.bundle-tick{
  grid-area:tick;width:22px;height:22px;padding:0;display:grid;place-items:center;
  border-radius:6px;border:1.5px solid var(--edge-hard);background:var(--stock);
  color:var(--ink-on-dark);font-size:13px;line-height:1;cursor:pointer;
}
.bundle-row.picked .bundle-tick{background:var(--ink);border-color:var(--ink);}
/* Tapping the picture is how you change a card, so it says so. */
.bundle-row-swap{
  position:absolute;right:-4px;bottom:-4px;width:17px;height:17px;
  display:grid;place-items:center;border-radius:50%;
  font-size:10px;line-height:1;
  background:var(--ink);color:var(--ink-on-dark);border:1.5px solid var(--stock);
}
@media (max-width:520px){ #bundleReviewBg .modal{padding:18px 16px;} }
/* Load failure. Never a blank collection -- see state.js. */
.load-failure{
  position:fixed;left:0;right:0;top:0;z-index:95;display:flex;
  align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
  padding:11px 18px;background:var(--down);color:#fff;
  font-size:13.5px;font-weight:500;
  padding-top:calc(11px + env(safe-area-inset-top));
}
.load-failure button{
  padding:6px 14px;border-radius:999px;background:#fff;color:var(--down);
  border:none;font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;
}

.bundle-review-head{display:flex;align-items:center;justify-content:space-between;gap:12px;}
.bundle-review-head h3{margin:0;}
.bundle-review-close{
  width:30px;height:30px;flex:none;padding:0;display:grid;place-items:center;
  border-radius:8px;background:none;border:none;color:var(--ink-3);
  font-size:22px;line-height:1;cursor:pointer;
}
.bundle-review-close:hover{background:var(--wall);color:var(--ink);}

/* Sticky to the bottom of the sheet, bled to its edges. Both controls stay reachable
   from anywhere in the list, which is the whole point once a run is hundreds long. */
.bundle-actions{
  position:sticky;bottom:0;z-index:2;display:flex;gap:10px;align-items:center;
  margin:16px -26px -26px;padding:14px 26px;
  background:var(--stock);border-top:1px solid var(--edge-soft);
  box-shadow:0 -10px 22px -16px rgba(16,28,42,.55);
}
.bundle-actions #bundleCommitBtn{flex:1;min-width:0;}
.bundle-actions .secondary{flex:none;white-space:nowrap;}
@media (max-width:520px){
  .bundle-actions{margin:14px -16px -16px;padding:12px 16px;}
}
.bundle-row-main{grid-area:main;}
.bundle-row-qty{grid-area:qty;}
/* The printing, on the row. A "?" means the scanner guessed it, which it has to do
   whenever a card exists as both normal and reverse holo -- no photograph settles
   that reliably. */
/* --- Which printing this card is ----------------------------------------
   The printing is the thing most likely to be wrong and the thing that moves the
   price most, and it used to be the smallest, quietest element in the row: a
   10.5px pill that read as a label rather than a control. It is now sized and
   shaped like something you press, and a guess announces itself. */
.bundle-var{
  grid-area:var;justify-self:start;
  display:inline-flex;align-items:center;gap:7px;
  padding:5px 9px;border-radius:8px;
  font-family:inherit;font-size:12px;font-weight:600;white-space:nowrap;
  background:var(--stock);border:1px solid var(--edge);color:var(--ink);
  cursor:pointer;transition:border-color .12s,background .12s;
}
.bundle-var:hover{border-color:var(--ink);background:var(--wall-hi);}
/* The visible control is 28px, which is right for the density of a four-hundred
   row list but under the 44px a thumb wants. The hit area is extended past the
   box instead of the box being grown, so the target is thumb-sized without every
   row getting taller. It stays inside the row, so it cannot steal a neighbour's
   tap. */
.bundle-var{position:relative;}
.bundle-var::after{
  content:'';position:absolute;left:0;right:0;top:50%;
  transform:translateY(-50%);height:44px;
}
.bundle-var-caret{font-size:9px;color:var(--ink-3);margin-left:1px;}
/* A guess is the one thing in this list that wants a decision, so it carries the
   brass and says so in words. No shadow: across four hundred rows a halo on every
   uncertain one stops being emphasis and becomes noise. */
.bundle-var.guessed{background:var(--tag-tint);border-color:var(--brass);color:var(--brass-lo);}
.bundle-var.guessed .bundle-var-caret{color:var(--brass);}
.bundle-var-check{
  font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.09em;
  background:var(--brass);color:#fff;padding:2px 5px;border-radius:4px;
}

/* What the printing looks like, so it can be recognised without being read.
   Meaningful rather than decorative: a foil genuinely is iridescent and a reverse
   genuinely is foiled around the art, so the swatch says something true. */
.bundle-var-swatch{
  width:13px;height:13px;border-radius:3px;flex:0 0 auto;
  border:1px solid rgba(20,23,28,.18);
}
.vs-flat{background:var(--stone-2);}
.vs-foil{background:linear-gradient(135deg,#CBDFEC 0%,#E7D3E2 34%,#F1E4C6 66%,#CCE4D6 100%);}
/* Foiled around a flat window of artwork, which is exactly what a reverse is. */
.vs-reverse{
  background:
    radial-gradient(circle at 50% 50%, var(--stone-1) 0 34%, transparent 35%),
    linear-gradient(135deg,#CBDFEC 0%,#E7D3E2 34%,#F1E4C6 66%,#CCE4D6 100%);
}
.vs-stamp{background:var(--tag-tint);border-color:var(--brass);}

/* --- Choosing the printing ---------------------------------------------- */
.bundle-var-list{display:flex;flex-wrap:wrap;gap:8px;}
.bundle-var-opt{
  display:flex;align-items:center;gap:9px;padding:9px 13px;border-radius:10px;
  background:var(--stock);border:1px solid var(--edge);color:var(--text);
  font-family:inherit;cursor:pointer;text-align:left;
  transition:border-color .12s,background .12s;
}
.bundle-var-opt:hover{border-color:var(--ink-3);background:var(--wall-hi);}
.bundle-var-opt.on{background:var(--ink);border-color:var(--ink);color:var(--ink-on-dark);}
.bundle-var-opt.on .bundle-var-swatch{border-color:rgba(255,255,255,.42);}
/* Name over price, not name beside price. These are the two things being weighed
   up when somebody picks a printing, and side by side at 12px they ran together
   into one string. */
.bundle-var-opt-text{display:flex;flex-direction:column;gap:1px;min-width:0;}
.bundle-var-opt-name{font-size:13px;font-weight:600;letter-spacing:-.005em;}
.bundle-var-price{font-size:12px;font-variant-numeric:tabular-nums;opacity:.72;font-weight:500;}
.bundle-row-price{grid-area:price;}
.bundle-row-x{grid-area:x;}
.bundle-row:last-child{border-bottom:none;}
.bundle-row img{width:38px;height:53px;object-fit:cover;border-radius:4px;
  border:1px solid var(--edge);background:var(--wall);}
.bundle-row.unsure img{border-color:var(--brass);box-shadow:0 0 0 1px var(--brass);}
.bundle-row-main{min-width:0;}
/* Scan number, so a row can be matched to a position in the physical stack. */
.bundle-row-seq{
  display:inline-block;min-width:20px;margin-right:7px;
  font-size:11px;font-weight:700;color:var(--ink-3);
  font-variant-numeric:tabular-nums;
}
.bundle-row-name{font-size:14px;font-weight:500;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bundle-row-native{color:var(--ink-2);}
.bundle-row-meta{font-size:12px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bundle-row-flag{margin-top:2px;font-size:11px;font-weight:600;color:var(--brass-lo);}
/* A wrong language is a wrong card, not a near miss, so it reads in the warning
   colour rather than the same brass as "check the set number". */
.bundle-row-flag.jp{color:var(--down);}
/* Rows are buttons now: the card is how you get to the picker. */
.bundle-row-pick,.bundle-row-main{
  background:none;border:none;padding:0;margin:0;text-align:left;
  font-family:inherit;color:inherit;cursor:pointer;
}
.bundle-row-main{min-width:0;width:100%;}
.bundle-row-pick:hover img,.bundle-row-main:hover .bundle-row-name{opacity:.75;}
.bundle-row-change{margin-top:2px;font-size:11px;color:var(--ink-3);opacity:0;transition:opacity .15s;}
.bundle-row:hover .bundle-row-change,.bundle-row-main:focus-visible .bundle-row-change{opacity:1;}
@media (hover:none){ .bundle-row-change{opacity:1;} }

/* Picker options: one tap target per candidate, priced, so the choice is informed. */
.bundle-opt{
  display:grid;grid-template-columns:40px minmax(0,1fr) auto;align-items:center;gap:12px;
  width:100%;padding:9px 10px;margin-bottom:6px;border-radius:9px;text-align:left;
  background:var(--stock);border:1px solid var(--edge);font-family:inherit;color:var(--text);cursor:pointer;
}
.bundle-opt:hover:not(:disabled){border-color:var(--ink);background:var(--wall-hi);}
.bundle-opt:disabled{cursor:default;background:var(--wall);border-style:dashed;opacity:.9;}
.bundle-opt img{width:40px;height:56px;object-fit:cover;border-radius:4px;
  border:1px solid var(--edge);background:var(--wall);}
.bundle-opt-main{display:flex;flex-direction:column;gap:2px;min-width:0;}
.bundle-opt-name{font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bundle-opt-meta{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bundle-opt-price{font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap;}
.bundle-opt-tag{grid-column:2/-1;justify-self:start;padding:1px 7px;border-radius:999px;
  font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  background:var(--tag-tint);color:var(--brass-lo);}
/* Above the review sheet AND the camera overlay: the picker opens from both, and at
   the shared .modal-bg z-index it was painting underneath whichever was already up,
   which looked like the tap doing nothing at all. */
#bundleFixBg{z-index:78;}
/* Only ever shown to warn. There is no matching "all good" state on purpose: a
   banner you read once and never need again is just clutter on a crowded sheet. */
.bundle-lang{
  margin:12px 0 0;padding:9px 12px;border-radius:8px;font-size:12.5px;line-height:1.5;
  background:var(--down-tint);color:var(--down);
}
.bundle-set-input{
  width:100%;margin-top:5px;font-family:inherit;font-size:14px;
  padding:10px 13px;border-radius:8px;border:1px solid var(--edge);
  background:var(--stock);color:var(--text);
}
.bundle-setlist{max-height:190px;overflow-y:auto;margin-top:6px;}
.bundle-setopt{
  display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:8px 12px;border-radius:7px;
  background:none;border:none;font-family:inherit;font-size:13.5px;color:var(--text);cursor:pointer;
}
.bundle-setopt-sub{font-size:11.5px;color:var(--muted);}
.bundle-setopt.on .bundle-setopt-sub{color:var(--brass-lo);opacity:.85;}
.bundle-setopt:hover{background:var(--wall);}
.bundle-setopt.on{background:var(--tag-tint);color:var(--brass-lo);font-weight:600;}
.bundle-setlist-empty{padding:9px 12px;font-size:13px;color:var(--muted);}
.bundle-fix-search{
  width:100%;margin-top:12px;font-family:inherit;font-size:14px;
  padding:10px 13px;border-radius:8px;border:1px solid var(--edge);
  background:var(--stock);color:var(--text);
}

.bundle-row-qty{display:flex;align-items:center;gap:2px;font-variant-numeric:tabular-nums;}
.bundle-row-qty span{min-width:22px;text-align:center;font-size:14px;font-weight:600;}
.bundle-row-qty button{width:28px;height:28px;padding:0;display:grid;place-items:center;
  border-radius:6px;font-size:15px;line-height:1;}
.bundle-row-price{font-size:14px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;white-space:nowrap;}
.bundle-row-x{width:28px;height:28px;padding:0;display:grid;place-items:center;
  background:none;border:none;color:var(--muted);font-size:18px;line-height:1;}
.bundle-row-x:hover{color:var(--down);}

@media (max-width:520px){
  .bundle-row{grid-template-columns:24px 34px minmax(0,1fr) auto;
    grid-template-areas:'tick img main x' 'tick img price qty' 'tick img var var';
    row-gap:4px;gap:4px 9px;}
  .bundle-row img{width:34px;height:48px;}
  .bundle-row-price{font-size:13px;}
  .bundle-tick{width:24px;height:24px;}
}

/* ------------------------------------------------- 12. SETS / POKEDEX ----- */
.game-tile-grid,.set-tile-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));}
.game-tile,.set-tile{
  position:relative;overflow:hidden;display:flex;flex-direction:column;gap:9px;cursor:pointer;
  background:var(--stock);border:1px solid var(--edge);border-radius:var(--tag-r);
  padding:14px;text-align:left;font-family:inherit;color:inherit;
  box-shadow:var(--lift-1);transition:box-shadow .2s,border-color .2s;
}
.game-tile:hover,.set-tile:hover{box-shadow:var(--lift-2);border-color:var(--ink);}
/* .game-tile-art / .set-tile-art are CONTAINERS, not images. They clip, and
   anything inside them is contained -- a publisher logo comes in at whatever
   natural size it was uploaded at and will happily paint over the whole page. */
.game-tile-art,.set-tile-art{
  position:relative;width:100%;height:76px;display:grid;place-items:center;overflow:hidden;
}
/* Explicit pixel caps, not percentages: inside a centred grid area a percentage
   max-height resolves against a row the oversized image itself created, so a
   114px logo happily "fit" a 76px box and got clipped in half. */
.game-tile-art img,.set-tile-art img,.game-tile img,.set-tile img{
  max-width:100%;max-height:76px;width:auto;height:auto;object-fit:contain;display:block;
}
/* Every logo file is 200px tall but they range from 1.54:1 (Magic) to 3.77:1
   (One Piece). Capping height alone made that ratio the thing that decided how big
   a logo looked: One Piece filled its box while Magic sat at 71px wide in the same
   space, which read as a mistake rather than a brand difference.
   Capping BOTH lets each one grow until it hits whichever edge it reaches first, so
   a wide logo is limited by the box's width and a squarer one by its height, and
   they end up carrying similar visual weight. */
.game-tile-art img.game-tile-logo{max-height:58px;max-width:92%;}
/* The squarer marks have more room to give before they crowd the box. */
.game-tile-art img.game-tile-logo[src*="magicthegathering"],
.game-tile-art img.game-tile-logo[src*="lorcana"]{max-height:64px;}

/* A game with no logo file gets its name set properly rather than whatever product
   photo the card API happened to return -- a booster box in a row of wordmarks
   reads as a bug. Deliberately typographic: reproducing a trademarked logo we
   don't have a file for would be worse than not showing one. */
.game-tile-wordmark{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;width:100%;height:100%;padding:0 12px;text-align:center;
}
.game-tile-wordmark b{
  font-size:clamp(15px,2.6vw,21px);font-weight:600;letter-spacing:-.02em;
  line-height:1.05;color:var(--graphite);text-wrap:balance;
}
.game-tile-wordmark span{
  font-size:8.5px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;
  color:var(--brass);
}
/* Shown only by the logo's own onerror handler, which sets display:flex. Giving
   it a display here painted the fallback art on top of every working logo. */
.game-tile-art-fallback,.set-tile-fallback,.game-tile-fallback{
  display:none;position:absolute;inset:0;place-items:center;align-items:center;
  justify-content:center;background:var(--wall);color:var(--edge-hard);
  font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.1em;
}
.game-tile-name,.set-tile-name{font-size:12.5px;font-weight:600;color:var(--ink);line-height:1.25;}
.game-tile-stat,.set-tile-stat,.set-tile-date{
  font-size:10px;font-weight:700;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-3);font-variant-numeric:tabular-nums;}
.set-tile-progress-track{height:4px;background:var(--wall-lo);border-radius:99px;overflow:hidden;}
.set-tile-progress-fill{height:100%;background:var(--ink);}
.sets-unowned-details{margin-top:14px;font-size:13px;color:var(--ink-3);}

.pokedex-grid{display:grid;gap:14px 10px;grid-template-columns:repeat(auto-fill,minmax(108px,1fr));}
.pokedex-card{
  position:relative;display:flex;flex-direction:column;gap:5px;cursor:pointer;
  background:none;border:0;padding:0;font-family:inherit;text-align:left;color:inherit;}
.pokedex-card img{width:100%;aspect-ratio:63/88;object-fit:cover;border-radius:4px;
  box-shadow:var(--lift-1);background:var(--wall-lo);}
/* An unowned slot is an empty hook on the wall, not a broken card. */
/* Dimming belongs on the artwork -- it's what makes an owned card read as owned and
   a missing one as missing. The "?" placeholder is not artwork: it was already a
   pale grey on a pale ground, and knocking it back to 32% on top of that left it
   barely visible. It keeps full strength and a darker ink. */
/* Greyscale carries "you don't have this" on its own. The extra knock-down to 32%
   opacity on top of it took the art past "muted" and into "can't tell what card that
   is", which defeats the point of showing a picture at all. */
.pokedex-card-missing img{filter:grayscale(1);opacity:.62;}
.pokedex-card-missing:hover img{opacity:.9;}
.pokedex-card,.chase-card,.result-card{content-visibility:auto;contain-intrinsic-size:auto 210px;}
.pokedex-card-q{display:grid;place-items:center;aspect-ratio:63/88;background:var(--wall-lo);
  border:1px dashed var(--slate-2);border-radius:4px;color:var(--slate);
  font-size:22px;font-weight:700;}
.pokedex-card-name{font-size:10px;font-weight:700;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pokedex-card-num{font-size:10px;font-weight:700;letter-spacing:.08em;
  color:var(--ink-3);font-variant-numeric:tabular-nums;}
.pokedex-zoom-bg{position:fixed;inset:0;z-index:70;background:rgba(16,26,38,.7);
  display:none;align-items:center;justify-content:center;padding:24px;}
.pokedex-zoom-bg.active{display:flex;}
.pokedex-zoom-bg img{max-width:min(420px,90vw);max-height:80vh;border-radius:8px;box-shadow:var(--lift-4);}
.pokedex-zoom-caption{color:#fff;text-align:center;margin-top:12px;font-size:12px;
  font-weight:700;text-transform:uppercase;letter-spacing:.1em;}

.grading-currency-label{font-size:10px;font-weight:600;
  text-transform:uppercase;letter-spacing:.11em;color:var(--ink-3);}

/* Loot system: kept in the backend, taken off the wall. Nothing renders. */
#panel-vault,.vault-header,.vault-xp-bar-wrap,.vault-inventory-grid,.vault-trove-box,
.vault-quest-list,.vault-level-block,.vault-coins,.rpg-levelup-bg,.vault-quest-toast,
.char-create-layout,.char-display-layout{display:none !important;}

/* -------------------------------------------------- 13. ONE MOTION -------
   The wall's single authored moment: stock arriving. Cards settle onto the wall
   from slightly above with the shadow catching up, staggered across the grid, on
   an exponential ease-out from an already-visible default -- so if the animation
   never runs, the wall is simply there. */
@keyframes hangOnWall{
  from{transform:translateY(-9px);opacity:0;}
  to{transform:none;opacity:1;}
}
.animate-frost-reveal,.collection-grid>.collection-tile{
  animation:hangOnWall .5s cubic-bezier(.16,1,.3,1) backwards;
}
.collection-grid>.collection-tile:nth-child(1){animation-delay:.00s}
.collection-grid>.collection-tile:nth-child(2){animation-delay:.02s}
.collection-grid>.collection-tile:nth-child(3){animation-delay:.04s}
.collection-grid>.collection-tile:nth-child(4){animation-delay:.06s}
.collection-grid>.collection-tile:nth-child(5){animation-delay:.08s}
.collection-grid>.collection-tile:nth-child(6){animation-delay:.10s}
.collection-grid>.collection-tile:nth-child(7){animation-delay:.12s}
.collection-grid>.collection-tile:nth-child(n+8){animation-delay:.14s}
/* Past the first screenful the entrance animation is invisible -- nobody sees a
   tile arrive 900 rows down -- but it still forced a composited layer for every one
   of them at render. Only the tiles that can actually be watched arriving animate. */
.collection-grid>.collection-tile:nth-child(n+25){animation:none;}
.grid>.result-card:nth-child(n+25){animation:none;}
.refresh-unlinked{opacity:.45;}

/* --------------------------------------------------- 14. RESPONSIVE ------- */
@media (max-width:900px){
  .detail-layout{grid-template-columns:1fr;}
  .detail-image-col{border-right:0;border-bottom:1px solid var(--edge-soft);
    padding:22px 22px 24px;max-width:340px;margin:0 auto;}
  .detail-info-col{padding:20px 20px 24px;}
}
@media (max-width:760px){
  body{padding-bottom:60px;}
  .nav-row{grid-template-columns:1fr;min-height:0;padding:10px 0;}
  .navtabs{display:none !important;}
  .brand{justify-content:flex-start;padding-bottom:0;}
  .account-menu-wrap{top:9px;}
  .bottom-nav{display:flex;}
  .collection-grid{gap:20px 12px;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));}
  .tile-price{font-size:15px;}
  .holdings-grid{grid-template-columns:repeat(auto-fill,minmax(108px,1fr));}
  .panel{padding:16px;}
  .modal-bg{padding:0;}
  .modal{border-radius:0;min-height:100%;}
  .detail-modal{min-height:100%;}
}
@media (max-width:420px){
  /* Three across, not two. minmax(0,...) not 1fr: a bare 1fr floors each track at
     the item's min-content width, and a nowrap card name never reports a small one,
     so the columns inflated past the screen and the page scrolled sideways. */
  /* Fits what the screen can take rather than hard-coding a count: three across a
     375px phone, four on a bigger one, without ever squeezing to a size that stops
     being a card. Browsing your own collection is the density case -- you already
     know what is in there. */
  .collection-grid{grid-template-columns:repeat(auto-fill,minmax(78px,1fr));gap:14px 8px;}
  /* Results get more room than the collection does, on purpose. This is the grid
     where you decide which print is actually yours, and that means reading a
     collector number off the card. Three across, ~96px on a small phone and ~114px
     on a large one, rather than the 84px that started this. */
  .grid{grid-template-columns:repeat(auto-fill,minmax(92px,1fr));gap:16px 12px;}
  /* At a third of a phone the body text has to come down with the card. */
  .collection-tile .tile-name{font-size:11.5px;}
  .collection-tile .tile-set,.collection-tile .tile-meta{font-size:10px;}
  .tile-price{font-size:13px;}
  .tile-body{padding-top:9px;gap:3px;}
  .collection-tile{contain-intrinsic-size:auto 210px;}
  .hero-value,.hero-value-flame-wrap{font-size:clamp(32px,11vw,46px);}
}

/* ---------------------------------------------------------- 15. ICONS ------
   One drawn set at one stroke weight. They inherit currentColor so an icon
   picks up whatever state has coloured its label -- a rising price arrow is
   green because the delta around it is, not because the icon knows. */
.cv-icon{width:1em;height:1em;flex-shrink:0;vertical-align:-.12em;}
.tile-delta .cv-icon,.holding-change .cv-icon,.chart-readout-delta .cv-icon{width:.82em;height:.82em;}
.caret{width:9px;height:9px;flex-shrink:0;color:var(--ink-3);}
.opt-icon{width:13px;height:13px;flex-shrink:0;vertical-align:-.15em;color:var(--ink-3);}
.ebay-listings-caret .cv-icon{width:10px;height:10px;}
.tile-favourite-btn .cv-icon,.tile-listed-btn .cv-icon{width:14px;height:14px;}

/* The mark. Bronze appears here and nowhere else in the app, which is what
   makes it read as a maker's mark rather than another accent colour. */
.brand-logo{
  display:inline-flex;align-items:center;gap:9px;
  background:none;border:0;padding:0;cursor:pointer;height:auto;
}
.brand-logo:hover{background:none;opacity:.72;}
.brand-mark{width:23px;height:18px;color:var(--bronze);flex-shrink:0;}

/* ------------------------------------------------- 16. WALL vs COUNTER ----
   The collection is not content inside a card -- it IS the wall, so its panel
   carries no surface of its own and the cards hang straight on the ground. The
   controls above it are the counter: a strip of stock that stays put while the
   wall scrolls past. Opaque, never blurred, for the same sticky-repaint reason
   the sign board is. */
#panel-collection,#panel-sets{background:none;border:0;box-shadow:none;padding:0;margin:18px 0 0;}
#collectionControls{
  background:var(--stock);border:1px solid var(--edge);border-top:2px solid var(--ink);
  border-radius:0;box-shadow:var(--lift-1);
  padding:13px 14px;margin-bottom:22px;
}
#collectionControls .row{gap:12px;}
#collectionControls .row+.row{margin-top:11px;padding-top:11px;border-top:1px solid var(--edge-soft);}
/* Sub-views (Collection / Sets / Sold) are the shop's own wall sections. */
#collectionViewToggle{margin-bottom:16px;}

/* Section signage inside the wall does not need a rule above it when it is the
   first thing under the counter. */
.collection-grid+.set-row,.collection-grid+.set-top{margin-top:34px;}

/* Grade ladder. One row per grading company, each grade its own cell, so the
   ladder can be read down a column instead of parsed out of a sentence. */
.grade-table{border-top:1px solid var(--edge-soft);margin-top:4px;}
.grade-row{display:flex;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--edge-soft);}
.grade-co{
  flex-shrink:0;width:46px;font-size:10px;font-weight:600;
  text-transform:uppercase;letter-spacing:.09em;color:var(--ink-3);
}
.grade-cells{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;}
.grade-cells::-webkit-scrollbar{display:none;}
.grade-cell{
  flex-shrink:0;display:inline-flex;align-items:baseline;gap:5px;
  padding:3px 7px;border:1px solid var(--edge);border-radius:var(--tag-r);
  background:var(--stock);font-size:11.5px;font-weight:700;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.grade-cell b{font-size:10px;font-weight:600;letter-spacing:.06em;color:var(--blue-lo);}
/* A title that is missing a segment still looks finished, so the gap is named. */
.listing-title-warn{color:var(--tag-lo);font-weight:600;}

/* The browser's own file control is the one unstyled object left on the page.
   Dressed as what it is next to: a stock button with a hairline, and the chosen
   filename set beside it in the app's own voice. */
input[type=file]{
  width:100%;font-family:inherit;font-size:13px;color:var(--ink-3);
  background:var(--stock);border:1px dashed var(--edge-hard);border-radius:var(--tag-r);
  padding:10px 12px;cursor:pointer;
}
input[type=file]:hover{border-color:var(--ink);color:var(--ink-2);}
input[type=file]::file-selector-button{
  font-family:inherit;font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:.07em;
  background:var(--ink);color:var(--ink-on-dark);border:1px solid var(--ink);
  border-radius:var(--tag-r);padding:7px 13px;margin-right:12px;cursor:pointer;
}
input[type=file]::file-selector-button:hover{background:#000;}

/* Section headings now carry their own weight, so they get the space the label
   above them used to take. */
h3{font-size:15px;margin:0 0 6px;}
h3+p,h3+.sub{margin-top:0;}

/* A grid of identical solid buttons makes the board about the buttons. Inside a
   result card the action is secondary to the card itself, and the whole tile is
   clickable anyway -- the button is the visible affordance, not the headline. */
.result-card button{
  width:100%;background:var(--stock);color:var(--ink);border:1px solid var(--edge);
  margin-top:auto;
}
.result-card:hover button{border-color:var(--ink);background:var(--wall);}
/* Trending/movers cards vary in text length; keep their actions on one baseline. */
.result-card{justify-content:flex-start;}


/* --- Scan shortcut panel (Portfolio sidebar) --------------------------------
   Restored after the vitrine rewrite replaced the block that held it: without
   these the corner marks and the icon render at intrinsic size. */
.rpg-scryer-frame{
  position:relative;display:flex;flex-direction:column;align-items:center;gap:12px;
  padding:26px 20px;background:var(--stone-1);border:1px solid var(--stone-3);
  border-radius:var(--tag-r);text-align:center;
}
.rpg-corner{position:absolute;width:14px;height:14px;border:1.5px solid var(--slate-2);}
.rpg-corner-tl{top:10px;left:10px;border-right:0;border-bottom:0;}
.rpg-corner-tr{top:10px;right:10px;border-left:0;border-bottom:0;}
.rpg-corner-bl{bottom:10px;left:10px;border-right:0;border-top:0;}
.rpg-corner-br{bottom:10px;right:10px;border-left:0;border-top:0;}
.rpg-scryer-icon{width:30px;height:30px;flex-shrink:0;color:var(--slate-2);}
.rpg-scryer-hint{margin:0;font-size:12px;color:var(--slate);max-width:22ch;line-height:1.5;}

/* --- The vitrine sits ON the ground, not inside a card ----------------------
   It shares a panel with the history chart in the markup; the panel's surface
   is dropped so the lit well reads against the stone, and the chart below gets
   its own separated sheet instead. */
.rpg-panel:has(.vitrine){background:none;border:0;box-shadow:none;padding:0;}
.rpg-panel:has(.vitrine) #portfolioHistoryBox{
  background:#fff;border:1px solid var(--stone-3);border-radius:var(--tag-r);
  box-shadow:var(--lift-1);padding:20px 22px;margin-top:8px !important;
}
.rpg-panel:has(.vitrine) #statGrid{margin-top:20px;}

/* Stat row: even columns that don't strand one item on its own line. */
.stat-grid{
  display:grid;gap:18px 26px;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  padding-top:20px;border-top:1px solid var(--stone-3);
}
.stat .value,.value{font-size:23px;font-weight:400;letter-spacing:-.028em;line-height:1.1;}
.stat .label,.stat-grid .label{
  font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.14em;
  color:var(--slate-2);margin:0 0 5px;
}

/* Chrome breathing room: the three-column grid was squeezing the right-hand
   tabs until they truncated. */
.nav-row{gap:clamp(14px,3vw,40px);}
.navtabs{gap:clamp(14px,1.6vw,26px);flex-wrap:nowrap;}
.tab{font-size:12.5px;}
@media (max-width:1180px){.tab{font-size:12px;}.navtabs{gap:14px;}}

/* --- Header layout: one row, everything in flow -----------------------------
   .account-menu-wrap was absolutely positioned, a leftover from the old two-row
   header where the nav sat on its own line below it. In a single-row header that
   put the currency and account controls ON TOP of the last two tabs -- Forums and
   How to Play were sitting under them, unreachable, with 120px of overlap.
   display:contents flattens .nav-row so brand, tabs and account all become flex
   children of the header and lay out in one row that can't collide. */
header{
  display:flex;align-items:center;gap:clamp(14px,2.2vw,32px);
  min-height:64px;padding:0 var(--rail);
}
.nav-row{display:contents;}
.brand{order:1;justify-content:flex-start;flex-shrink:0;}
.navtabs-left{order:2;}
.navtabs-right{order:3;}
.account-menu-wrap{
  position:static;order:4;margin-left:auto;flex-shrink:0;
  display:flex;align-items:center;gap:10px;
}
/* Both tab groups read as one continuous run now that the brand leads. */
.navtabs{gap:clamp(13px,1.5vw,24px);}
@media (max-width:1240px){
  .tab{font-size:12px;}
  .navtabs{gap:13px;}
}
@media (max-width:980px){
  /* Below this the six tabs plus the brand plus the controls stop fitting, so
     the tabs move to the thumb rail rather than shrinking into illegibility. */
  .navtabs{display:none !important;}
}
@media (max-width:980px){
  .bottom-nav{display:flex;}
  body{padding-bottom:64px;}
}

/* --- On-screen keyboard --------------------------------------------------------
   iOS keeps position:fixed anchored to the LAYOUT viewport, and that viewport does
   not shrink when the keyboard opens. So the thumb rail, pinned to bottom:0, ends
   up floating in the middle of the screen across whatever you were reading, and the
   padding reserved for it becomes a dead band down behind the keyboard. Both go
   away while you're typing, which is what a native app does anyway -- the keyboard
   IS the bottom of the screen at that point, and a nav bar you can't reach is just
   something in the way.

   Gated to touch pointers: a narrow desktop window hits the same breakpoint but has
   no on-screen keyboard, so there the rail should stay put. */
@media (hover:none){
  body.keyboard-open .bottom-nav{display:none;}
  body.keyboard-open{padding-bottom:0;}
}

/* --- Inline SVG icons default to a black FILL ------------------------------
   The rail and scan icons are drawn as open paths meant to be stroked, but they
   carry no fill/stroke attributes in the markup, so the browser filled them and
   they rendered as solid black blobs. Every icon in the app is a stroked line
   drawing at one weight; this states that once rather than per-icon. */
.bnav-item svg,.rpg-scryer-icon,.empty-state svg,.scan-flip svg,.scan-torch svg,
.scan-close svg,.detail-list-btn svg,.ice-icon-btn svg{
  fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.bnav-item svg{width:20px;height:20px;}

/* --- Add cards: the two ways in, given equal weight ------------------------
   Scanning and searching are the same job by different means, so they read as a
   pair rather than as a big block followed by a small one. */
#panel-add>.panel,#panel-add .panel{margin:0 0 20px;}
#panel-add h3{font-size:19px;font-weight:500;letter-spacing:-.018em;}
#panel-add .sub{max-width:68ch;}
.camera-toggle{display:flex;gap:8px;margin-bottom:14px;}
.upload{
  border:1px dashed var(--stone-3);border-radius:8px;background:var(--stone-0);
  padding:34px 24px;text-align:center;color:var(--slate);
  transition:border-color .18s,background .18s;
}
.upload:hover,.upload.detecting{border-color:var(--slate-2);background:#fff;color:var(--graphite);}
/* Search results are cards, so they get the same presentation as the holdings. */
.result-card{display:flex;flex-direction:column;gap:0;}
.result-card img{
  width:100%;aspect-ratio:63/88;object-fit:cover;border-radius:5px;background:var(--stone-2);
  box-shadow:var(--lift-2);
  transition:transform .45s cubic-bezier(.16,1,.3,1),box-shadow .45s cubic-bezier(.16,1,.3,1);
}
.result-card:hover img{transform:translateY(-6px) scale(1.01);box-shadow:var(--lift-3);}
.result-card.selected img{outline:2px solid var(--graphite);outline-offset:3px;}
.result-card>div,.result-card>span{padding-top:14px;}
.result-card button{
  width:100%;margin-top:14px;background:#fff;color:var(--graphite);
  border:1px solid var(--stone-3);box-shadow:none;font-size:12.5px;
}
.result-card:hover button{border-color:var(--slate-2);}

/* --- Empty states: an invitation, not an apology --------------------------- */
.empty-state{text-align:center;padding:74px 24px;color:var(--slate);}
.empty-state svg{width:34px;height:34px;stroke:var(--stone-3);margin-bottom:20px;}
.empty-state-title{
  font-size:19px;font-weight:500;letter-spacing:-.018em;color:var(--graphite);
  text-transform:none;margin-bottom:9px;
}
.empty-state-sub{font-size:14px;max-width:42ch;margin:0 auto 22px;line-height:1.65;color:var(--slate);}

/* --- One authored moment ---------------------------------------------------
   Cards settle onto the surface as they arrive, staggered across the first row
   and then together. Exponential ease-out from an already-visible default, so if
   the animation never runs the collection is simply there. */
@keyframes settle{from{transform:translateY(10px);opacity:0}to{transform:none;opacity:1}}
.collection-grid>.collection-tile,.grid>.result-card{
  animation:settle .55s cubic-bezier(.16,1,.3,1) backwards;
}
.collection-grid>.collection-tile:nth-child(1),.grid>.result-card:nth-child(1){animation-delay:0s}
.collection-grid>.collection-tile:nth-child(2),.grid>.result-card:nth-child(2){animation-delay:.035s}
.collection-grid>.collection-tile:nth-child(3),.grid>.result-card:nth-child(3){animation-delay:.07s}
.collection-grid>.collection-tile:nth-child(4),.grid>.result-card:nth-child(4){animation-delay:.105s}
.collection-grid>.collection-tile:nth-child(5),.grid>.result-card:nth-child(5){animation-delay:.14s}
.collection-grid>.collection-tile:nth-child(6),.grid>.result-card:nth-child(6){animation-delay:.175s}
.collection-grid>.collection-tile:nth-child(n+7),.grid>.result-card:nth-child(n+7){animation-delay:.2s}
/* The vitrine arrives first and on its own, since it is the thesis. */
@keyframes present{from{transform:translateY(14px) scale(.985);opacity:0}to{transform:none;opacity:1}}
.vitrine-well{animation:present .7s cubic-bezier(.16,1,.3,1) backwards;}
.vitrine-body>*{animation:settle .6s cubic-bezier(.16,1,.3,1) .1s backwards;}

/* --- Sold: the card, not just its name -------------------------------------
   A sold card is still a card. The thumb is small and quiet -- this is a ledger,
   so it identifies the row rather than competing with the figures beside it. */
.sold-card-cell{display:flex;align-items:center;gap:12px;min-width:0;}
.sold-thumb{
  flex-shrink:0;width:34px;height:47px;border-radius:3px;overflow:hidden;
  background:var(--stone-2);box-shadow:0 1px 3px rgba(20,30,45,.20);
}
.sold-thumb img{width:100%;height:100%;object-fit:cover;display:block;}
.sold-thumb-empty{border:1px dashed var(--stone-3);background:var(--stone-0);box-shadow:none;}
.sold-card-text{display:flex;flex-direction:column;gap:2px;min-width:0;}
.sold-card-name{
  font-size:13.5px;font-weight:550;color:var(--graphite);letter-spacing:-.006em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.sold-card-num{font-size:11px;color:var(--slate-2);font-variant-numeric:tabular-nums;}
#soldTable td:first-child{min-width:210px;}

/* ============================================================ 17. GROUNDS ==
   Five variations on the same idea rather than five different apps: a mineral
   ground under one directional light, with one metal reserved for the mark and
   the active state. Only the tokens and the light change; every component keeps
   its structure, so the identity holds across all of them.
   Applied as body[data-ground]; the picker lives in Settings and persists. */

/* --- Greige: warmer stone, same brass. Reads softer, less clinical. --- */
:root[data-ground="greige"]{
  --stone-0:#FBFAF8; --stone-1:#EEEBE6; --stone-2:#DED9D1; --stone-3:#C9C2B7;
  --graphite:#1A1815; --slate:#635C52; --slate-2:#6E6659;
  --brass:#9A7B3E; --brass-lo:#755B2A;
}
:root[data-ground="greige"] .bg-scene{
  background:
    radial-gradient(120% 80% at 12% -10%, rgba(255,253,249,.96), transparent 60%),
    radial-gradient(90% 70% at 100% 110%, rgba(150,135,110,.18), transparent 65%),
    var(--stone-1);
}

/* --- Ice: the glacier surface. Built from Jesse's own reference photo and
       palette (#EFF5F6 pale ice, #CDE4F3 ice shadow, #65CFF7 and #3BC0F9 the
       cyan in the light, #04245C the deep navy at the bottom of the crack).
       The idea the photo carries: the SURFACE is almost white, and colour only
       appears where the ice opens up. So here the ground stays pale and the
       saturated cyan lives in the recesses -- the vitrine well, the plinth --
       exactly where a crevasse would be. --- */
:root[data-ground="ice"]{
  --stone-0:#F9FCFD; --stone-1:#EFF5F6; --stone-2:#CDE4F3; --stone-3:#A6CBE4;
  --graphite:#04245C; --slate:#2E5680; --slate-2:#33608E;
  --brass:#3BC0F9; --brass-lo:#0E6E9B;   /* bright cyan reads as the mark, deep cyan as text */
  --up:#0E6E5A; --up-tint:#E2F2EF; --down:#A33228; --down-tint:#FBECEA;
  --blue:#0E6E9B; --blue-lo:#04245C; --blue-tint:#DFEEF9;
}
:root[data-ground="ice"] .bg-scene{
  background:
    radial-gradient(125% 82% at 14% -12%, rgba(255,255,255,.98), transparent 58%),
    radial-gradient(70% 55% at 82% 104%, rgba(59,192,249,.20), transparent 66%),
    radial-gradient(90% 70% at 100% 112%, rgba(4,36,92,.14), transparent 68%),
    var(--stone-1);
}
/* Where the surface opens, the ice goes deep and turquoise. */
:root[data-ground="ice"] .vitrine-well{
  background:linear-gradient(158deg,#E4F0F8 0%,#D3E7F4 55%,#C0DCEF 100%);
}
:root[data-ground="ice"] .detail-image-col{
  background:
    radial-gradient(120% 70% at 26% -8%, rgba(255,255,255,1), transparent 60%),
    linear-gradient(168deg,#F7FBFD 0%,#E4F1F8 55%,#CFE5F2 100%);
}
:root[data-ground="ice"] .ground-chip::after{background:rgba(255,255,255,.92);}

/* --- Crevasse: the same glacier seen from inside the crack. The dark ice
       ground, where the cards are the light coming down from the surface. --- */
:root[data-ground="crevasse"]{
  --stone-0:#123A63; --stone-1:#04245C; --stone-2:#0A2F69; --stone-3:#1B4A80;
  --graphite:#EAF4FB; --slate:#9CC4E2; --slate-2:#8FB9DA;
  --brass:#65CFF7; --brass-lo:#8FDCFA;
  --stock:#082A5F; --edge:#1B4A80; --edge-soft:#12386F; --edge-hard:#2A5C93;
  --ink-on-dark:#04245C; --chrome-bg:rgba(4,36,92,.94);
  --up:#4FD6A8; --up-tint:#0B3A4A; --down:#F08C7C; --down-tint:#3A1E22;
  --tag-tint:#0E3A66; --blue:#65CFF7; --blue-lo:#9BDDFA; --blue-tint:#0E3A66;
  --lift-1:0 1px 2px rgba(0,10,26,.40), 0 2px 6px rgba(0,10,26,.30);
  --lift-2:0 2px 4px rgba(0,10,26,.48), 0 12px 26px -10px rgba(0,10,26,.66);
  --lift-3:0 3px 6px rgba(0,10,26,.52), 0 22px 40px -14px rgba(0,10,26,.72);
  --lift-4:0 8px 16px rgba(0,10,26,.54), 0 44px 84px -26px rgba(0,10,26,.76);
  --sink-1:inset 0 1px 3px rgba(0,8,22,.60), inset 0 -1px 0 rgba(140,200,240,.10);
  --sink-2:inset 0 2px 7px rgba(0,8,22,.66), inset 0 -1px 0 rgba(140,200,240,.12);
}
:root[data-ground="crevasse"] .bg-scene{
  background:
    radial-gradient(110% 70% at 50% -18%, rgba(120,205,245,.42), transparent 60%),
    radial-gradient(80% 60% at 8% 108%, rgba(0,8,22,.60), transparent 62%),
    var(--stone-1);
}
:root[data-ground="crevasse"] .vitrine-well{
  background:linear-gradient(158deg,#0B3168,#134479 45%,#082A5F);
}
:root[data-ground="crevasse"] .detail-image-col{
  background:radial-gradient(120% 70% at 26% -10%, rgba(120,205,245,.34), transparent 62%),
             linear-gradient(168deg,#0E3971 0%,#0A2F69 55%,#061F4F 100%);
}

/* --- Porcelain: near white and very quiet, one muted indigo. --- */
:root[data-ground="porcelain"]{
  --stone-0:#FFFFFF; --stone-1:#F4F5F7; --stone-2:#E4E7EB; --stone-3:#D2D7DD;
  --graphite:#15171A; --slate:#5A6068; --slate-2:#646A73;
  --brass:#4A5578; --brass-lo:#39435F;
}
:root[data-ground="porcelain"] .bg-scene{
  background:
    radial-gradient(130% 85% at 18% -12%, rgba(255,255,255,1), transparent 58%),
    radial-gradient(80% 60% at 92% 108%, rgba(120,130,160,.13), transparent 66%),
    var(--stone-1);
}/* --- Graphite: the dark one. The ground recedes and the cards become the only
       lit things on the screen,
which is how a real display case works. Ink and
       surface swap; the depth ladder inverts to glow-free darker shadows. --- */
:root[data-ground="graphite"]{
  --stone-0:#22262C; --stone-1:#15181D; --stone-2:#1D2127; --stone-3:#2E343C;
  --graphite:#EDF0F3; --slate:#9AA4AF; --slate-2:#8B95A1;
  --brass:#B99356; --brass-lo:#CBA867;
  --stock:#1B1F25; --edge:#2E343C; --edge-soft:#252A31; --edge-hard:#3C444E;
  --ink-on-dark:#15181D; --chrome-bg:rgba(21,24,29,.94);
  --up:#4FBF8B; --up-tint:#16281F; --down:#E0705F; --down-tint:#2A1A18;
  --tag-tint:#2A2216; --blue-tint:#1A2330;
  --blue:#7FA8D4; --blue-lo:#9BC0E6;
  --lift-1:0 1px 2px rgba(0,0,0,.34), 0 2px 6px rgba(0,0,0,.26);
  --lift-2:0 2px 4px rgba(0,0,0,.42), 0 12px 26px -10px rgba(0,0,0,.62);
  --lift-3:0 3px 6px rgba(0,0,0,.46), 0 22px 40px -14px rgba(0,0,0,.70);
  --lift-4:0 8px 16px rgba(0,0,0,.48), 0 44px 84px -26px rgba(0,0,0,.74);
  --sink-1:inset 0 1px 3px rgba(0,0,0,.55), inset 0 -1px 0 rgba(255,255,255,.05);
  --sink-2:inset 0 2px 6px rgba(0,0,0,.62), inset 0 -1px 0 rgba(255,255,255,.06);
}:root[data-ground="graphite"] .bg-scene,
:root[data-ground="crevasse"] .bg-scene{
  background:
    radial-gradient(115% 75% at 14% -12%, rgba(70,80,95,.55), transparent 62%),
    radial-gradient(90% 70% at 100% 112%, rgba(0,0,0,.55), transparent 62%),
    var(--stone-1);
}:root[data-ground="graphite"] .bg-grain,
:root[data-ground="crevasse"] .bg-grain{mix-blend-mode:overlay;opacity:.35;}:root[data-ground="graphite"] .panel,
:root[data-ground="crevasse"] .panel,
:root[data-ground="graphite"] .rpg-panel,
:root[data-ground="crevasse"] .rpg-panel,
:root[data-ground="graphite"] .modal,
:root[data-ground="crevasse"] .modal,
:root[data-ground="graphite"] .detail-modal,
:root[data-ground="crevasse"] .detail-modal,
:root[data-ground="graphite"] .detail-info-col,
:root[data-ground="crevasse"] .detail-info-col{background:var(--stock);}:root[data-ground="graphite"] .detail-image-col,
:root[data-ground="crevasse"] .detail-image-col{
  background:radial-gradient(120% 70% at 26% -8%, rgba(90,102,120,.40), transparent 62%),
             linear-gradient(168deg,#21262D 0%,#191D23 55%,#12151A 100%);
}:root[data-ground="graphite"] .vitrine-well,
:root[data-ground="crevasse"] .vitrine-well{
  background:linear-gradient(160deg,#1A1E24,#20252C 55%,#171B21);
}:root[data-ground="graphite"] input,
:root[data-ground="crevasse"] input,
:root[data-ground="graphite"] select,
:root[data-ground="crevasse"] select,
:root[data-ground="graphite"] textarea,
:root[data-ground="crevasse"] textarea,
:root[data-ground="graphite"] .pill-group,
:root[data-ground="crevasse"] .pill-group,
:root[data-ground="graphite"] .secondary,
:root[data-ground="crevasse"] .secondary,
:root[data-ground="graphite"] .ice-icon-btn,
:root[data-ground="crevasse"] .ice-icon-btn,
:root[data-ground="graphite"] .variant-pill,
:root[data-ground="crevasse"] .variant-pill,
:root[data-ground="graphite"] .data-source-tab,
:root[data-ground="crevasse"] .data-source-tab{
  background:var(--stone-2);color:var(--graphite);
}:root[data-ground="graphite"] button:not(.secondary):not(.plain):not(.danger):not(.pill):not(.chart-range-pill):not(.brand-logo):not(.variant-pill):not(.data-source-tab):not(.tab):not(.bnav-item):not(.account-menu-item):not(.currency-option):not(.dropdown-pill-option):not(.ground-opt):not(.crown-opt):not(.market-star):not(.market-yn):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta),
:root[data-ground="crevasse"] button:not(.secondary):not(.plain):not(.danger):not(.pill):not(.chart-range-pill):not(.brand-logo):not(.variant-pill):not(.data-source-tab):not(.tab):not(.bnav-item):not(.account-menu-item):not(.currency-option):not(.dropdown-pill-option):not(.ground-opt):not(.crown-opt):not(.market-star):not(.market-yn):not(.market-tab):not(.listing-post):not(.market-card):not(.chase-set-row):not(.chase-set-card):not(.chase-view-opt):not(.overview-cta){
  background:var(--graphite);color:var(--stone-1);border-color:var(--graphite);
}/* These are buttons structurally but not primary actions,
so they keep their own
   surfaces on the dark ground. */
:root[data-ground="graphite"] .brand-logo,
so they keep their own
   surfaces on the dark ground. */
:root[data-ground="crevasse"] .brand-logo,
:root[data-ground="graphite"] .tab,
:root[data-ground="crevasse"] .tab,
:root[data-ground="graphite"] .ground-opt,:root[data-ground="crevasse"] .ground-opt,
:root[data-ground="graphite"] .crown-opt,:root[data-ground="crevasse"] .crown-opt,
:root[data-ground="graphite"] .account-menu-item,
:root[data-ground="crevasse"] .account-menu-item,
:root[data-ground="graphite"] .currency-option,
:root[data-ground="crevasse"] .currency-option,
:root[data-ground="graphite"] .dropdown-pill-option,
:root[data-ground="crevasse"] .dropdown-pill-option{background:none;color:var(--graphite);border-color:transparent;}:root[data-ground="graphite"] .pill,
:root[data-ground="crevasse"] .pill,
:root[data-ground="graphite"] .chart-range-pill,
:root[data-ground="crevasse"] .chart-range-pill{background:none;color:var(--slate);}:root[data-ground="graphite"] .pill.active,
:root[data-ground="crevasse"] .pill.active,
:root[data-ground="graphite"] .chart-range-pill.active,
:root[data-ground="crevasse"] .chart-range-pill.active{background:var(--graphite);color:var(--stone-1);}:root[data-ground="graphite"] .bnav-item,
:root[data-ground="crevasse"] .bnav-item{background:none;color:var(--slate);}:root[data-ground="graphite"] .bnav-item.active,
:root[data-ground="crevasse"] .bnav-item.active{color:var(--graphite);}:root[data-ground="graphite"] .bottom-nav,
:root[data-ground="crevasse"] .bottom-nav{background:var(--stock);border-top-color:var(--edge);}:root[data-ground="graphite"] .pill.active,
:root[data-ground="crevasse"] .pill.active,
:root[data-ground="graphite"] .chart-range-pill.active,
:root[data-ground="crevasse"] .chart-range-pill.active,
:root[data-ground="graphite"] .variant-pill.active,
:root[data-ground="crevasse"] .variant-pill.active,
:root[data-ground="graphite"] .data-source-tab.active,
:root[data-ground="crevasse"] .data-source-tab.active,
:root[data-ground="graphite"] .tab.active,
:root[data-ground="crevasse"] .tab.active{color:var(--stone-1);}:root[data-ground="graphite"] .tab.active,
:root[data-ground="crevasse"] .tab.active{background:none;color:var(--graphite);}:root[data-ground="graphite"] .card-box,
:root[data-ground="crevasse"] .card-box,
:root[data-ground="graphite"] .chart-panel,
:root[data-ground="crevasse"] .chart-panel{background:var(--stone-2);}:root[data-ground="graphite"] tbody tr:hover,
:root[data-ground="crevasse"] tbody tr:hover,
:root[data-ground="graphite"] .ebay-listing-row:hover,
:root[data-ground="crevasse"] .ebay-listing-row:hover,
:root[data-ground="graphite"] .account-menu-item:hover,
:root[data-ground="crevasse"] .account-menu-item:hover,
:root[data-ground="graphite"] .currency-option:hover,
:root[data-ground="crevasse"] .currency-option:hover{
  background:var(--stone-2);
}

/* The picker itself. */
.ground-row{display:flex;gap:12px;flex-wrap:wrap;margin-top:10px;}
.ground-opt{
  display:flex;flex-direction:column;gap:8px;align-items:flex-start;cursor:pointer;
  background:none;border:0;padding:0;font-family:inherit;
}
.ground-chip{
  width:72px;height:48px;border-radius:7px;border:1px solid var(--stone-3);
  box-shadow:var(--lift-1);position:relative;overflow:hidden;
}
.ground-chip::after{
  content:'';position:absolute;left:9px;top:11px;width:17px;height:24px;border-radius:2px;
  background:rgba(255,255,255,.85);box-shadow:0 1px 3px rgba(20,30,45,.35);
}
.ground-opt[aria-pressed="true"] .ground-chip{border-color:var(--brass-lo);box-shadow:0 0 0 2px var(--brass-lo);}
.ground-name{font-size:12px;font-weight:550;color:var(--slate);}
.ground-opt[aria-pressed="true"] .ground-name{color:var(--graphite);}
/* Missing-art placeholder: a drawn card, not a glyph. */
.tile-placeholder{display:grid;place-items:center;width:100%;height:100%;color:var(--stone-3);}
.tile-placeholder svg{width:36%;height:auto;}

/* The count sits beside the card's name, directly under the card, rather than
   buried at the end of the meta line where it read as an afterthought. It is
   the second thing you want to know about a holding after what it is. */
.tile-name-row{display:flex;align-items:baseline;gap:8px;min-width:0;}
.tile-name-row .tile-name{flex:1;min-width:0;}
.tile-name-row .tile-qty-chip{flex-shrink:0;}


/* ================================================== 18. ICE ACCENT PLACEMENT ==
   Where the saturated ice actually belongs. In the reference photo the colour
   only appears where the glacier OPENS -- so it should mark depth or a way in,
   never sit still as decoration. Four candidates, switchable in Settings while
   we decide; body[data-ice].

   A. REVEAL -- the ice opens under the card you are pointing at. Interactive,
      and the only one where the colour means "you are looking here". */
body[data-ice="reveal"] .collection-tile::after{
  content:'';position:absolute;inset:-26px -22px -14px -22px;z-index:-1;
  pointer-events:none;opacity:0;border-radius:50%;
  /* An ellipse with no hard edge, so nothing of it can line up with a card
     corner and read as a coloured square behind the art. */
  background:radial-gradient(closest-side, rgba(59,192,249,.30), rgba(59,192,249,.10) 58%, transparent 76%);
  filter:blur(10px);
  transition:opacity .38s cubic-bezier(.16,1,.3,1);
}
body[data-ice="reveal"] .collection-tile:hover::after,
body[data-ice="reveal"] .collection-tile:focus-visible::after{opacity:1;}
body[data-ice="reveal"] .collection-tile{isolation:auto;}

/* B. DEPTH -- the page itself deepens into ice toward the floor, the way the
      surface does as it drops into the crack. Ambient, always on. */
body[data-ice="depth"] .bg-scene{
  background:
    radial-gradient(125% 82% at 14% -12%, rgba(255,255,255,.98), transparent 56%),
    linear-gradient(180deg, transparent 38%, rgba(59,192,249,.16) 78%, rgba(4,36,92,.30) 100%),
    var(--stone-1);
}

/* C. CHART -- the price history is the crack: the line in ice cyan over a
      turquoise that deepens as it falls. The one place depth is literally the
      subject. */
body[data-ice="chart"] .chart-panel{
  background:linear-gradient(180deg,#F4FAFD,#DCEEF9 70%,#C3E2F5);
  border-color:#A8D2EC;
}
body[data-ice="chart"] #portfolioHistoryBox{
  background:linear-gradient(180deg,#F4FAFD,#DCEEF9 70%,#C3E2F5) !important;
  border-color:#A8D2EC !important;
}

/* D. CHROME -- a deep ice band across the top, the collection hanging below the
      surface line. Boldest, and the only one visible before you scroll. */
body[data-ice="chrome"]{--chrome-bg:rgba(4,36,92,.96);}
body[data-ice="chrome"] header{border-bottom-color:#0E6E9B;}
body[data-ice="chrome"] .cardvault-wordmark,
body[data-ice="chrome"] .tab{color:#CDE4F3;}
body[data-ice="chrome"] .tab.active{color:#FFFFFF;border-bottom-color:#3BC0F9;}
body[data-ice="chrome"] .tab:hover{color:#FFFFFF;}
body[data-ice="chrome"] .brand-mark{color:#65CFF7;}
body[data-ice="chrome"] .currency-select-trigger,
body[data-ice="chrome"] .account-menu-trigger{
  background:rgba(255,255,255,.10);border-color:rgba(160,210,240,.34);color:#EAF4FB;
}
body[data-ice="chrome"] .currency-select-trigger .caret{color:#9BC0E6;}

/* The picker's own swatches. */
.ice-row{display:flex;gap:12px;flex-wrap:wrap;margin-top:10px;}
.ice-opt{display:flex;flex-direction:column;gap:8px;align-items:flex-start;cursor:pointer;
  background:none;border:0;padding:0;font-family:inherit;}
.ice-swatch{width:72px;height:48px;border-radius:7px;border:1px solid var(--stone-3);
  box-shadow:var(--lift-1);overflow:hidden;}
.ice-opt[aria-pressed="true"] .ice-swatch{border-color:var(--brass-lo);box-shadow:0 0 0 2px var(--brass-lo);}
.ice-name{font-size:12px;font-weight:550;color:var(--slate);}
.ice-opt[aria-pressed="true"] .ice-name{color:var(--graphite);}
.ice-note{font-size:11.5px;color:var(--slate-2);margin-top:2px;max-width:14ch;line-height:1.4;text-align:left;}

/* ======================================================== 19. TEXTURES ======
   All generated, no image assets: fractal noise for grain, layered gradients for
   the structured ones. Painted in one fixed layer so a 3000-card grid never
   stretches them, and set in multiply so they read as the surface's own tooth
   rather than a film laid over it. body[data-texture]. */

/* Fine grain -- the default, barely there. */
body[data-texture="grain"] .bg-grain{opacity:.5;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");}

/* Frost -- coarse, crystalline, the closest to real ice. */
body[data-texture="frost"] .bg-grain{opacity:.85;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.028' numOctaves='5' seed='9'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='0 0 .35 .6 .9'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)' opacity='0.10'/%3E%3C/svg%3E");}

/* Brushed -- fine horizontal drag, like brushed metal or planed ice. */
body[data-texture="brushed"] .bg-grain{opacity:.6;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cfilter id='b'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9 0.012' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23b)' opacity='0.055'/%3E%3C/svg%3E");}

/* Tooth -- the felt of coated paper, soft and irregular. */
body[data-texture="tooth"] .bg-grain{opacity:.75;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='t'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.42' numOctaves='4' seed='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23t)' opacity='0.06'/%3E%3C/svg%3E");}

/* Strata -- fine horizontal banding, the compressed layers in a glacier wall. */
body[data-texture="strata"] .bg-grain{opacity:.55;mix-blend-mode:multiply;
  background-image:
    repeating-linear-gradient(178deg, rgba(20,40,70,.05) 0 1px, transparent 1px 9px),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");}

body[data-texture="none"] .bg-grain{display:none;}
/* Textures are painted in multiply, which eats a dark ground -- lift them out. */
:root[data-ground="graphite"] .bg-grain,:root[data-ground="crevasse"] .bg-grain{
  mix-blend-mode:overlay;opacity:.4;
}


/* Texture swatch previews. Same patterns as the grounds use, on a pale chip. */
.ice-swatch[data-texture-demo]{background:linear-gradient(150deg,#F9FCFD,#DCE7EF);position:relative;}
.ice-swatch[data-texture-demo]::after{content:'';position:absolute;inset:0;mix-blend-mode:multiply;}
.ice-swatch[data-texture-demo="grain"]::after{opacity:.9;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.10'/%3E%3C/svg%3E");}
.ice-swatch[data-texture-demo="frost"]::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.028' numOctaves='5' seed='9'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='0 0 .35 .6 .9'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)' opacity='0.26'/%3E%3C/svg%3E");}
.ice-swatch[data-texture-demo="brushed"]::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cfilter id='b'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9 0.012' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23b)' opacity='0.16'/%3E%3C/svg%3E");}
.ice-swatch[data-texture-demo="tooth"]::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='t'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.42' numOctaves='4' seed='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23t)' opacity='0.18'/%3E%3C/svg%3E");}
.ice-swatch[data-texture-demo="strata"]::after{
  background-image:repeating-linear-gradient(178deg, rgba(20,40,70,.14) 0 1px, transparent 1px 7px);}
.ice-swatch[data-texture-demo="none"]::after{background:none;}

/* While a card is tracking the pointer it drops its transition, or the tilt
   lags a frame or two behind the cursor and feels broken. It gets the easing
   back the moment the pointer leaves, so the card settles rather than snapping. */
.tile-thumb.is-tilting,.holo-tilt.is-tilting,.result-card img.is-tilting{transition:none;}
.result-card img,.holding-thumb,.pokedex-card img{transform-style:preserve-3d;will-change:transform;}


/* Chrome controls: a hover you can read. These are buttons structurally, so the
   primary-action rules were turning Sort by, Filter, Grid, the currency picker
   and the account button solid black under the cursor. */
.dropdown-pill-trigger:hover,
.currency-select-trigger:hover,
.account-menu-trigger:hover{
  background:var(--stone-1);border-color:var(--slate-2);color:var(--graphite);
}
.dropdown-pill.active .dropdown-pill-trigger,
.currency-select-wrap.active .currency-select-trigger{
  background:var(--stone-1);border-color:var(--graphite);color:var(--graphite);
}
.ebay-listings-toggle:hover{background:none;}
.tile-favourite-btn:hover,.tile-listed-btn:hover{
  background:#fff;color:var(--graphite);
}
:root[data-ground="graphite"] .dropdown-pill-trigger:hover,
:root[data-ground="crevasse"] .dropdown-pill-trigger:hover,
:root[data-ground="graphite"] .currency-select-trigger:hover,
:root[data-ground="crevasse"] .currency-select-trigger:hover,
:root[data-ground="graphite"] .account-menu-trigger:hover,
:root[data-ground="crevasse"] .account-menu-trigger:hover{
  background:var(--stone-3);border-color:var(--slate-2);color:var(--graphite);
}


/* The card is the only thing you should see. A 3D-transformed box with
   overflow:hidden and a border-radius does not clip cleanly in Chromium, so the
   container's own fill leaked at the corners as the card tilted -- a small
   coloured square poking out from behind each rounded corner. The container
   carries no fill of its own while it holds an image, and the image takes the
   radius directly. */
.tile-thumb,.holo-tilt{background:transparent;}
.tile-thumb:empty,.holo-tilt:empty{background:var(--stone-2);}
.tile-thumb img,.holo-tilt img{border-radius:inherit;}
.tile-thumb .tile-placeholder,.holo-tilt .tile-placeholder{background:var(--stone-2);border-radius:inherit;}
/* Same for the sheen: clip it to the card's own shape, not the box's. */
.holo-foil::after{border-radius:inherit;}

/* The opened card enlarges on click, so it has to look clickable. */
#detailImageWrap{cursor:zoom-in;}
/* The lightbox: the card on a dark ground with nothing else competing. */
.pokedex-zoom-bg{
  position:fixed;inset:0;z-index:90;display:none;
  align-items:center;justify-content:center;flex-direction:column;gap:16px;
  padding:32px;background:rgba(8,16,28,.86);cursor:zoom-out;
}
.pokedex-zoom-bg.active{display:flex;}
.pokedex-zoom-bg img{
  max-width:min(560px,92vw);max-height:82vh;width:auto;height:auto;
  border-radius:12px;box-shadow:0 20px 50px rgba(0,0,0,.5),0 60px 120px -40px rgba(0,0,0,.7);
}
.pokedex-zoom-caption{
  color:#EAF1F8;font-size:13px;font-weight:500;letter-spacing:.02em;
  font-variant-numeric:tabular-nums;text-transform:none;
}

/* --- Card corner radius ----------------------------------------------------
   The "blue corners" were the card SCAN's own corners, not anything we drew:
   Scrydex images are rectangles with the card's rounded corner baked in, so the
   pixels outside that curve are scan background. A 5px radius on a 220px card is
   tighter than the real printed corner, leaving a sliver of that background
   showing at each corner. A real card's corner is about 3mm on a 63mm width, so
   the radius is set proportionally and now scales with the card instead of
   being right at one size and wrong at every other. */
.tile-thumb,.holo-tilt,.tile-thumb img,.holo-tilt img,
.result-card img,.holding-thumb,.pokedex-card img,
.vitrine-card,.vitrine-card img,
.detail-image-col .holo-tilt,.detail-image-col>img,
.pokedex-zoom-bg img{
  border-radius:4.8% / 3.4%;
}

/* --- Tooltips: text needs room ---------------------------------------------
   The condition explainer ran flush to the left edge of its box. Wider box, real
   padding, and the term set apart from its definition so five conditions read as
   five entries rather than one paragraph. */
.info-tip-box{
  width:min(300px,78vw);padding:14px 16px;line-height:1.55;
  border-radius:8px;
}
.info-tip-box b{
  display:inline-block;min-width:34px;color:var(--graphite);font-weight:600;
}
.info-tip-box br+b{margin-top:7px;}

/* --- Detail column: the facts belong with the card -------------------------
   There was a dead gap between the card and its own details, so the two read as
   unrelated blocks. Tightened, and the facts get a little more air between rows
   in exchange. */
.detail-image-col{gap:14px;}
#detailImageWrap{margin-bottom:0 !important;}
#detailTypeTags{margin-bottom:0 !important;}
#detailVariantSelector{margin-bottom:0 !important;}
#detailFacts{margin-bottom:0 !important;line-height:1.75;}
#detailFacts b,#detailFacts strong{color:var(--graphite);font-weight:600;}

/* --- Set mark beside the variant picker ------------------------------------
   A set's own logo is how a collector identifies a print at a glance, so it sits
   at the top of the column where the card's identity is established, at a size
   you can actually read, rather than tucked under a key/value list at 20px where
   it looked like a stray favicon. */
.detail-variant-row{
  display:flex;align-items:flex-start;justify-content:space-between;gap:14px;
}
.detail-variant-row #detailVariantSelector{flex:1;min-width:0;}
#detailSetLogoSlot{flex-shrink:0;display:flex;align-items:flex-start;justify-content:flex-end;max-width:44%;}
.set-logo-img{
  height:auto;width:auto;max-height:52px;max-width:100%;object-fit:contain;
  opacity:.95;
}
@media (max-width:900px){
  .detail-variant-row{align-items:center;}
  .set-logo-img{max-height:42px;}
}

/* --- Magic rarity, in Magic's own colours ----------------------------------
   The set symbol on a Magic card IS the rarity: black common, silver uncommon,
   gold rare, orange mythic. Players read it without thinking, so the badge uses
   the same code rather than throwing that away as another grey chip. Each is a
   tint with a matching border and ink dark enough to clear 4.5:1 on the tint. */
.fact-pill.rarity-common{background:#E7E9EC;border-color:#B9BFC6;color:#2B2F34;}
.fact-pill.rarity-uncommon{background:#E6ECF0;border-color:#9DAEBA;color:#3B4C58;}
.fact-pill.rarity-rare{background:#F7EFD9;border-color:#C9A94E;color:#6E5411;}
.fact-pill.rarity-mythic{background:#FBEADF;border-color:#D2703A;color:#8A3A12;}
.fact-pill.rarity-special{background:#F0E8F7;border-color:#9B79C4;color:#553077;}
/* On the dark grounds the tints invert: the hue stays, the ink lightens. */
:root[data-ground="graphite"] .fact-pill.rarity-common,
:root[data-ground="crevasse"] .fact-pill.rarity-common{background:#2A2F36;border-color:#4A525C;color:#D7DDE4;}
:root[data-ground="graphite"] .fact-pill.rarity-uncommon,
:root[data-ground="crevasse"] .fact-pill.rarity-uncommon{background:#26333C;border-color:#5B7080;color:#CFE0EC;}
:root[data-ground="graphite"] .fact-pill.rarity-rare,
:root[data-ground="crevasse"] .fact-pill.rarity-rare{background:#33290F;border-color:#A8873A;color:#F0DCA4;}
:root[data-ground="graphite"] .fact-pill.rarity-mythic,
:root[data-ground="crevasse"] .fact-pill.rarity-mythic{background:#3A1E10;border-color:#C4652F;color:#F6C7A6;}
:root[data-ground="graphite"] .fact-pill.rarity-special,
:root[data-ground="crevasse"] .fact-pill.rarity-special{background:#2C2038;border-color:#8259B0;color:#E0CDF3;}
/* --- The other five games ---------------------------------------------------
   Lorcana, One Piece, Gundam, Riftbound and Yu-Gi-Oh share one escalating ladder,
   because unlike Magic none of them prints a rarity colour their players already
   read at a glance. Inventing five private colour codes would mean five things to
   learn; one ladder means one. It picks up where the shared common/uncommon/rare
   above leave off:

     super     violet    first real chase tier
     legend    amber     top of the ordinary printed ladder
     secret    rose      above the printed ladder, box-hit territory
     enchanted iridescent  Lorcana's rainbow-foil chase

   Two of them sit OUTSIDE the ladder on purpose. A One Piece Leader is a role, not
   a tier -- every deck has exactly one -- and a Riftbound Showcase is an alternate
   treatment of a card you can also get normally. Colouring either as though it
   outranked Super Rare would be telling the reader something untrue. */
.fact-pill.rarity-super{background:#EFE7F9;border-color:#8B63C2;color:#4C2C77;}
.fact-pill.rarity-legend{background:#FBF0DA;border-color:#C79A34;color:#6B4E08;}
.fact-pill.rarity-secret{background:#FBE7F1;border-color:#C25E96;color:#7C2653;}
.fact-pill.rarity-leader{background:#DFF1F1;border-color:#3F9997;color:#175455;}
.fact-pill.rarity-showcase{background:#E9EEF7;border-color:#7189BD;color:#2F4479;}
/* The only gradient in the rarity set, and it earns it: Enchanted cards are
   literally rainbow foil, and it is the one rarity in any of these games that a
   collector identifies by iridescence rather than by a colour. */
.fact-pill.rarity-enchanted{
  background:linear-gradient(100deg,#FBE9F2 0%,#EFE9FA 38%,#E4F2F4 68%,#FCF2DF 100%);
  border-color:#A981C4;color:#5B3170;
}

/* --- Yu-Gi-Oh: the rarity IS the foil ---------------------------------------
   Yu-Gi-Oh names its rarities after the finish on the card, which makes it the one
   game here where the honest colour is the physical one. Ultra Rare has gold
   lettering, Secret Rare is rainbow foil, Platinum Secret is cold silver, Ghost is
   a pale near-white relief, Collector's is rose gold. Matching the card beats
   putting them on the shared ladder. */
.fact-pill.rarity-ygo-gold{background:#FAF0D4;border-color:#BE9A2E;color:#634C05;}
.fact-pill.rarity-ygo-platinum{background:#EAEEF2;border-color:#8B9AA8;color:#3A4753;}
.fact-pill.rarity-ygo-collector{background:#FAEAE4;border-color:#C08064;color:#7A3E24;}
.fact-pill.rarity-ygo-starlight{background:#E6ECFA;border-color:#6E82C6;color:#2C3B7C;}
.fact-pill.rarity-ygo-ghost{background:#F1F3F5;border-color:#AEB7C0;color:#4A535D;}
.fact-pill.rarity-ygo-starfoil{background:#E8F1E9;border-color:#6D9E76;color:#2C5334;}
.fact-pill.rarity-ygo-parallel{background:#EDEBF6;border-color:#8681B5;color:#413B72;}
/* Quarter Century Secret Rare is the current top-end chase and reads as a silver
   rainbow, so it gets the second gradient rather than being flattened to grey. */
.fact-pill.rarity-ygo-qcsr{
  background:linear-gradient(100deg,#EDF0F6 0%,#F6ECF3 42%,#EAF3F1 100%);
  border-color:#7C86A8;color:#333B57;
}

/* Dark grounds: keep the hue, lift the ink. */
:root[data-ground="graphite"] .fact-pill.rarity-super,
:root[data-ground="crevasse"] .fact-pill.rarity-super{background:#2B2040;border-color:#7A56AE;color:#DCC8F3;}
:root[data-ground="graphite"] .fact-pill.rarity-legend,
:root[data-ground="crevasse"] .fact-pill.rarity-legend{background:#332812;border-color:#A98429;color:#F2DCA3;}
:root[data-ground="graphite"] .fact-pill.rarity-secret,
:root[data-ground="crevasse"] .fact-pill.rarity-secret{background:#361A29;border-color:#AE5081;color:#F6C6DF;}
:root[data-ground="graphite"] .fact-pill.rarity-leader,
:root[data-ground="crevasse"] .fact-pill.rarity-leader{background:#123030;border-color:#3D8B89;color:#B6E6E4;}
:root[data-ground="graphite"] .fact-pill.rarity-showcase,
:root[data-ground="crevasse"] .fact-pill.rarity-showcase{background:#1E2740;border-color:#5E76AC;color:#C6D5F1;}
:root[data-ground="graphite"] .fact-pill.rarity-enchanted,
:root[data-ground="crevasse"] .fact-pill.rarity-enchanted{
  background:linear-gradient(100deg,#33203A 0%,#252245 40%,#1B3238 70%,#332B18 100%);
  border-color:#9271AE;color:#E9D6F5;
}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-gold,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-gold{background:#332913;border-color:#AB8B2A;color:#F2DFA6;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-platinum,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-platinum{background:#242A31;border-color:#71808E;color:#D2DBE4;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-collector,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-collector{background:#33201A;border-color:#A96A50;color:#F2CBB8;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-starlight,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-starlight{background:#1D2440;border-color:#5C6FB0;color:#C7D2F4;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-ghost,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-ghost{background:#282D33;border-color:#8C959E;color:#DCE2E8;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-starfoil,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-starfoil{background:#1B2C1F;border-color:#588A61;color:#C5E5CB;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-parallel,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-parallel{background:#26233A;border-color:#6E699C;color:#D2CDEE;}
:root[data-ground="graphite"] .fact-pill.rarity-ygo-qcsr,
:root[data-ground="crevasse"] .fact-pill.rarity-ygo-qcsr{
  background:linear-gradient(100deg,#252A38 0%,#332530 42%,#1F3231 100%);
  border-color:#6B759A;color:#D5DBEC;
}

/* --- Showing the working on a sold price -------------------------------------
   The figure leads, but the sample sits right beside it at a readable size, not
   hidden in a tooltip. A reader who can see "19 sales, 2 outliers dropped" can
   decide for themselves whether to believe it, and that is the whole difference
   between a price people quote and a price people check. */
.sold-summary{
  margin:6px 0 10px;padding:11px 13px;border:1px solid var(--edge-soft);
  border-left:3px solid var(--brass);border-radius:4px;background:var(--wall-hi);
}
.sold-line{
  display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  padding:3px 0;font-size:13px;
}
.sold-line-label{
  font-size:10px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--slate-2);min-width:48px;
}
.sold-line-price{font-weight:600;font-size:15px;font-variant-numeric:tabular-nums;color:var(--ink);}
.sold-line-price .ccy{font-size:10px;font-weight:600;color:var(--slate-2);letter-spacing:.06em;}
.sold-line-meta{font-size:11.5px;color:var(--slate-2);}
.sold-summary-note{
  margin:7px 0 0;padding-top:7px;border-top:1px solid var(--edge-soft);
  font-size:11.5px;line-height:1.5;color:var(--slate);
}
/* How sure we are, as a word rather than a number -- "Thin" tells a collector
   more at a glance than "n=2" does. */
.conf-chip{
  font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  padding:2px 6px;border-radius:2px;border:1px solid;white-space:nowrap;
}
.conf-high{background:var(--up-tint);border-color:#BCD9C9;color:var(--up);}
.conf-medium{background:#FBF1DF;border-color:#EBD6AE;color:#8A6118;}
.conf-low{background:var(--down-tint);border-color:#E8CFCB;color:var(--down);}
:root[data-ground="graphite"] .sold-summary,
:root[data-ground="crevasse"] .sold-summary{background:rgba(255,255,255,.03);}
:root[data-ground="graphite"] .conf-high,
:root[data-ground="crevasse"] .conf-high{background:#14261D;border-color:#26452F;color:#6FC79B;}
:root[data-ground="graphite"] .conf-medium,
:root[data-ground="crevasse"] .conf-medium{background:#282013;border-color:#4A3B1C;color:#DDB463;}
:root[data-ground="graphite"] .conf-low,
:root[data-ground="crevasse"] .conf-low{background:#2A1917;border-color:#4A2A25;color:#E9857A;}

/* Scan/search result tags: which game, and which language printing. */
.result-card-tags{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:2px;}
.result-game{font-size:11px;font-weight:600;opacity:.75;color:var(--ink-2);}
.result-lang{
  font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  padding:2px 5px;border-radius:2px;
  background:var(--tag-tint);border:1px solid var(--tag);color:var(--tag-lo);
}

/* The scanner's verdict on its own match. Says how sure it is and, when it is not
   sure, what to look at -- "check the collector number" is something the person
   holding the card can act on; "low confidence" is not. */
.scan-verdict{
  display:inline-block;font-size:11px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;padding:3px 8px;border-radius:3px;border:1px solid;margin-bottom:5px;
}
.scan-sure{background:var(--up-tint);border-color:#BCD9C9;color:var(--up);}
.scan-likely{background:var(--blue-tint);border-color:#B7C9DD;color:var(--blue-lo);}
.scan-tossup{background:#FBF1DF;border-color:#EBD6AE;color:#8A6118;}
.scan-weak{background:var(--down-tint);border-color:#E8CFCB;color:var(--down);}
.scan-graded{background:var(--tag-tint);border-color:var(--tag);color:var(--tag-lo);}
/* Named -note, not -hint: .scan-hint is already the camera overlay's floating
   caption, which is absolutely positioned, white and uppercase. Reusing it meant
   this inherited all three. */
.scan-note{display:block;font-size:12.5px;line-height:1.5;color:var(--slate);max-width:62ch;}
.scan-note b{color:var(--ink);font-weight:600;}

/* --- "Which one is yours?" ---------------------------------------------------
   Shown only when the model genuinely cannot separate two prints. The collector
   number is the largest thing in each option on purpose: the names are identical
   across these candidates, so the name cannot help anyone choose, and the number
   is printed in the corner of the card already in their hand. Big, tabular, and
   the first thing the eye lands on. */
.scan-chooser{
  grid-column:1/-1;                       /* spans the results grid */
  background:var(--wall-hi);border:1px solid var(--edge);
  border-left:3px solid var(--tag);border-radius:5px;
  padding:16px 18px;margin-bottom:6px;
}
.scan-chooser-head{display:flex;flex-direction:column;gap:3px;margin-bottom:13px;}
.scan-chooser-sub{font-size:12.5px;color:var(--slate);line-height:1.5;}
.scan-chooser-row{
  display:flex;gap:12px;overflow-x:auto;padding-bottom:4px;
  scroll-snap-type:x proximity;
}
.scan-choice{
  flex:0 0 118px;scroll-snap-align:start;
  display:flex;flex-direction:column;align-items:center;gap:5px;
  background:var(--stock);border:1px solid var(--edge);border-radius:4px;
  padding:10px 8px 11px;cursor:pointer;font-family:inherit;color:inherit;
  transition:border-color .14s,box-shadow .14s,transform .14s;
}
.scan-choice:hover,.scan-choice:focus-visible{
  border-color:var(--tag);box-shadow:var(--lift-2);transform:translateY(-2px);
}
.scan-choice-art{
  width:100%;aspect-ratio:63/88;border-radius:3px;overflow:hidden;
  background:var(--stone-2);display:grid;place-items:center;
}
.scan-choice-art img{width:100%;height:100%;object-fit:cover;display:block;}
/* The deciding fact. */
.scan-choice-num{
  font-size:21px;font-weight:650;letter-spacing:-.02em;line-height:1.1;
  font-variant-numeric:tabular-nums;color:var(--ink);
}
.scan-choice-set,.scan-choice-rarity{
  font-size:10.5px;line-height:1.3;color:var(--slate-2);text-align:center;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
@media (prefers-reduced-motion:reduce){.scan-choice{transition:none;}}
@media (max-width:620px){.scan-choice{flex-basis:104px;}}

/* --- Light / dark ------------------------------------------------------------
   The header toggle shows the mode you would GET by pressing it, not the one you
   are in -- a sun while dark, a moon while light. Both icons live in the button
   and the inactive one is simply not rendered, so there is no swap flicker. */
.mode-toggle{
  width:32px;height:32px;padding:0;display:grid;place-items:center;cursor:pointer;
  background:var(--stock);border:1px solid var(--edge);border-radius:2px;
  color:var(--ink-2);transition:color .14s,border-color .14s;
}
.mode-toggle:hover{color:var(--ink);border-color:var(--edge-hard);}
.mode-toggle svg{width:16px;height:16px;grid-area:1/1;}
/* Light now -> offer the moon. Dark now -> offer the sun. */
.mode-icon-sun{display:none;}
:root[data-mode="dark"] .mode-icon-sun{display:block;}
:root[data-mode="dark"] .mode-icon-moon{display:none;}

.mode-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px;}
.mode-opt{
  display:inline-flex;align-items:baseline;gap:5px;cursor:pointer;font-family:inherit;
  font-size:13px;font-weight:500;padding:8px 14px;border-radius:3px;
  background:var(--stock);border:1px solid var(--edge);color:var(--ink);
  text-transform:none;letter-spacing:0;
}
.mode-opt:hover{border-color:var(--edge-hard);}
.mode-opt[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--ink-on-dark);}
.mode-opt-note{font-size:11px;opacity:.65;}

/* The whole surface should cross over together rather than element by element.
   Short, and off entirely for anyone who has asked for less movement. */
@media (prefers-reduced-motion:no-preference){
  .bg-scene,.card-box,.panel,.modal,.board{transition:background-color .22s ease,border-color .22s ease;}
}

/* The app's own alert/confirm. Deliberately narrower than a content modal --
   it asks one question and offers two answers, so it should not present itself
   with the weight of a full panel. */
.cv-dialog{width:min(420px,calc(100vw - 32px));}
.cv-dialog h3{margin:0 0 10px;font-size:17px;font-weight:600;letter-spacing:-.012em;}
.cv-dialog-body{
  margin:0 0 20px;font-size:14px;line-height:1.6;color:var(--slate);
  white-space:pre-line;                     /* lets a caller break its own lines */
}
.cv-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin:0;}
.cv-dialog-input{width:100%;margin:0 0 20px;}

/* Crown picker swatches. */
.crown-opt{display:flex;flex-direction:column;gap:8px;align-items:center;cursor:pointer;
  background:none;border:0;padding:0;font-family:inherit;}
.crown-chip{
  width:64px;height:52px;display:grid;place-items:center;border-radius:7px;
  background:#fff;border:1px solid var(--stone-3);box-shadow:var(--lift-1);color:var(--brass);
}
.crown-opt[aria-pressed="true"] .crown-chip{border-color:var(--brass-lo);box-shadow:0 0 0 2px var(--brass-lo);}
.crown-opt[aria-pressed="true"] .ice-name{color:var(--graphite);}
:root[data-ground="graphite"] .crown-chip,:root[data-ground="crevasse"] .crown-chip{background:var(--stone-2);}

/* .crown-opt is a <button>, so the primary-action fill claimed it and every
   swatch rendered as a solid ink block with the mark punched out of it. */
.crown-opt,.ice-opt{background:none;border:0;color:inherit;}
.crown-opt:hover,.ice-opt:hover{background:none;}
.crown-opt:hover .crown-chip{border-color:var(--slate-2);}

/* --- Graded prices: the answer first, the ladder on request -----------------
   Six grading companies at four grades each is twenty-odd chips. The top grade
   from each company leads on one line, sorted by what it is worth, and the full
   per-company ladder stays folded until someone is genuinely comparing 9 to 9.5. */
.grade-best{display:flex;flex-wrap:wrap;gap:7px;margin-top:8px;}
.grade-best-cell{
  display:inline-flex;align-items:baseline;gap:7px;padding:6px 11px;
  background:#fff;border:1px solid var(--stone-3);border-radius:6px;
  font-size:13.5px;font-weight:550;color:var(--graphite);font-variant-numeric:tabular-nums;
}
.grade-best-cell b{
  font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--slate-2);
}
.grade-best-cell:first-child{border-color:var(--brass-lo);}
.grade-best-cell:first-child b{color:var(--brass-lo);}

.grade-more{margin-top:10px;}
.grade-more-toggle{
  display:inline-flex;align-items:center;gap:7px;background:none;border:0;box-shadow:none;padding:4px 0;
  color:var(--slate);font-size:12.5px;font-weight:500;cursor:pointer;
}
.grade-more-toggle:hover{background:none;color:var(--graphite);}
.grade-more-toggle .cv-icon{width:10px;height:10px;transition:transform .18s;}
.grade-more.open .grade-more-toggle .cv-icon{transform:rotate(90deg);}
.grade-more .grade-table{display:none;margin-top:6px;}
.grade-more.open .grade-table{display:block;}
/* Price by condition gets air under it now that something follows. */
.fact-pills-row+.eyebrow,.grade-best+.eyebrow{margin-top:18px;}
:root[data-ground="graphite"] .grade-best-cell,
:root[data-ground="crevasse"] .grade-best-cell{background:var(--stone-2);}

/* The add dialog's card behaves like every other card: it lifts to the pointer
   and enlarges on click, because choosing between near-identical reprints is
   exactly when someone needs a proper look at the scan. */
.add-modal-card{
  max-width:220px;width:100%;display:block;margin:0 auto;
  border-radius:4.8% / 3.4%;cursor:zoom-in;background:var(--stone-2);
  box-shadow:var(--lift-2);
  transition:transform .35s cubic-bezier(.16,1,.3,1),box-shadow .35s cubic-bezier(.16,1,.3,1);
}
.add-modal-card:hover{transform:translateY(-4px);box-shadow:var(--lift-3);}

/* --- Pokémon rarity, and what the card physically is ------------------------
   Same principle as the Magic pills: use the code the hobby already reads. The
   illustration rarities are the purple chase cards, ACE SPEC is printed red,
   shinies read teal, and the ordinary ladder climbs grey to green to amber.
   Every ink is dark enough to clear 4.5:1 on its own tint. */
.fact-pill.rarity-double{background:#FBF0DC;border-color:#C79A3C;color:#6B4A08;}
.fact-pill.rarity-ultra{background:#E7EEF6;border-color:#7C9BBD;color:#2C4A6B;}
.fact-pill.rarity-illus{background:#F1EAFA;border-color:#9B79C4;color:#553077;}
.fact-pill.rarity-sir{background:#EBE2F8;border-color:#8158B8;color:#452766;}
.fact-pill.rarity-hyper{background:#FAEFD4;border-color:#C79A2C;color:#63490A;}
.fact-pill.rarity-ace{background:#FBE9E7;border-color:#CE5346;color:#8C2A1D;}
.fact-pill.rarity-shiny{background:#E0F3F1;border-color:#5AA69C;color:#1F5A53;}
.fact-pill.rarity-mega{background:#FCEDE0;border-color:#CE8143;color:#84450F;}
.fact-pill.rarity-promo{background:#EDEFF2;border-color:#A7B0BB;color:#41494F;}

/* Variants. Holo is the light blue of the foil catching the light; the ball
   reverses carry their own seal colour, which is how you actually recognise a
   Master Ball print without reading the label. */
.fact-pill.pill-holo{background:#E1F1FB;border-color:#63AEDA;color:#175A83;}
.fact-pill.pill-reverse{background:#E2ECF9;border-color:#6C93CC;color:#1F4477;}
.fact-pill.pill-pokeball{background:#FBE7E6;border-color:#CE5B52;color:#8A2B23;}
.fact-pill.pill-masterball{background:#EFE6F8;border-color:#8B5FBF;color:#4C2A75;}
.fact-pill.pill-loveball{background:#FCE7F0;border-color:#CE6796;color:#8A2A55;}
.fact-pill.pill-friendball{background:#E4F3E4;border-color:#68A868;color:#265A26;}
.fact-pill.pill-cosmos{background:#E7E9F8;border-color:#7276C4;color:#33377C;}
.fact-pill.pill-firsted{background:#F8F0D8;border-color:#C6A340;color:#6A5008;}
/* Stamps sit apart from the foil patterns above: a stamp is an event or retailer
   mark on an otherwise ordinary card, and on a bulk common it is frequently the
   only thing making it worth anything. Warm neutrals so they read as a category of
   their own rather than as another ball colour. */
.fact-pill.pill-stamp{background:#F2EFE7;border-color:#A99B7E;color:#5A4F38;}
.fact-pill.pill-stamp-play{background:#E8F0F7;border-color:#6389B0;color:#26466B;}
.fact-pill.pill-stamp-ebgames{background:#EAF1E8;border-color:#6E9A62;color:#2F4C27;}
.fact-pill.pill-stamp-center{background:#FBEAEA;border-color:#C36A6A;color:#7C2B2B;}
.fact-pill.pill-stamp-prerelease{background:#EDECF6;border-color:#8886B8;color:#413E70;}
.fact-pill.pill-stamp-staff{background:#F6EBF2;border-color:#B172A0;color:#6D2B58;}

/* ---- Per-variant sheen ------------------------------------------------------
   Foil patterns catch light differently in the hand, so they catch it differently
   here. All of them ride the same --ang custom property the tilt handler already
   sets, so the highlight tracks the pointer rather than sweeping on a timer. */
.holo-tilt[class*="sheen-"]{position:relative;}
.holo-tilt[class*="sheen-"]::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .28s;border-radius:inherit;
}
.chase-hit:hover .holo-tilt[class*="sheen-"]::after,
.pokedex-card:hover .holo-tilt[class*="sheen-"]::after,
.holo-tilt[class*="sheen-"].holo-active::after{opacity:1;}

/* Flat holo: one clean sweep down the card. */
.sheen-holo::after{
  background:linear-gradient(var(--ang,110deg),transparent 36%,rgba(255,255,255,.42) 48%,
    rgba(255,255,255,.12) 57%,transparent 68%);
}
/* Reverse holo foils the border, not the art, so the light sits at the edges. */
.sheen-reverse::after{
  background:
    linear-gradient(var(--ang,110deg),transparent 38%,rgba(255,255,255,.30) 50%,transparent 62%),
    radial-gradient(140% 90% at 50% 50%,transparent 58%,rgba(255,255,255,.34) 100%);
}
/* Cosmos scatters: speckled, not a single band. */
.sheen-cosmos::after{
  background:
    radial-gradient(1.5px 1.5px at 22% 18%,rgba(255,255,255,.85),transparent),
    radial-gradient(1.5px 1.5px at 68% 31%,rgba(255,255,255,.75),transparent),
    radial-gradient(2px 2px at 41% 62%,rgba(255,255,255,.8),transparent),
    radial-gradient(1.5px 1.5px at 82% 74%,rgba(255,255,255,.7),transparent),
    radial-gradient(2px 2px at 14% 84%,rgba(255,255,255,.8),transparent),
    linear-gradient(var(--ang,110deg),transparent 40%,rgba(255,255,255,.22) 52%,transparent 64%);
}
/* Ball reverses carry the seal's own colour through the shine. */
.sheen-ball::after{
  background:linear-gradient(var(--ang,110deg),transparent 34%,rgba(255,255,255,.34) 46%,
    rgba(190,150,255,.26) 54%,transparent 66%);
}
/* A stamp catches light only on the seal, low and to one side. */
.sheen-stamp::after{
  background:radial-gradient(58% 34% at 24% 82%,rgba(255,225,150,.55),transparent 70%);
}
@media (prefers-reduced-motion:reduce){
  .holo-tilt[class*="sheen-"]::after{transition:none;}
}

/* Dark grounds keep the hue and lift the ink. */
:root[data-ground="graphite"] .fact-pill[class*="rarity-"],
:root[data-ground="crevasse"] .fact-pill[class*="rarity-"],
:root[data-ground="graphite"] .fact-pill[class*="pill-"],
:root[data-ground="crevasse"] .fact-pill[class*="pill-"]{
  background:var(--stone-2);
}
:root[data-ground="graphite"] .fact-pill.rarity-illus,:root[data-ground="crevasse"] .fact-pill.rarity-illus,
:root[data-ground="graphite"] .fact-pill.rarity-sir,:root[data-ground="crevasse"] .fact-pill.rarity-sir,
:root[data-ground="graphite"] .fact-pill.pill-masterball,:root[data-ground="crevasse"] .fact-pill.pill-masterball{
  border-color:#9B79C4;color:#DCC8F2;}
:root[data-ground="graphite"] .fact-pill.pill-holo,:root[data-ground="crevasse"] .fact-pill.pill-holo,
:root[data-ground="graphite"] .fact-pill.pill-reverse,:root[data-ground="crevasse"] .fact-pill.pill-reverse,
:root[data-ground="graphite"] .fact-pill.rarity-ultra,:root[data-ground="crevasse"] .fact-pill.rarity-ultra{
  border-color:#63AEDA;color:#BFE2F6;}
:root[data-ground="graphite"] .fact-pill.rarity-double,:root[data-ground="crevasse"] .fact-pill.rarity-double,
:root[data-ground="graphite"] .fact-pill.rarity-hyper,:root[data-ground="crevasse"] .fact-pill.rarity-hyper,
:root[data-ground="graphite"] .fact-pill.pill-firsted,:root[data-ground="crevasse"] .fact-pill.pill-firsted{
  border-color:#C79A3C;color:#F2DCA8;}
:root[data-ground="graphite"] .fact-pill.rarity-ace,:root[data-ground="crevasse"] .fact-pill.rarity-ace,
:root[data-ground="graphite"] .fact-pill.pill-pokeball,:root[data-ground="crevasse"] .fact-pill.pill-pokeball{
  border-color:#CE5346;color:#F6BDB4;}
:root[data-ground="graphite"] .fact-pill.rarity-shiny,:root[data-ground="crevasse"] .fact-pill.rarity-shiny{
  border-color:#5AA69C;color:#B6E4DE;}
:root[data-ground="graphite"] .fact-pill.pill-loveball,:root[data-ground="crevasse"] .fact-pill.pill-loveball{
  border-color:#CE6796;color:#F7C4DA;}
:root[data-ground="graphite"] .fact-pill.pill-friendball,:root[data-ground="crevasse"] .fact-pill.pill-friendball{
  border-color:#68A868;color:#BFE4BF;}
:root[data-ground="graphite"] .fact-pill.pill-cosmos,:root[data-ground="crevasse"] .fact-pill.pill-cosmos{
  border-color:#7276C4;color:#C6C8F0;}

/* --- The wall, where it belongs ---------------------------------------------
   A signed-out visitor reads the whole card -- price, history, condition ladder,
   graded values, live listings -- and meets the account only when they try to
   keep it. Gating the card itself hid the one thing that would make someone
   want an account. */
.signup-gate{
  margin-top:22px;padding:20px 22px;border-radius:8px;
  background:var(--stone-1);border:1px solid var(--stone-3);
  display:flex;flex-direction:column;align-items:flex-start;gap:4px;
}
.signup-gate-line{margin:0;font-size:16px;font-weight:550;color:var(--graphite);letter-spacing:-.012em;}
.signup-gate-sub{margin:0 0 14px;font-size:13.5px;line-height:1.55;color:var(--slate);max-width:46ch;}
.signup-gate button{margin-right:8px;}
:root[data-ground="graphite"] .signup-gate,
:root[data-ground="crevasse"] .signup-gate{background:var(--stone-2);}

/* Close control for dialogs that interrupt you. */
.modal-close{
  position:absolute;top:12px;right:12px;width:30px;height:30px;padding:0;
  display:grid;place-items:center;border-radius:6px;
  background:none;border:1px solid transparent;color:var(--slate-2);box-shadow:none;
}
.modal-close:hover{background:var(--stone-1);border-color:var(--stone-3);color:var(--graphite);}
.modal-close svg{width:15px;height:15px;}

/* ---------------------------------------------------- 14. THE SHARED LIST ---
   A public page, opened by someone who has never seen this app, on a phone, from a
   link a mate sent them. It borrows the whole token system above so it is visibly the
   same product, and adds only what a pick-list needs that the app does not have.

   The basket bar is the one piece of real estate spent boldly: on a 400 card list the
   thing you want in view is what you have chosen so far, not the header. Everything
   else stays quiet so the cards carry the page. */
.share-body{background:var(--bg);color:var(--text);margin:0;padding-bottom:96px;}
.share-top{position:sticky;top:0;z-index:30;background:var(--chrome-bg,var(--panel));
  border-bottom:1px solid var(--edge-soft);backdrop-filter:saturate(140%) blur(8px);}
.share-top-in{max-width:1120px;margin:0 auto;padding:11px 18px;
  display:flex;align-items:center;justify-content:space-between;gap:12px;}
.brand-mark-wrap{display:inline-flex;align-items:center;gap:9px;color:var(--ink);}
.share-theme{background:none;border:1px solid var(--edge);border-radius:8px;
  width:32px;height:32px;line-height:1;cursor:pointer;color:var(--ink-2);font-size:14px;}
.share-theme:hover{border-color:var(--edge-hard);color:var(--ink);}

.share-wrap{max-width:1120px;margin:0 auto;padding:26px 18px 0;}
.share-intro{max-width:56ch;margin-bottom:22px;}
.share-eyebrow{font-family:var(--font-display);font-size:10.5px;font-weight:600;
  text-transform:uppercase;letter-spacing:.13em;color:var(--tag);margin:0 0 6px;}
.share-title{font-family:var(--font-display);font-size:29px;font-weight:600;
  letter-spacing:-.02em;margin:0 0 8px;color:var(--ink);text-wrap:balance;}
.share-lede{font-size:14px;line-height:1.6;color:var(--ink-2);margin:0;}

.share-tools{display:flex;gap:12px;align-items:center;margin-bottom:18px;}
.share-search{flex:1;min-width:0;font-family:inherit;font-size:14px;padding:10px 13px;
  border-radius:9px;border:1px solid var(--edge);background:var(--panel);color:var(--ink);}
.share-search:focus{outline:2px solid var(--blue);outline-offset:1px;border-color:var(--blue);}
.share-count{font-size:12px;color:var(--muted);white-space:nowrap;
  font-variant-numeric:tabular-nums;}

.share-set{margin-bottom:26px;}
.share-set-name{font-family:var(--font-display);font-size:13px;font-weight:600;
  letter-spacing:.02em;color:var(--ink);margin:0 0 10px;padding-bottom:7px;
  border-bottom:1px solid var(--edge-soft);display:flex;justify-content:space-between;}
.share-set-name span{color:var(--muted);font-weight:450;font-variant-numeric:tabular-nums;}

/* Four across on a phone, which is what Jesse asked for on every other grid here. */
.share-grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));}
.share-card{display:flex;flex-direction:column;gap:3px;text-align:left;padding:0 0 9px;
  background:var(--panel);border:1px solid var(--edge-soft);border-radius:10px;
  cursor:pointer;overflow:hidden;font-family:inherit;transition:border-color .12s,box-shadow .12s;}
.share-card:hover{border-color:var(--edge-hard);box-shadow:var(--lift-1);}
.share-card:focus-visible{outline:2px solid var(--blue);outline-offset:2px;}
.share-card.picked{border-color:var(--tag);box-shadow:0 0 0 1px var(--tag);}
.share-card-img{position:relative;display:block;aspect-ratio:63/88;background:var(--panel2);}
.share-card-img img{width:100%;height:100%;object-fit:cover;display:block;}
.share-noimg{display:block;width:100%;height:100%;}
/* The count sits on the picture rather than beside the name: on a dense grid it is the
   only place it reads at a glance without stealing a line from the card's own details. */
.share-tick{position:absolute;top:6px;right:6px;min-width:21px;height:21px;padding:0 5px;
  border-radius:11px;background:var(--tag);color:var(--stock);font-size:11.5px;
  font-weight:650;display:flex;align-items:center;justify-content:center;
  font-variant-numeric:tabular-nums;box-shadow:var(--lift-1);}
.share-card-name{font-size:12px;font-weight:550;color:var(--ink);line-height:1.25;
  padding:7px 8px 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.share-card-sub{font-size:10.5px;color:var(--muted);padding:0 8px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.share-card-meta{display:flex;gap:5px;align-items:center;padding:2px 8px 0;flex-wrap:wrap;}
/* The printing never truncates. It is the reason this page exists: somebody picking
   a reverse holo has to be able to read that it is a reverse holo. When the row is
   tight the card number gives way instead, because a number that ends in an ellipsis
   is still recognisable and a printing that does is not. */
.share-variant{font-size:9.5px;font-weight:600;text-transform:uppercase;
  letter-spacing:.05em;color:var(--tag);background:var(--tag-tint);
  padding:2px 5px;border-radius:4px;white-space:nowrap;flex-shrink:0;}
.share-num{font-size:10px;color:var(--muted);font-variant-numeric:tabular-nums;
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.share-card-foot{display:flex;justify-content:space-between;align-items:baseline;
  gap:6px;padding:4px 8px 0;}
.share-price{font-size:13px;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums;}
.share-have{font-size:9.5px;color:var(--muted);}

.share-loading,.share-empty{color:var(--muted);font-size:14px;padding:26px 0;}

.share-basket{position:fixed;left:0;right:0;bottom:0;z-index:40;
  background:var(--chrome-bg,var(--panel));border-top:1px solid var(--edge);
  box-shadow:0 -6px 24px -12px rgba(20,30,45,.4);
  padding-bottom:env(safe-area-inset-bottom);}
.share-basket-in{max-width:1120px;margin:0 auto;padding:11px 18px;
  display:flex;align-items:center;justify-content:space-between;gap:14px;}
.share-basket-sum{display:flex;flex-direction:column;line-height:1.25;}
.share-basket-sum strong{font-size:14px;color:var(--ink);}
.share-basket-sum span{font-size:17px;font-weight:600;color:var(--tag);
  font-variant-numeric:tabular-nums;}
.share-basket-actions{display:flex;gap:8px;flex-shrink:0;}

/* Joins the global button exception chain rather than fighting it -- see the note on
   the shared button rule; overriding it by specificity is how this drifts later. */
.share-btn{font-family:var(--font-display);font-size:13.5px;font-weight:550;
  padding:10px 16px;border-radius:9px;border:1px solid var(--ink);
  background:var(--ink);color:var(--stock);cursor:pointer;white-space:nowrap;}
.share-btn:hover{opacity:.9;}
.share-btn:focus-visible{outline:2px solid var(--blue);outline-offset:2px;}
.share-btn[disabled]{opacity:.55;cursor:default;}
.share-btn.ghost{background:transparent;color:var(--ink);border-color:var(--edge);}
.share-btn.ghost:hover{border-color:var(--edge-hard);opacity:1;}

.share-send{max-width:430px;}
.share-send-sub{font-size:13px;color:var(--ink-2);margin:0 0 16px;line-height:1.5;}
.share-label{display:block;font-size:11.5px;font-weight:600;color:var(--ink-2);
  margin:12px 0 5px;}
.share-opt{font-weight:400;color:var(--muted);}
.share-input{width:100%;font-family:inherit;font-size:14px;padding:9px 12px;
  border-radius:8px;border:1px solid var(--edge);background:var(--panel);color:var(--ink);
  box-sizing:border-box;}
.share-input:focus{outline:2px solid var(--blue);outline-offset:1px;border-color:var(--blue);}
.share-err{color:var(--down);font-size:12.5px;margin:12px 0 0;}
.share-send-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:20px;}
.share-copy-fallback{position:fixed;left:-9999px;top:0;}

@media (max-width:560px){
  .share-wrap{padding:20px 13px 0;}
  .share-title{font-size:23px;}
  .share-grid{grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:8px;}
  .share-basket-in{padding:10px 13px;}
  .share-btn{padding:10px 13px;font-size:13px;}
  .share-basket-sum strong{font-size:13px;}
}
@media (prefers-reduced-motion:reduce){
  .share-card{transition:none;}
}

/* The owner's share panel. Deliberately plain: this is a thing you do now and then,
   so it borrows the modal and button styles already here rather than inventing a
   look of its own. */
.share-owner{max-width:560px;}
.share-owner-head{display:flex;justify-content:space-between;align-items:center;gap:12px;}
.share-owner-head h3{margin:0;}
.share-owner-tabs{display:flex;gap:4px;margin:14px 0 16px;border-bottom:1px solid var(--edge-soft);}
.share-otab{background:none;border:none;border-bottom:2px solid transparent;
  font-family:var(--font-display);font-size:13px;font-weight:550;color:var(--muted);
  padding:8px 12px;cursor:pointer;}
.share-otab.active{color:var(--ink);border-bottom-color:var(--tag);}
.share-dot{display:inline-flex;align-items:center;justify-content:center;min-width:17px;
  height:17px;padding:0 4px;margin-left:5px;border-radius:9px;background:var(--tag);
  color:var(--stock);font-size:10.5px;font-weight:650;}
.share-hint{font-size:12px;color:var(--muted);margin:8px 0;line-height:1.5;}

.share-setpick{max-height:230px;overflow-y:auto;border:1px solid var(--edge-soft);
  border-radius:9px;padding:4px;}
.share-setrow{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:7px;
  cursor:pointer;font-size:13.5px;}
.share-setrow:hover{background:var(--panel2);}
.share-setname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.share-setn{font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums;}
.share-owner-actions{display:flex;justify-content:space-between;align-items:center;
  gap:12px;margin-top:14px;}
.share-made{margin-top:16px;padding-top:14px;border-top:1px solid var(--edge-soft);}
.share-made-row{display:flex;gap:8px;}
.share-made-row .share-input{flex:1;min-width:0;font-size:12.5px;}

.share-linkrow{display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:11px 0;border-bottom:1px solid var(--edge-soft);}
.share-linkrow.off{opacity:.5;}
.share-linkmain{display:flex;flex-direction:column;gap:2px;min-width:0;}
.share-linkmain .share-hint{margin:0;}

.share-req{padding:13px 0;border-bottom:1px solid var(--edge-soft);}
.share-req.done{opacity:.55;}
.share-req-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;}
.share-req-head .share-hint{display:block;margin:1px 0 0;}
.share-req-right{display:flex;align-items:center;gap:10px;flex-shrink:0;}
.share-req-total{font-weight:650;color:var(--tag);font-variant-numeric:tabular-nums;}
.share-req-note{font-size:12.5px;color:var(--ink-2);margin:7px 0 0;font-style:italic;}
.share-req-items{list-style:none;margin:8px 0 0;padding:0;
  display:flex;flex-direction:column;gap:3px;}
.share-req-items li{font-size:12.5px;color:var(--ink);}
.share-req-items li span:first-child{font-weight:650;font-variant-numeric:tabular-nums;
  margin-right:4px;}
.share-req-items em{font-style:normal;font-size:10px;font-weight:600;
  text-transform:uppercase;letter-spacing:.04em;color:var(--tag);
  background:var(--tag-tint);padding:1px 5px;border-radius:4px;margin:0 5px;}

/* The run-wide Japanese switch. Reads as pressed when on, because it changes what
   every row in the run is, and that has to be obvious before you tap Add. */
.bundle-lang-btn.on{background:var(--tag);border-color:var(--tag);color:var(--stock);}
/* Marks a tile as the Japanese printing. Sits beside the name rather than over the art
   so it survives the three-up phone grid, where anything on the image is unreadable. */
.tile-jp-chip{font-size:9.5px;font-weight:700;letter-spacing:.04em;color:var(--tag);
  background:var(--tag-tint);padding:1px 4px;border-radius:4px;flex-shrink:0;}
/* The guide box while it holds for a frame sharp enough to identify. Amber rather
   than the usual detecting green, so waiting for focus does not read as ready. */
#cameraGuide.waiting-focus{border-color:#C9962E;box-shadow:0 0 0 2px rgba(201,150,46,.25);}
/* Printing choice on the scan setup sheet. Same pill treatment as the game row above
   it, because it is the same kind of decision: what am I about to scan. */
.bundle-langpick{display:flex;gap:6px;margin-top:5px;}
/* The card's own name under the English one. Smaller and quieter: it is there to be
   matched against the card in your hand, not read as the primary label. */
.tile-native{font-size:11px;color:var(--ink-2);line-height:1.25;margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* The demo-account banner. Deliberately loud and not dismissible: its whole job is to
   stop somebody mistaking a sample for their own collection, and a banner you can
   close is one you will close and then forget. */
/* The hidden attribute has to win. A class rule setting display:flex outranks the
   browser's own [hidden]{display:none}, so the banner stayed on screen for everyone
   after it was shown once -- including on a real account, telling the owner their own
   collection was a demo. Every check said hidden was true, because it was; it just
   was not doing anything. */
.demo-banner[hidden]{display:none;}
.demo-banner{display:flex;align-items:center;gap:10px;
  padding:9px 18px;background:var(--tag);color:var(--stock);
  font-size:13px;line-height:1.4;
  /* Positioned, or the atmospheric .bg-scene layer paints straight over it. That is
     a fixed full-viewport div at z-index 0, and any positioned element paints above
     in-flow static content, so the banner reserved its space and then showed the page
     background through it: the gap was there, the warning was not. */
  /* Sticks under the chrome. The offset is a plain number that matches the header's
     own min-height, declared a few hundred lines below this as `header{min-height:64px}`
     -- change one and change the other.
     It used to be fed by JavaScript from the header's measured size, which held
     perfectly still in a desktop browser and came apart on a real phone: the URL bar
     collapsing during a scroll changes the header's height, so the observer fired
     mid-scroll and moved the offset under your thumb. Nothing measures anything now.
     The banner cannot live inside <header> either: that element is display:flex with
     .nav-row flattened into it by display:contents, so a child there becomes a column
     beside the tabs rather than a bar beneath them. */
  position:sticky;top:64px;z-index:39;}
.demo-banner strong{font-weight:700;}
.demo-dot{width:8px;height:8px;border-radius:50%;background:var(--stock);flex-shrink:0;}
/* The message takes the room that is left and wraps inside itself, rather than the
   three pieces each wrapping onto their own line and tripling the bar's height. */
.demo-banner > span:not(.demo-dot){flex:1 1 auto;min-width:0;}
.demo-leave{flex-shrink:0;margin-left:6px;color:var(--stock);text-decoration:underline;
  font-size:12.5px;padding:0;white-space:nowrap;}
@media (max-width:560px){
  .demo-banner{padding:8px 13px;font-size:12px;}
}

/* -------------------------------------------- The card's actions, always reachable ---
   Save, Sell, Delete and Close sat at the very bottom of a card that can be well over
   a screen tall, so acting on a card meant scrolling to the end to find the buttons and
   scrolling back to see what you had changed.

   Sticky rather than fixed: it stays inside the dialog and cannot end up floating over
   the page when the dialog is closed. It works against whichever ancestor is doing the
   scrolling, which differs here -- the dialog itself on desktop, the backdrop behind it
   on a phone (see the max-width:760px block above, where the dialog deliberately does
   not scroll so iOS is not running two momentum surfaces at once). */
.detail-modal .modal-actions{
  position:sticky;bottom:0;z-index:5;
  margin-top:22px;padding:14px 22px;
  background:var(--panel);
  border-top:1px solid var(--edge-soft);
  /* Lifted off the content passing underneath, so it reads as a bar rather than as
     buttons that happen to be sitting on top of the card. */
  box-shadow:0 -8px 18px -12px rgba(20,30,45,.35);
}
@media (max-width:760px){
  /* Spans the dialog's own 16px side padding, so the bar meets both edges. */
  .detail-modal .modal-actions{
    margin-left:-16px;margin-right:-16px;padding:12px 16px;
    padding-bottom:calc(12px + env(safe-area-inset-bottom));
    justify-content:stretch;
  }
  .detail-modal .modal-actions button{flex:1 1 auto;min-width:0;}
}

/* --- Bulk holdings, and pretty dates on native date inputs ---------------- */

/* Value that is deliberately NOT in the headline. Quieter than the real stats so
   it reads as a footnote to the total rather than competing with it. */
.stat-bulk .value{color:var(--slate);}
.stat-bulk-sub{font-size:14px;color:var(--slate-2);letter-spacing:-.01em;}

/* The real input keeps every job -- picking, keyboard entry, the native mobile
   wheel -- and only its rendered text is hidden, because the browser's locale
   format cannot be changed. The formatted date is painted over the top.
   The calendar button sets its own colour, so it survives `color:transparent`. */
.date-field{position:relative;display:block;}
.date-field > input[type="date"]{color:transparent;}
/* Selecting inside the field would highlight text nobody can see. */
.date-field > input[type="date"]::selection{background:transparent;}
.date-shown{
  position:absolute;left:0;top:0;height:100%;
  display:flex;align-items:center;
  /* Sits exactly where the native text did: 1px border + 12px padding. Right edge
     stops short of the calendar button. Font must match the input's own 14px or the
     painted date will not line up with the other fields in the form. */
  padding-left:13px;padding-right:42px;
  font-size:14px;font-family:inherit;
  font-variant-numeric:tabular-nums;
  color:var(--ink);pointer-events:none;white-space:nowrap;overflow:hidden;
}
.date-shown.is-empty{color:var(--slate-2);}
/* Focus belongs to the input underneath; the painted text must not look inert
   while the field is active. */
.date-field > input[type="date"]:focus ~ .date-shown{color:var(--ink);}

/* --- The pitch CTA ------------------------------------------------------- *
   Lives in the demo banner, which is the only place it appears, because the
   demo login is the one Jesse hands to somebody he is pitching.

   Deliberately louder than anything else in this app. It sits on the brass
   banner, so the red is a vivid one rather than the muted --down used for
   losses: a muted red on brown goes muddy and reads as a disabled control. */
.overview-cta{
  --overview-red:#E8392B;
  position:relative;flex-shrink:0;
  display:inline-flex;align-items:center;gap:8px;
  padding:9px 20px;border-radius:100px;
  background:var(--overview-red);color:#fff;
  font-size:14px;font-weight:650;letter-spacing:.01em;
  text-decoration:none;white-space:nowrap;
  border:1px solid rgba(255,255,255,.34);
  box-shadow:0 0 0 0 rgba(232,57,43,.55), 0 3px 10px rgba(0,0,0,.26);
  transition:transform .16s ease, box-shadow .16s ease;
}
.overview-cta:hover{
  transform:translateY(-1px);
  box-shadow:0 0 0 5px rgba(232,57,43,.22), 0 6px 16px rgba(0,0,0,.3);
}
.overview-cta:active{transform:translateY(0);}
.overview-cta:focus-visible{outline:2px solid #fff;outline-offset:2px;}

/* The halo, on its own element so the button's own shadow stays available for
   the hover state and the two never fight. */
.overview-cta-pulse{
  position:absolute;inset:-3px;border-radius:100px;
  border:1.5px solid var(--overview-red);
  pointer-events:none;opacity:0;
}
@media (prefers-reduced-motion:no-preference){
  .overview-cta-pulse{animation:overviewPulse 2.4s cubic-bezier(.22,.61,.36,1) infinite;}
}
@keyframes overviewPulse{
  0%   {opacity:.85;transform:scale(1);}
  70%  {opacity:0;transform:scale(1.28);}
  100% {opacity:0;transform:scale(1.28);}
}
@media (max-width:560px){
  .overview-cta{padding:7px 15px;font-size:12.5px;}
}

/* --- Slabs, fitted ------------------------------------------------------- *
   A slab carries .holo-tilt, which forces the 63:88 card footprint and hides
   overflow. That footprint is right: a graded card should occupy the same space
   as a raw one, in the grid and in the detail view, or the layout goes ragged.

   What was wrong is that nothing gave the extra height back. The label and the
   cert bar are real height on top of the artwork, and the artwork was still being
   drawn at full size, so it ran 64px past the bottom of the case and the case
   simply cut it off mid-card.

   So the case is a column: label and cert bar take what they need, the window
   takes the rest, and the artwork is contained inside whatever is left. That is
   also what a real slab looks like -- the card sits inside the case with room
   around it, rather than filling it edge to edge. */
.slab-case{display:flex;flex-direction:column;flex-shrink:0;}
.slab-label,.slab-label-bottom{flex:0 0 auto;}
.slab-window{
  flex:1 1 auto;min-height:0;
  display:flex;align-items:center;justify-content:center;
}
.slab-window img{
  width:auto;height:100%;max-width:100%;max-height:100%;
  object-fit:contain;
}

/* The same object at tile size.
   A dark bar with a company and a number on it reads as a badge stuck onto a photo.
   A real case carries the card's own identity on the left and the grader's mark on
   the right, over a white window with the card set back inside it, and that is what
   makes it read as an object rather than a label. Same structure as the detail view,
   so opening one is a continuation rather than a surprise.

   This replaced small brass caps under the artwork, which were argued for on the
   grounds that a coloured strip drawn 130 times becomes wallpaper. Fair point, but
   they were rendered inside a card-aspect box with hidden overflow and an image set
   to fill it, so they were never visible at all. */
.tile-thumb.tile-slab{
  display:flex;flex-direction:column;
  background:var(--stock);
}
.tile-thumb.tile-slab .tile-slab-strip{
  margin:0;flex:0 0 auto;
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:6px 8px;background:var(--ink);color:var(--ink-on-dark);
}
.tile-slab-id{display:flex;flex-direction:column;gap:1px;min-width:0;}
.tile-slab-name,.tile-slab-set{
  font-size:8.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.tile-slab-set{font-weight:600;opacity:.62;}
.tile-slab-mark{display:flex;align-items:center;gap:6px;flex:0 0 auto;}
.tile-thumb.tile-slab .tile-slab-company{
  font-size:8.5px;font-weight:700;letter-spacing:.1em;color:rgba(255,255,255,.72);
}
.tile-thumb.tile-slab .tile-slab-grade{
  font-size:15px;font-weight:600;line-height:1;color:#fff;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
}
/* The window. A card sits inside a case with room around it, never edge to edge.
   Direct child only: the label above holds the company logo, and an unscoped
   `img` here caught that too and stretched it to the full width of the tile. */
.tile-thumb.tile-slab > img{
  flex:1 1 auto;min-height:0;width:100%;height:auto;
  object-fit:contain;padding:7px 9px 9px;
  background:linear-gradient(180deg,#fff,var(--wall));
}

/* --- Sets: grid or list -------------------------------------------------- *
   Two ways of reading the same shelf. The grid leads with the logo, which is how
   somebody who knows the game recognises a set at a glance; the list leads with the
   name, which is how somebody scanning for one particular set finds it. Neither is
   the right default for both jobs, so it is a choice. */
.chase-toolbar{
  display:flex;align-items:center;justify-content:space-between;
  gap:12px;flex-wrap:wrap;margin:12px 0 4px;
}
.chase-views{display:inline-flex;gap:2px;padding:2px;border-radius:8px;
  background:var(--wall-lo);}
/* Sized to its content rather than stretched: a full-width select above a grid of
   sets reads as a filter on the page, not a control on the list. */
.chase-sort select{width:auto;min-width:186px;padding-top:7px;padding-bottom:7px;font-size:12.5px;}
.chase-view-opt{
  padding:5px 13px;border-radius:6px;border:0;background:none;cursor:pointer;
  font-family:inherit;font-size:12.5px;font-weight:600;color:var(--slate-2);
}
.chase-view-opt:hover{color:var(--ink);}
.chase-view-opt.on{background:var(--stock);color:var(--ink);box-shadow:var(--lift-1);}

.chase-set-grid{
  display:grid;gap:14px;margin-top:12px;
  grid-template-columns:repeat(auto-fill,minmax(168px,1fr));
}
.chase-set-card{
  display:flex;flex-direction:column;gap:0;padding:0;cursor:pointer;text-align:left;
  background:var(--stock);border:1px solid var(--edge);border-radius:10px;
  overflow:hidden;font-family:inherit;color:var(--ink);
  box-shadow:var(--lift-1);
  transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .16s ease,border-color .16s ease;
}
.chase-set-card:hover{transform:translateY(-3px);box-shadow:var(--lift-2);border-color:var(--edge-hard);}
/* The logo gets a lit panel of its own rather than sitting on the card face: set
   logos are drawn for print and most of them carry their own light. */
.chase-set-card-art{
  display:grid;place-items:center;padding:20px 16px;min-height:104px;
  background:radial-gradient(120% 80% at 30% 0%, #fff, var(--wall) 78%);
  border-bottom:1px solid var(--edge-soft);
}
.chase-set-card-art img{max-width:100%;max-height:66px;width:auto;object-fit:contain;}
.chase-set-card-art .chase-set-card-fallback{
  font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--slate-2);
}
.chase-set-card-body{padding:11px 13px 13px;display:flex;flex-direction:column;gap:3px;min-width:0;}
.chase-set-card-name{
  font-size:13.5px;font-weight:600;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.chase-set-card-meta{font-size:11.5px;color:var(--slate-2);}
@media (max-width:560px){
  .chase-set-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px;}
  .chase-set-logo{width:78px;height:46px;}
  .chase-set-logo img{max-height:46px;}
}

/* --- Slab labels, per grading company ------------------------------------ *
   The company classes (.slab-psa, .slab-bgs, ...) have existed since slabs were
   added but nothing ever styled them, so every company rendered the same dark bar.
   That is why a PSA looked approximately right and a BGS looked wrong: the label on
   a real BGS holder is black with a gold grade block, and PSA's is white card stock
   with a red logo and black type.

   These are recognisable representations of the holder, drawn from the app's own
   data. Nothing here reproduces a company's artwork beyond its wordmark. */

/* PSA: white label stock, black type, red only on the logo. */
.slab-psa .slab-label{
  background:#FBFBF9;color:#15181C;
  border-bottom:1px solid #DDD9CF;
}
.slab-psa .slab-l1,.slab-psa .slab-r1{color:#6A6459;}
.slab-psa .slab-l3,.slab-psa .slab-r2{color:#6A6459;}
.slab-psa .slab-grade-number{color:#15181C;font-weight:700;}
/* Clean stock behind the logo, not a tinted panel: a PSA holder's label is one
   sheet of white and the logo sits straight on it. */
.slab-psa .slab-label-bottom{
  background:#FBFBF9;color:#6A6459;border-top:1px solid #E6E2D8;
}
/* PSA's signature is a red logo and a big black number on white. BGS gets its weight
   from the gold block, so PSA has to get its weight from the number. */
.slab-psa .slab-grade-number{font-size:26px;letter-spacing:-.02em;}
.slab-psa .slab-logo-img{height:26px;}
.slab-psa .slab-cert{color:#6A6459;}
.slab-psa .slab-company-wordmark{color:#E4002B;font-weight:800;letter-spacing:.02em;}

/* BGS: black label, silver type, and a grade block whose colour is the grade.
   Beckett's own tiers -- gold from 9.5, silver below it, and the black label that
   only a straight-tens Pristine 10 gets. */
.slab-bgs .slab-label{
  background:linear-gradient(180deg,#141414,#0B0B0B);color:#D8D8D8;
  border-bottom:1px solid #2C2C2C;
}
.slab-bgs .slab-l1,.slab-bgs .slab-r1,.slab-bgs .slab-l3,.slab-bgs .slab-r2{color:#9A9A9A;}
.slab-bgs .slab-l2{color:#F2F2F2;}
.slab-bgs .slab-grade-number{
  color:#0B0B0B;background:linear-gradient(180deg,#E4CE7B,#B8912F);
  padding:2px 9px;border-radius:3px;font-weight:800;
}
.slab-bgs .slab-label-bottom{background:#0B0B0B;color:#8A8A8A;border-top:1px solid #2C2C2C;}
.slab-bgs .slab-cert{color:#8A8A8A;}
.slab-bgs .slab-company-wordmark{color:#E4CE7B;font-weight:800;}

/* CGC: white stock, blue rule and wordmark. */
.slab-cgc .slab-label{background:#FFFFFF;color:#15181C;border-bottom:2px solid #123C8C;}
.slab-cgc .slab-l1,.slab-cgc .slab-r1,.slab-cgc .slab-l3,.slab-cgc .slab-r2{color:#5A6472;}
.slab-cgc .slab-grade-number{color:#123C8C;font-weight:700;}
.slab-cgc .slab-label-bottom{background:#F3F5F9;color:#5A6472;border-top:1px solid #DCE2EC;}
.slab-cgc .slab-cert{color:#5A6472;}
.slab-cgc .slab-company-wordmark{color:#123C8C;font-weight:800;}

/* SGC: black label, white type, no metal. */
.slab-sgc .slab-label{background:#101010;color:#F2F2F2;border-bottom:1px solid #2A2A2A;}
.slab-sgc .slab-l1,.slab-sgc .slab-r1,.slab-sgc .slab-l3,.slab-sgc .slab-r2{color:#9A9A9A;}
.slab-sgc .slab-grade-number{color:#FFFFFF;font-weight:700;}
.slab-sgc .slab-label-bottom{background:#0A0A0A;color:#8A8A8A;border-top:1px solid #2A2A2A;}
.slab-sgc .slab-cert{color:#8A8A8A;}
.slab-sgc .slab-company-wordmark{color:#FFFFFF;font-weight:800;}

/* The window behind the card is the holder, not the page. Slightly cool and glossy
   so the card reads as sitting under plastic rather than printed onto the panel. */
.slab-case .slab-window{
  background:linear-gradient(168deg,#FFFFFF 0%,#F4F6F9 46%,#E7ECF2 100%);
}
.slab-case .slab-window img{box-shadow:0 1px 3px rgba(15,20,28,.24), 0 6px 14px -6px rgba(15,20,28,.28);}

/* --- The same, at tile size ---------------------------------------------- */
/* Scoped through .tile-thumb on purpose. `.tile-thumb img` is (0,1,1) and sets the
   card image to fill its box; a bare `.tile-slab-logo` is (0,1,0) and loses to it, so
   the company mark inherited width:100%/height:100% and rendered 704px wide inside a
   208px tile. Second time this exact collision has bitten on this element. */
.tile-thumb.tile-slab .tile-slab-logo{
  height:14px;width:auto;max-width:44px;object-fit:contain;display:block;flex:0 0 auto;
  border-radius:0;
}
.tile-slab.slab-psa .tile-slab-strip{background:#FBFBF9;color:#15181C;}
.tile-slab.slab-psa .tile-slab-set{color:#6A6459;opacity:1;}
.tile-slab.slab-psa .tile-slab-company{color:#E4002B;}
.tile-slab.slab-psa .tile-slab-grade{color:#15181C;}

.tile-slab.slab-bgs .tile-slab-strip{background:linear-gradient(180deg,#141414,#0B0B0B);color:#F2F2F2;}
.tile-slab.slab-bgs .tile-slab-set{color:#9A9A9A;opacity:1;}
.tile-slab.slab-bgs .tile-slab-company{color:#B8912F;}
.tile-slab.slab-bgs .tile-slab-grade{
  color:#0B0B0B;background:linear-gradient(180deg,#E4CE7B,#B8912F);
  padding:1px 6px;border-radius:3px;
}

.tile-slab.slab-cgc .tile-slab-strip{background:#FFFFFF;color:#15181C;border-bottom:2px solid #123C8C;}
.tile-slab.slab-cgc .tile-slab-set{color:#5A6472;opacity:1;}
.tile-slab.slab-cgc .tile-slab-company{color:#123C8C;}
.tile-slab.slab-cgc .tile-slab-grade{color:#123C8C;}

.tile-slab.slab-sgc .tile-slab-strip{background:#101010;color:#F2F2F2;}
.tile-slab.slab-sgc .tile-slab-set{color:#9A9A9A;opacity:1;}

/* --- Sets that have not come out yet ------------------------------------- */
.chase-soon{
  display:inline-block;margin-right:7px;padding:2px 7px;border-radius:100px;
  background:var(--tag-tint);color:var(--brass-lo);
  font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;
  vertical-align:1px;
}
/* A dashed edge on the art panel, because an unreleased set has no cards behind it
   yet and a solid frame implies there is something to open. */
.chase-set-card.is-upcoming .chase-set-card-art{
  border-bottom-style:dashed;border-bottom-color:var(--brass);
  background:radial-gradient(120% 80% at 30% 0%, #fff, var(--tag-tint) 92%);
}

/* --- A feature that is not ready to be trusted --------------------------- *
   Red rather than the brass used for "coming soon", because those are different
   promises: one is "not here yet", this is "here, and wrong". Anybody who lands on
   the centering page from the outside has no other way to know that. */
.not-ready-notice{
  display:flex;align-items:flex-start;gap:11px;
  margin:0 0 16px;padding:13px 15px;
  background:var(--down-tint);border-left:3px solid var(--down);
  border-radius:0 8px 8px 0;
  font-size:13.5px;line-height:1.5;color:#7E2A22;
}
.not-ready-notice strong{font-weight:700;}
.not-ready-tag{
  flex:0 0 auto;margin-top:1px;padding:3px 8px;border-radius:100px;
  background:var(--down);color:#fff;
  font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.11em;
}
@media (max-width:560px){
  .not-ready-notice{flex-direction:column;gap:8px;padding:12px 13px;font-size:13px;}
}

/* --- Thumb-sized controls on a phone ------------------------------------- *
   Measured on a 390px screen, logged out: 44 controls came in under the 40px a
   thumb wants, on a product whose whole pitch is that you use it one-handed while
   the other hand holds a stack of cards.

   The worst were the 24 "Search all printings" buttons under the search results at
   135x19, which is the most-tapped control on the Add screen.

   Grown where there is room, and hit-area-extended where growing the box would
   push the layout around. Phone only: on a mouse these sizes are already fine and
   a 40px minimum would coarsen the desktop UI for nothing. */
@media (max-width:760px){
  .result-card button,
  .pill,
  .secondary,
  .mode-toggle,
  .currency-select-trigger,
  .account-menu-trigger,
  .brand-logo{
    min-height:40px;
  }
  /* Centre what is inside them, or the label sits at the top of its new box. */
  .result-card button,.pill,.secondary,.mode-toggle,.currency-select-trigger,.brand-logo{
    display:inline-flex;align-items:center;justify-content:center;
  }
  .brand-logo{justify-content:flex-start;}
  /* Square icon buttons need the width too, not just the height. */
  .mode-toggle{min-width:40px;}
  /* The search button, which has no class of its own and sits next to the field it
     submits. The one control on this screen somebody presses before anything else. */
  #searchInput + button{min-height:40px;}

  /* Inside the card detail, which is where most of a demo actually happens. The
     listing rows were 19px tall, and they are the one thing on that screen somebody
     is invited to tap to check a price for themselves. */
  .ebay-listing-row,
  .data-source-tab,
  .chart-range-pill,
  .variant-pill,
  .detail-back,
  .detail-list-btn,
  .grade-more-toggle,
  .ebay-listings-toggle{
    min-height:40px;
    display:inline-flex;align-items:center;
  }
  .ebay-listing-row{display:flex;}
  .data-source-tab,.chart-range-pill{justify-content:center;}
  /* The last two, both three pixels short: the favourite toggle and Save. */
  .detail-fav-btn,#detailModalBg .modal-actions button,.modal-actions button{min-height:40px;}

  /* A 22px icon that cannot grow without pushing the text beside it around, so the
     target is extended past the box instead. Kept to 40 rather than 44 so two of
     them side by side cannot overlap. */
  .help-btn{position:relative;}
  .help-btn::after{
    content:'';position:absolute;left:50%;top:50%;
    transform:translate(-50%,-50%);width:40px;height:40px;
  }
}

/* On a phone a collection tile is about 82px wide, and the slab label's two lines of
   card name and set were being set at 8.5px in it, which is not readable and is not
   worth the room. The company mark and the grade are what say "this one is slabbed",
   and the card's name is already written directly under the tile, so the label keeps
   the part that is doing work and drops the part that repeats. */
@media (max-width:560px){
  .tile-thumb.tile-slab .tile-slab-id{display:none;}
  .tile-thumb.tile-slab .tile-slab-strip{justify-content:space-between;padding:4px 6px;}
  .tile-thumb.tile-slab .tile-slab-grade{font-size:12px;}
  .tile-thumb.tile-slab .tile-slab-logo{height:12px;max-width:36px;}
}

/* --- Favourite, from inside the card ------------------------------------- *
   The tile buttons are hover-only and a phone has no hover, so this is the only
   way to reach favouriting on the device the app is mostly used on. Sits beside
   "List for sale" and behaves the same way. */
.detail-fav-btn{
  display:inline-flex;align-items:center;gap:7px;
  padding:8px 13px;border-radius:8px;
  background:var(--stock);border:1px solid var(--edge);color:var(--ink-2);
  font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;white-space:nowrap;
  transition:border-color .14s,color .14s,background .14s;
}
.detail-fav-btn:hover{border-color:var(--ink-3);color:var(--ink);}
.detail-fav-btn svg{width:15px;height:15px;flex:0 0 auto;}
.detail-fav-btn.active{
  background:var(--tag-tint);border-color:var(--brass);color:var(--brass-lo);
}
.detail-fav-btn.active svg{fill:currentColor;}

/* --- Recent sales -------------------------------------------------------- *
   These rows were inline-styled with no class of their own, so nothing could
   target them and they sat at 19px tall on a phone -- on the one part of the card
   that invites you to go and check a sale for yourself. */
.recent-sale-row{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-size:12px;padding:3px 0;border-bottom:1px solid var(--border);
}
.recent-sale-when{display:flex;align-items:center;gap:6px;min-width:0;}
.recent-sale-price{color:inherit;font-variant-numeric:tabular-nums;}
@media (max-width:760px){
  .recent-sale-row{padding:0;}
  .recent-sale-row,.recent-sale-price{min-height:40px;}
  .recent-sale-price{display:inline-flex;align-items:center;padding-left:12px;}
}

/* --- Running out of scans ------------------------------------------------ *
   Not styled as an error. Reaching a free limit is the product working, and it is
   the moment somebody is most persuadable -- they were mid-task and they wanted the
   thing. Brass rather than red, and the way to keep working without paying is right
   there underneath. */
.scan-limit{
  max-width:520px;padding:20px 22px;border-radius:12px;
  background:var(--stock);border:1px solid var(--edge);border-left:3px solid var(--brass);
  box-shadow:var(--lift-1);
}
.scan-limit-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;}
.scan-limit-tag{
  padding:3px 9px;border-radius:100px;background:var(--tag-tint);color:var(--brass-lo);
  font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.11em;
}
.scan-limit-count{font-size:12px;color:var(--slate-2);font-variant-numeric:tabular-nums;}
.scan-limit p{margin:0;font-size:14.5px;line-height:1.55;color:var(--ink);}
.scan-limit-alt{margin-top:12px !important;padding-top:12px;border-top:1px solid var(--edge-soft);
  font-size:13px !important;color:var(--slate) !important;}

/* --- Scans left this month ----------------------------------------------- *
   Sits under the scan buttons, and only when there is a limit to show -- an
   uncapped plan gets nothing rather than a bar that never moves. */
.scan-meter{margin:10px 0 2px;max-width:340px;}
.scan-meter-row{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:5px;}
.scan-meter-label{font-size:12px;color:var(--slate);}
.scan-meter-count{font-size:11.5px;color:var(--slate-2);font-variant-numeric:tabular-nums;}
.scan-meter-track{height:4px;border-radius:99px;background:var(--wall-lo);overflow:hidden;}
.scan-meter-fill{height:100%;background:var(--ink);border-radius:99px;transition:width .3s ease;}
/* Three states, not a gradient: fine, getting close, gone. A bar that shades
   gradually tells somebody nothing they can act on. */
.scan-meter.is-low .scan-meter-fill{background:var(--brass);}
.scan-meter.is-low .scan-meter-label{color:var(--brass-lo);}
.scan-meter.is-out .scan-meter-fill{background:var(--down);}
.scan-meter.is-out .scan-meter-label{color:var(--down);font-weight:600;}

/* ============================================================================
   MARKETPLACE
   ----------------------------------------------------------------------------
   Same Vitrine world as the rest of the app: cold stone ground, cards in wells,
   brass on almost nothing. A shelf of cards for sale is the one screen here that
   is genuinely a grid of objects, so it gets the same treatment the collection
   grid does rather than a new visual language.
   ============================================================================ */

/* Shown while the feature flag is off. Brass rather than red: this is "not yet",
   not "something went wrong". */
.market-closed{border-left:3px solid var(--brass);}
.market-closed-tag{
  display:inline-block;margin-bottom:10px;padding:3px 9px;border-radius:100px;
  background:var(--tag-tint);color:var(--brass-lo);
  font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.11em;
}

.market-tabs{display:flex;gap:2px;padding:2px;border-radius:9px;background:var(--wall-lo);
  margin-bottom:16px;width:fit-content;}
.market-tab{
  min-height:40px;padding:0 18px;border:0;border-radius:7px;background:none;cursor:pointer;
  font-family:inherit;font-size:13px;font-weight:600;color:var(--slate-2);
}
.market-tab:hover{color:var(--ink);}
.market-tab.active{background:var(--stock);color:var(--ink);box-shadow:var(--lift-1);}

.market-filters{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:10px;}
.market-filters input[type=search]{flex:1 1 220px;max-width:340px;}
.market-filters select{width:auto;min-width:150px;}

/* --- The shelf ----------------------------------------------------------- */
.market-grid{
  display:grid;gap:18px 14px;margin-top:14px;
  grid-template-columns:repeat(auto-fill,minmax(168px,1fr));
}
.market-card{cursor:pointer;}
.market-card-art{
  position:relative;aspect-ratio:63/88;border-radius:6px;overflow:hidden;
  background:var(--stone-2);box-shadow:var(--lift-2);
  transition:transform .18s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease;
}
.market-card:hover .market-card-art{transform:translateY(-4px);box-shadow:var(--lift-3);}
.market-card-art img{width:100%;height:100%;object-fit:cover;display:block;}
.market-card-grade,.market-card-boost{
  position:absolute;top:6px;padding:3px 7px;border-radius:4px;
  font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
}
.market-card-grade{left:6px;background:var(--ink);color:var(--ink-on-dark);}
/* A boost buys position in results the buyer already asked for, so it is labelled
   rather than disguised. */
.market-card-boost{right:6px;background:var(--brass);color:#fff;}
.market-card-body{display:flex;flex-direction:column;gap:2px;padding:9px 2px 0;min-width:0;}
.market-card-name{font-size:13.5px;font-weight:600;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.market-card-set{font-size:11.5px;color:var(--slate-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.market-card-foot{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-top:3px;}
.market-card-price{font-size:16px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums;}
.market-card-qty{font-size:11px;color:var(--slate-2);}
.market-card-meta{font-size:11px;color:var(--slate-2);}
.market-empty{padding:28px 4px;color:var(--slate);}

/* --- One listing --------------------------------------------------------- */
.listing-modal{max-width:640px;}
.listing-close{margin-bottom:8px;}
.listing-top{display:grid;grid-template-columns:150px minmax(0,1fr);gap:20px;}
.listing-art{border-radius:8px;overflow:hidden;background:var(--stone-2);box-shadow:var(--lift-2);
  aspect-ratio:63/88;}
.listing-art img{width:100%;height:100%;object-fit:cover;display:block;}
.listing-info h2{margin:0 0 2px;font-size:22px;letter-spacing:-.02em;}
.listing-pills{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0;}
.listing-price{font-size:30px;font-weight:300;letter-spacing:-.03em;margin:10px 0 2px;}
.listing-note{margin-top:10px;padding:10px 12px;border-radius:8px;background:var(--wall);
  font-size:13.5px;color:var(--slate);}
.listing-buy{margin-top:22px;padding-top:18px;border-top:1px solid var(--edge);}
.listing-buy h4{margin:0 0 10px;font-size:11px;font-weight:700;text-transform:uppercase;
  letter-spacing:.13em;color:var(--slate-2);}
.listing-post{
  display:flex;align-items:center;gap:10px;padding:10px 12px;margin-bottom:6px;
  border:1px solid var(--edge);border-radius:8px;cursor:pointer;font-size:13.5px;
}
.listing-post:hover{border-color:var(--ink-3);}
.listing-post span:first-of-type{flex:1;}
.listing-post em{font-style:normal;font-size:10px;font-weight:700;text-transform:uppercase;
  letter-spacing:.09em;color:var(--up);}
.listing-post-price{font-variant-numeric:tabular-nums;color:var(--slate);}
.listing-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:16px;}
.listing-offer{display:flex;gap:8px;align-items:center;}
.listing-offer input{width:110px;}

/* --- Selling ------------------------------------------------------------- */
.market-selling-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:12px;}
.market-offer{
  display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;
  padding:12px 0;border-bottom:1px solid var(--edge-soft);
}
.market-offer:last-child{border-bottom:0;}
.market-offer-what{font-size:14px;}
.market-offer-actions{display:flex;gap:8px;}
.market-mine-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto auto auto auto;
  gap:14px;align-items:center;padding:11px 0;border-bottom:1px solid var(--edge-soft);
  font-size:13.5px;
}
.market-mine-row:last-child{border-bottom:0;}
.market-mine-name{display:flex;flex-direction:column;min-width:0;}
.market-mine-name .sub{font-size:11.5px;}
.market-mine-price{font-variant-numeric:tabular-nums;font-weight:600;}
.market-mine-qty,.market-mine-status{font-size:11.5px;color:var(--slate-2);}
.market-mine-sold .market-mine-status{color:var(--up);font-weight:600;}
.market-mine-ended{opacity:.55;}

/* --- Orders -------------------------------------------------------------- */
.market-order{margin-bottom:14px;}
.market-order-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px;}
.market-order-status{
  padding:3px 9px;border-radius:100px;font-size:9.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.1em;background:var(--wall-lo);color:var(--slate);
}
.status-placed{background:var(--tag-tint);color:var(--brass-lo);}
.status-shipped,.status-paid{background:var(--blue-tint);color:var(--blue-lo);}
.status-complete{background:var(--up-tint);color:var(--up);}
.status-declined,.status-cancelled{background:var(--down-tint);color:var(--down);}
.market-order-item{display:flex;justify-content:space-between;gap:12px;padding:5px 0;font-size:13.5px;}
.market-order-totals{display:flex;justify-content:space-between;gap:12px;margin-top:8px;
  padding-top:8px;border-top:1px solid var(--edge-soft);font-size:13.5px;}
.market-order-note{margin-top:10px;padding:10px 12px;border-radius:8px;
  background:var(--tag-tint);color:var(--brass-lo);font-size:13px;}
.market-order-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}

@media (max-width:700px){
  .listing-top{grid-template-columns:1fr;}
  .listing-art{max-width:190px;}
  .market-mine-row{grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;}
  .market-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:14px 10px;}
}

/* --- Ratings ---------------------------------------------------------------------
   Deliberately quiet. Stars are a well-worn shape and dressing them up reads as a
   game, which is the wrong note on a screen where somebody is deciding whether to
   trust a stranger with $200. The only saturated colour is the star itself. */

.stars{display:inline-flex;align-items:center;gap:4px;color:var(--brass-lo);
  letter-spacing:.5px;font-size:11px;white-space:nowrap;}
.stars-n{color:var(--ink-3);font-size:11px;letter-spacing:0;
  font-variant-numeric:tabular-nums;}
.stars-none{color:var(--ink-3);letter-spacing:0;font-style:italic;}

.market-pending{border-color:var(--brass-lo);margin-bottom:16px;}
.market-pending-head{font-weight:650;margin:0 0 2px;}
.market-pending-sub{color:var(--ink-2);margin:0 0 12px;font-size:13px;}

.market-rate{display:flex;flex-direction:column;gap:10px;margin-top:14px;
  padding-top:14px;border-top:1px solid var(--stone-3);}
.market-rate-q{margin:0;font-size:13px;font-weight:600;}
.market-rate-err{color:var(--danger,#B4232B);}
.market-rate-stars{display:flex;gap:2px;}

/* 44px so it is a real tap target on a phone. The star itself stays small; the
   generous box around it is what makes this workable one-handed. */
/* flex:0 0 44px, not width:44px -- as a flex item it shrinks to fit and lands at
   24px, which is under the tap-target floor and turns "I meant four" into three. */
.market-star{flex:0 0 44px;min-width:44px;height:44px;padding:0;border:0;
  background:none;cursor:pointer;
  font-size:24px;line-height:1;color:var(--stone-3);
  transition:color .12s ease,transform .12s ease;}
.market-star:hover{color:var(--brass-lo);transform:scale(1.08);}
.market-star.on{color:var(--brass-lo);}
.market-star:focus-visible{outline:2px solid var(--accent);outline-offset:2px;
  border-radius:6px;}

.market-rate-desc{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  font-size:13px;color:var(--ink-2);}
.market-yn{min-height:44px;padding:5px 18px;border:1px solid var(--stone-3);
  border-radius:999px;background:var(--paper);color:var(--ink-2);cursor:pointer;
  font-size:12px;font-weight:600;transition:all .12s ease;}
.market-yn:hover{border-color:var(--ink-3);}
.market-yn.on{background:var(--ink);border-color:var(--ink);color:var(--ink-on-dark);}
.market-yn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}

.market-rate-review{width:100%;resize:vertical;font:inherit;font-size:13px;
  padding:8px 10px;border:1px solid var(--stone-3);border-radius:8px;
  background:var(--paper);color:var(--ink);}
/* align-self:flex-start in a column flex lets the button collapse to its text, which
   came out at 34px wide. Sized explicitly instead. */
.market-rate-send{align-self:flex-start;min-height:44px;min-width:150px;}
.market-rate-blind{margin:0;font-size:11px;max-width:46ch;}
.market-rated{margin:10px 0 0;color:var(--brass-lo);letter-spacing:.5px;}

@media (max-width:560px){
  /* Left to wrap on its own the row breaks between Yes and No, stranding Yes beside
     the question and dropping No onto its own line -- which reads as two unrelated
     controls. The question takes the full line instead, so the pair stays a pair. */
  .market-rate-desc > span{flex:1 0 100%;}
  .market-rate-desc{gap:8px;}
  .market-yn{flex:1;}
  .market-rate-send{align-self:stretch;width:100%;}
}
.stars-half{position:relative;display:inline-block;color:var(--stone-3);}
.stars-half::before{content:'★';position:absolute;left:0;top:0;width:50%;
  overflow:hidden;color:var(--brass-lo);}
.listing-seller{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:8px 0 0;}
.listing-seller-desc{font-size:11px;color:var(--ink-3);}
.listing-reviews{list-style:none;padding:0;margin:10px 0 0;display:flex;
  flex-direction:column;gap:6px;font-size:12px;color:var(--ink-2);}
.listing-reviews li{line-height:1.45;}

/* --- Evidence photos -------------------------------------------------------------
   One step at a time. Somebody doing this is holding a parcel in one hand, so the
   whole thing has to work with a thumb: one big target, one instruction, no form. */

.market-photos{margin-top:14px;padding-top:14px;border-top:1px solid var(--stone-3);
  display:flex;flex-direction:column;gap:10px;}
.market-photos-progress{display:flex;align-items:center;gap:6px;}
.market-photos-dot{width:7px;height:7px;border-radius:50%;background:var(--stone-3);}
.market-photos-dot.on{background:var(--brass-lo);}
.market-photos-progress .sub{margin-left:4px;}

.market-seal-code{margin:0;font-size:13px;line-height:1.5;color:var(--ink-2);
  background:var(--stone);border-radius:8px;padding:10px 12px;}
.market-seal-code strong{font-family:var(--mono,ui-monospace,monospace);
  font-size:16px;letter-spacing:2px;color:var(--ink);}

/* The one instruction people will skip, so it does not look like the rest. */
.market-photos-warn{margin:0;font-size:13px;line-height:1.5;color:var(--ink);
  border-left:3px solid var(--brass-lo);padding:2px 0 2px 12px;}

/* This is a <label> so the whole box triggers the file input, which means it inherits
   the global label style -- 10.5px uppercase with wide tracking, meant for form field
   captions. Left alone it renders the instruction as a section heading, and an action
   that looks like a heading does not get tapped. Every inherited property is undone
   deliberately rather than by accident. */
.market-photo-take{display:flex;align-items:center;justify-content:center;gap:10px;
  min-height:64px;margin:0;padding:14px 20px;border:1.5px dashed var(--stone-3);
  border-radius:10px;cursor:pointer;text-align:center;
  font-size:14px;font-weight:600;text-transform:none;letter-spacing:0;
  color:var(--ink);background:var(--stone);transition:all .14s ease;}
.market-photo-take::before{content:'';flex:0 0 20px;height:20px;opacity:.55;
  background:currentColor;
  -webkit-mask:var(--cam) center/contain no-repeat;mask:var(--cam) center/contain no-repeat;}
:root{--cam:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z'/%3E%3Ccircle cx='12' cy='13' r='4'/%3E%3C/svg%3E");}
.market-photo-take:hover{border-color:var(--brass-lo);background:var(--stone-2);}
.market-photo-take input{position:absolute;width:1px;height:1px;opacity:0;}
.market-photo-take:focus-within{outline:2px solid var(--accent);outline-offset:2px;}
.market-photo-take.busy{opacity:.55;pointer-events:none;}
.market-photo-take.busy span::after{content:' — uploading';}

.market-photos-err{margin:0;font-size:13px;color:var(--danger,#B4232B);}
.market-photos-done{margin:10px 0 0;color:var(--brass-lo);}
.market-report{margin-top:10px;font-size:12px;}
.market-disputed{margin:10px 0 0;font-size:12px;color:var(--ink-2);
  border-left:3px solid var(--stone-3);padding-left:10px;}

.market-card-seal{font-size:10px;font-weight:700;letter-spacing:.6px;
  text-transform:uppercase;color:var(--brass-lo);}
.listing-seal{margin:10px 0 0;font-size:12px;line-height:1.5;}
.listing-seal.is-sealed{color:var(--ink-2);}
.listing-seal.is-open{color:var(--ink-3);}

/* --- Ask the rulebook ------------------------------------------------------------
   Sits under a hand-written summary, so it has to read as a different kind of thing:
   the summary is ours, these are quotes from the book with a page number attached. */

.rule-ask{margin-top:20px;padding-top:18px;border-top:1px solid var(--stone-3);}
.rule-ask-label{display:block;margin:0 0 8px;}
.rule-ask-row{display:flex;gap:8px;align-items:stretch;}
.rule-ask-row input{flex:1;min-width:0;min-height:44px;font:inherit;font-size:14px;
  padding:10px 14px;border:1px solid var(--stone-3);border-radius:9px;
  background:var(--paper);color:var(--ink);}
.rule-ask-row input:focus{outline:2px solid var(--accent);outline-offset:1px;
  border-color:transparent;}
.rule-ask-go{min-height:44px;white-space:nowrap;}

.rule-hits{list-style:none;margin:16px 0 0;padding:0;display:flex;
  flex-direction:column;gap:14px;counter-reset:hit;}
/* A quote, marked as one. The vertical rule does the work a quotation mark would,
   without competing with the apostrophes inside the rules text itself. */
.rule-hits li{border-left:2px solid var(--brass-lo);padding-left:14px;}
.rule-hit-text{margin:0;font-size:14px;line-height:1.6;color:var(--ink);}
.rule-hit-cite{margin:5px 0 0;font-size:11px;letter-spacing:.04em;color:var(--ink-3);
  font-variant-numeric:tabular-nums;}
.rule-ask-foot{margin:14px 0 0;font-size:11px;color:var(--ink-3);font-style:italic;}

@media (max-width:560px){
  .rule-ask-row{flex-wrap:wrap;}
  .rule-ask-go{width:100%;}
}

/* --- Scan accuracy panel (admin) --------------------------------------------------
   An instrument, not a dashboard. Four numbers big enough to read at a glance, and
   the failures listed underneath where they can be worked from. */

.sm-figures{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));
  gap:16px;margin:16px 0 4px;}
.sm-figure{display:flex;flex-direction:column;gap:3px;}
.sm-num{font-size:26px;font-weight:700;line-height:1.05;
  font-variant-numeric:tabular-nums;color:var(--ink);}
/* Coloured against the 90% shipping bar, not against nothing. */
.sm-num.good{color:#1F7A5A;} .sm-num.ok{color:#8A6D1F;} .sm-num.poor{color:#B4232B;}
.sm-lab{font-size:11px;color:var(--ink-3);line-height:1.35;}
.sm-h{margin:24px 0 6px;font-size:13px;}
.sm-thin{font-size:11px;margin-top:8px;}
.sm-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:13px;}
.sm-table th{text-align:left;font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);font-weight:700;padding:0 10px 6px 0;border-bottom:1px solid var(--stone-3);}
.sm-table td{padding:7px 10px 7px 0;border-bottom:1px solid var(--stone-2);
  color:var(--ink-2);font-variant-numeric:tabular-nums;}
.sm-table tr:last-child td{border-bottom:0;}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .sm-num.good{color:#6FD6AE;}
  :root:not([data-theme="light"]) .sm-num.ok{color:#E0BE63;}
  :root:not([data-theme="light"]) .sm-num.poor{color:#F08C8C;}
}
:root[data-theme="dark"] .sm-num.good{color:#6FD6AE;}
:root[data-theme="dark"] .sm-num.ok{color:#E0BE63;}
:root[data-theme="dark"] .sm-num.poor{color:#F08C8C;}
