/* ═══════════════════════════════════════════════════════════════════════════
   Riftbound @ Bunker TCG — design system
   ───────────────────────────────────────────────────────────────────────────
   Dark by default, and not as a style choice: card art IS the content here,
   and 1,966 pieces of saturated Riot key art read as a jumble on white. The
   page is a gallery wall, so it is painted the colour of a gallery wall.

   🔴 NO ABSOLUTE URLS IN THIS FILE either. The font url()s below are RELATIVE
   to this stylesheet, so they resolve under /riftbound/static/ wherever the
   app is mounted. A leading "/" would leave the mount — the same trapdoor the
   templates are grepped for. test_riftshop.py §3 greps this file too.

   Layer order, top to bottom:
     1. Fonts        — self-hosted, no third-party request
     2. Tokens       — colour, space, radius, shadow, motion
     3. Reset + base
     4. Primitives   — buttons, fields, chips, panels, skeletons
     5. Chrome       — topbar, mobile tab bar, footer
     6. Pages        — landing, catalog, card, cart, portfolio, decks, desk
     7. Telegram Mini App overrides
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Fonts ─────────────────────────────────────────────────────────────
   Self-hosted rather than linked to fonts.gstatic.com, for the same reason
   the card-art mirror is self-hosted: a shop must not go blank-faced because
   someone else's CDN is having a day. It is also the only way the Telegram
   Mini App gets text on the first frame — inside Telegram's webview a
   third-party font request is one more round trip before anything is legible.
   Latin + latin-ext only; 171 KB for all four files. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/inter-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;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/inter-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: "Sora";
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url("fonts/sora-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;
}
@font-face {
  font-family: "Sora";
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url("fonts/sora-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;
}

/* ── 2. Tokens ───────────────────────────────────────────────────────────── */

:root {
  /* Surfaces. Four steps, not two: a panel inside a panel needs to separate
     without a border doing all the work. */
  --bg:        #070910;
  --bg-2:      #0c1018;
  --bg-raise:  #111724;
  --bg-hover:  #19202f;
  --bg-sunk:   #05070c;

  /* Hairlines. The faint one is for dividers INSIDE a panel, where a full
     --line reads as a second panel edge. */
  --line:      #212a3b;
  --line-soft: rgba(255, 255, 255, .06);
  --line-lit:  #37455f;

  --text:      #eef1f7;
  --text-mid:  #b3bccf;
  --text-dim:  #7d879d;
  --text-faint:#5b6479;

  /* Brand. Gold is the Bunker's mark (the ◈), so it owns primary actions and
     nothing else — a gold that also means "warning" means nothing at all. */
  --gold:      #e8bd5b;
  --gold-deep: #c9993a;
  --gold-glow: rgba(232, 189, 91, .22);

  --accent:     #6aa5ff;
  --accent-dim: rgba(106, 165, 255, .14);

  --good:      #43c98d;
  --good-dim:  rgba(67, 201, 141, .13);
  --bad:       #f0695e;
  --bad-dim:   rgba(240, 105, 94, .13);
  --warn:      #e8bd5b;

  /* Rarity. The catalogue's only native taxonomy — there is no domain/colour
     column in the data — so it carries the visual coding instead. Kept low
     saturation: these tint a 1px edge and a 10px chip, not a whole tile. */
  --r-common:    #8b95ab;
  --r-uncommon:  #56b9a8;
  --r-rare:      #6aa5ff;
  --r-epic:      #b07ce8;
  --r-legendary: #e8bd5b;

  --radius-sm: 8px;
  --radius:    13px;
  --radius-lg: 18px;
  --radius-xl: 24px;

  /* Ambient, not drop: a black shadow on a near-black page is invisible, so
     these carry a cool tint and a wide spread to read as depth. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow:    0 4px 16px -4px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-lg: 0 18px 42px -12px rgba(0, 0, 0, .7), 0 2px 6px rgba(0, 0, 0, .4);
  --shadow-gold: 0 8px 26px -8px rgba(232, 189, 91, .35);

  --ease: cubic-bezier(.22, .61, .36, 1);
  --fast: .14s var(--ease);
  --med:  .24s var(--ease);

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;
  --font-display: "Sora", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  --topbar-h: 58px;
  --tabbar-h: 0px;           /* becomes real on phones; see §5 */
}

/* ── 3. Reset + base ─────────────────────────────────────────────────────── */

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* 🔴 The single place sideways scroll is prevented, so no individual
     component has to clip itself to stay safe. `clip`, never `hidden`:
     `overflow-x: hidden` on the root element silently makes the whole page a
     scroll container and `position: sticky` stops working everywhere — which
     would take the top bar, the filter row and the card-detail art with it.
     `clip` has no such side effect. */
  overflow-x: clip;
}

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Inter's contextual alternates + tabular figures. Prices sit in columns on
     every page in this app; proportional digits make them jitter. */
  font-feature-settings: "cv05" 1, "cv11" 1, "ss03" 1;
}

body {
  /* The page has a light source. Two very wide, very faint radials at the top
     stop a #070910 background from reading as an unpainted div — which is
     most of what "vibe coded" looks like from across the room. */
  background-image:
    radial-gradient(120% 70% at 12% -8%, rgba(106, 165, 255, .09), transparent 58%),
    radial-gradient(90% 60% at 92% -4%, rgba(232, 189, 91, .07), transparent 55%);
  background-repeat: no-repeat;
  background-attachment: fixed;
  padding-bottom: var(--tabbar-h);

  /* Sticky footer. Every short page in this app — an empty cart, the account
     summary, the desk with no orders, a 404 — put the footer's top border
     halfway down a 900px screen with a black void beneath it, which reads as
     the page having failed to finish loading. Pinning the footer to the
     bottom of the viewport is the difference between "short page" and
     "broken page". */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* `flex: 1 0 auto`, not `flex: 1`: `flex: 1` sets flex-basis to 0%, which
   makes a long catalogue page compute its height from the flex line rather
   than its content and lets the grid overflow the footer.

   🔴 `width: 100%` IS NOT REDUNDANT. `.wrap` carries `margin: 0 auto`, and an
   auto margin on a flex item's CROSS axis overrides `align-self: stretch` —
   the item is shrink-to-fit and centred instead of filling the row. Making
   body a flex column therefore silently collapsed every page whose content
   was narrower than its wrap: /decks rendered 643px wide inside a 1280px
   container and /login 576px inside 860px, while the catalogue looked
   perfect because a 1,966-card grid is wide enough to hide it. A layout bug
   that only appears on the pages with little content is one you fix twice. */
body > main { flex: 1 0 auto; width: 100%; }
.footer { margin-top: auto; }

h1, h2, h3, .font-display {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.15;
}

a { color: inherit; text-decoration: none; }

.inline-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: rgba(106, 165, 255, .32);
  text-underline-offset: 3px;
  transition: text-decoration-color var(--fast), color var(--fast);
}
.inline-link:hover { text-decoration-color: var(--accent); }

/* One focus ring for the whole app, and only for keyboards. Mouse users got a
   blue box on every click before; keyboard users got nothing they could see. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

::selection { background: rgba(232, 189, 91, .28); }

/* Numbers that must line up in a column. */
.tnum, .price, .big-price, .buy-price, .dl-price, .oi-total, .oi-price,
.pf-value, .cart-price, .total, .ask, .acct-card .v, .deckstats .v, .mv-pct {
  font-variant-numeric: tabular-nums;
}

.wrap {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 clamp(16px, 3vw, 28px) 80px;
}
.wrap.narrow { max-width: 860px; }
.wrap.wide   { max-width: 1440px; }

/* Screen-reader-only, for labels that a sighted user reads from context. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── 4. Primitives ───────────────────────────────────────────────────────── */

/* Panel — the single raised surface. Every box in this app is one of these,
   which is most of why the pages now look related to each other. The inset
   highlight on the top edge is what makes it read as lit from above rather
   than as a lighter rectangle. */
.panel {
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .04);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 17px;
  min-height: 42px;          /* a real tap target on a phone */
  border: 1px solid var(--line-lit);
  border-radius: 10px;
  background: linear-gradient(180deg, var(--bg-hover), var(--bg-raise));
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--fast), border-color var(--fast),
              transform var(--fast), box-shadow var(--fast);
  box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .05);
}
.btn:hover { border-color: #4b5c7c; transform: translateY(-1px); box-shadow: var(--shadow); }
.btn:active { transform: translateY(0); }

.btn-primary {
  background: linear-gradient(180deg, var(--gold), var(--gold-deep));
  border-color: var(--gold-deep);
  color: #1a1305;
  box-shadow: var(--shadow-gold), inset 0 1px 0 rgba(255, 255, 255, .3);
}
.btn-primary:hover {
  border-color: var(--gold);
  box-shadow: 0 10px 30px -8px rgba(232, 189, 91, .5),
              inset 0 1px 0 rgba(255, 255, 255, .35);
}

.btn-ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--text-mid);
  box-shadow: none;
}
.btn-ghost:hover { background: var(--bg-raise); color: var(--text); }

.btn-sm { padding: 6px 12px; min-height: 34px; font-size: 13px; border-radius: 8px; }
.btn-lg { padding: 13px 28px; min-height: 50px; font-size: 16px; border-radius: 12px; }
.btn[disabled], .btn-primary[disabled] { opacity: .42; pointer-events: none; }
button.btn { font: inherit; font-weight: 600; cursor: pointer; }
button.btn-lg { font-size: 16px; }

.linkbtn {
  background: none; border: 0; color: var(--text-dim);
  font: inherit; cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px; padding: 0;
  transition: color var(--fast);
}
.linkbtn:hover { color: var(--text); }
.linkbtn.danger:hover { color: var(--bad); }
.danger-form { margin-top: 38px; }
.hidden-form { display: none; }

/* Fields. Native selects get the chrome stripped and one shared chevron, so a
   macOS select and an Android select stop looking like two different sites. */
input[type="search"], input[type="text"], input[type="number"],
select, textarea {
  width: 100%;
  padding: 10px 13px;
  min-height: 42px;
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  transition: border-color var(--fast), box-shadow var(--fast), background var(--fast);
}
input::placeholder, textarea::placeholder { color: var(--text-faint); }
input:hover, select:hover, textarea:hover { border-color: var(--line-lit); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
  background: var(--bg-2);
}
textarea { resize: vertical; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; }

select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 34px;
  background-image:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%237d879d' stroke-width='1.8' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  cursor: pointer;
}
select option { background: var(--bg-raise); color: var(--text); }

code {
  background: var(--bg-hover); padding: 2px 7px; border-radius: 6px;
  font-family: var(--font-mono); font-size: .88em; color: var(--text-mid);
}

/* Chips */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--line-soft);
  font-size: 11.5px;
  font-weight: 550;
  color: var(--text-dim);
  white-space: nowrap;
  letter-spacing: .005em;
}
.chip-gold { background: rgba(232,189,91,.13); border-color: rgba(232,189,91,.3); color: var(--gold); }
.chip-good { background: var(--good-dim); border-color: rgba(67,201,141,.34); color: var(--good); }
.chip-bad  { background: var(--bad-dim);  border-color: rgba(240,105,94,.34);  color: var(--bad); }

.pill {
  display: inline-block;
  background: var(--gold); color: #1a1305; border-radius: 999px;
  padding: 1px 7px; font-size: 11px; font-weight: 750;
  font-variant-numeric: tabular-nums;
}

/* Section heading — the small uppercase rule used above every block. */
.sec-h2, .chart-wrap h2, .others h2, .deskbox h2 {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--text-dim);
  margin: 34px 0 12px;
  display: flex;
  align-items: center;
  gap: 12px;
}
/* The hairline that runs off to the right of a section title. */
.sec-h2::after, .chart-wrap h2::after, .others h2::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line), transparent);
}

.page-h1 {
  margin: 38px 0 6px;
  font-size: clamp(26px, 3.4vw, 33px);
}
.sub { color: var(--text-dim); margin: 0 0 24px; max-width: 62ch; }
.dim { color: var(--text-dim); }
.fine { color: var(--text-dim); font-size: 12.5px; margin: 8px 0 0; line-height: 1.6; }
.warn { color: var(--gold); }
.warn-line { color: var(--bad); font-size: 13px; margin: 6px 0 0; }
.ok-line { color: var(--good); }

/* Banners */
.banner {
  max-width: 1280px;
  margin: 16px auto 0;
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 14px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  box-shadow: var(--shadow-sm);
}
.banner ul { margin: 6px 0 0; padding-left: 18px; }
.banner-ok   { background: var(--good-dim); border: 1px solid rgba(67,201,141,.38); color: #b9f0d6; }
.banner-err  { background: var(--bad-dim);  border: 1px solid rgba(240,105,94,.40); color: #ffd3ce; }
.banner-warn { background: rgba(232,189,91,.11); border: 1px solid rgba(232,189,91,.38); color: #f4e0ae; }

/* Skeleton shimmer, used while the catalogue re-fetches. Honours
   prefers-reduced-motion — a whole grid of sliding gradients is exactly the
   thing that setting exists for. */
@keyframes shimmer { to { background-position: 200% 0; } }
.skeleton {
  background: linear-gradient(90deg, var(--bg-raise) 0%, var(--bg-hover) 50%, var(--bg-raise) 100%);
  background-size: 200% 100%;
  animation: shimmer 1.3s linear infinite;
  border-radius: var(--radius-sm);
}

@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.rise { animation: rise .4s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ── 5. Chrome ───────────────────────────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  height: var(--topbar-h);
  padding: 0 clamp(16px, 3vw, 28px);
  border-bottom: 1px solid var(--line);
  background: rgba(7, 9, 16, .82);
  backdrop-filter: saturate(170%) blur(14px);
  -webkit-backdrop-filter: saturate(170%) blur(14px);
  position: sticky;
  top: 0;
  z-index: 30;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -.02em;
  flex-shrink: 0;
}
.brand-mark {
  display: grid;
  place-items: center;
  width: 28px; height: 28px;
  border-radius: 9px;
  background: linear-gradient(145deg, var(--gold), var(--gold-deep));
  color: #1a1305;
  font-size: 14px;
  box-shadow: 0 2px 10px -2px var(--gold-glow), inset 0 1px 0 rgba(255,255,255,.4);
  flex-shrink: 0;
}
.brand-text em {
  font-style: normal;
  color: var(--text-dim);
  font-weight: 500;
  font-family: var(--font);
  font-size: 13px;
}

/* `white-space: nowrap` is not cosmetic: at 390px the two-word links broke
   mid-label into "In / stock" and "Sign / in", which reads as a layout fault
   on the first screen a phone buyer ever sees. */
.topnav {
  display: flex;
  align-items: center;
  gap: clamp(4px, 1.4vw, 10px);
  font-size: 14px;
  white-space: nowrap;
}
.topnav a {
  color: var(--text-dim);
  padding: 7px 10px;
  border-radius: 8px;
  font-weight: 550;
  transition: color var(--fast), background var(--fast);
}
.topnav a:hover { color: var(--text); background: rgba(255,255,255,.05); }
.topnav a.is-active { color: var(--text); background: rgba(255,255,255,.07); }
.topnav .signin { color: var(--gold); }
.topnav .admin-link { color: var(--gold); }
.topnav .navcart { display: inline-flex; align-items: center; gap: 6px; }

/* The phone cart. Hidden on desktop, where the text "Cart" link is already
   in the nav — two carts in one row is the clutter the tab bar was added to
   remove. */
.topcart { display: none; position: relative; padding: 7px 9px; }
.topcart svg { width: 21px; height: 21px; stroke-width: 1.7; display: block; }
.topcart .pill {
  position: absolute; top: 0; right: 0;
  transform: translate(2px, -1px);
  font-size: 9.5px; line-height: 15px; padding: 0 5px;
}
@media (max-width: 720px) {
  .topcart { display: inline-flex; align-items: center; color: var(--text); }
}

/* Mobile tab bar — the one structural borrowing from Bilgewater, and it earns
   its place: the primary surface here is a phone inside Telegram, where a
   top nav sits under Telegram's own header and above the fold of everything.
   Thumbs are at the bottom. Hidden on desktop, where it would be a phone UI
   pasted onto a 1440px page. */
.tabbar { display: none; }

@media (max-width: 720px) {
  :root { --tabbar-h: calc(58px + env(safe-area-inset-bottom)); }

  .tabbar {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 40;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    background: rgba(7, 9, 16, .93);
    backdrop-filter: saturate(170%) blur(16px);
    -webkit-backdrop-filter: saturate(170%) blur(16px);
    border-top: 1px solid var(--line);
  }
  .tabbar a {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 5px 2px;
    border-radius: 10px;
    color: var(--text-faint);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .01em;
    min-height: 46px;
    transition: color var(--fast), background var(--fast);
  }
  .tabbar a svg { width: 21px; height: 21px; stroke-width: 1.7; }
  .tabbar a.is-active { color: var(--gold); background: rgba(232,189,91,.10); }
  .tabbar .tb-badge {
    position: absolute;
    transform: translate(11px, -9px);
    background: var(--gold); color: #1a1305;
    font-size: 9.5px; font-weight: 800;
    border-radius: 999px; padding: 0 5px; line-height: 15px;
    font-variant-numeric: tabular-nums;
  }

  /* With a tab bar carrying the same destinations, the top nav keeps only what
     the tab bar cannot: identity and the admin desk. Two navs showing the same
     links twice over is the clutter the tab bar was added to remove. */
  .topnav .nav-dup { display: none; }
}

.footer {
  border-top: 1px solid var(--line);
  margin-top: 40px;
  padding: 34px clamp(16px, 3vw, 28px) 52px;
  color: var(--text-faint);
  font-size: 12px;
  background: var(--bg-sunk);
}
.footer-inner { max-width: 1280px; margin: 0 auto; display: grid; gap: 10px; }
.footer p { margin: 0; max-width: 90ch; }
.legal { line-height: 1.65; }

/* The footer's own nav. On a phone this is the only complete set of links in
   the document, so the tap targets are sized like real controls (44px high)
   rather than like the fine print they sit above. `wrap` because four labels
   plus a 16px gutter overflow a 320px screen on one line. */
.footlinks {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 18px;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
}
.footlinks a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-dim);
  transition: color var(--fast);
}
.footlinks a:hover { color: var(--accent); }
.footer .inline-link { color: var(--text-dim); }
.footer .inline-link:hover { color: var(--accent); }

/* The other game. The return leg of bunkertcg.com's front-page chooser: a
   chooser with no way back is a trapdoor. Deliberately the quietest thing on
   the page — a visitor who is here has already chosen, so this is an exit,
   not a pitch. Hidden inside the Mini App, where it would kick the buyer out
   to an external browser mid-session. */
.xgame { margin: 16px 0 0; font-size: 13px; color: var(--text-dim); }
.xgame a { display: inline-block; padding: 4px 0; min-height: 26px; }

/* ── 5b. How buying works ────────────────────────────────────────────────── */

/* A <dl>, not a list of headings: these genuinely are term/definition pairs,
   and the pairing is what a screen reader announces. Each Q&A is boxed so a
   long answer cannot visually run into the next question. */
.faq { margin: 0; display: grid; gap: 12px; }
.faq-item {
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md, 12px);
  padding: 16px 18px;
}
.faq-item dt {
  font-size: 15.5px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.01em;
}
.faq-item dd {
  margin: 7px 0 0;
  max-width: 78ch;
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1.7;
}
.faq-cta { margin-top: 28px; }
.faq-cta h2 { margin: 0 0 6px; }
.faq-cta p { margin: 0 0 14px; color: var(--text-dim); max-width: 70ch; }

/* ── 6a. Landing ─────────────────────────────────────────────────────────── */

/* The phone hero must not eat the whole first screen. A landing page whose
   entire viewport is a headline and two buttons tells a returning buyer
   nothing and costs them a scroll before any card is visible — so the top
   padding collapses hard below 720px and the stat strip is pulled up to sit
   just under the fold. */
.hero {
  position: relative;
  padding: clamp(26px, 7vw, 92px) 0 clamp(24px, 4vw, 52px);
  text-align: center;
  isolation: isolate;
}

/* The atmosphere is FULL-BLEED, and the two rules below are what make it so.
   `.hero` sits inside `.wrap`, which is capped at 1280px — so a pseudo-element
   sized to its parent stopped dead at the content column and drew a visible
   vertical seam down both sides of the page at 1440px. A background with an
   edge is worse than no background: it reads as an image that failed to tile.

   `left: 50%` + `width: 100vw` + a -50% translate escapes the column and
   centres on the VIEWPORT instead. That deliberately overhangs the document,
   which is exactly why `html { overflow-x: clip }` exists above — without it
   this widens the page and the whole site scrolls sideways (it did: 137px at
   1440px, 56px on a 390px phone, invisible until you try to swipe). */
   ⚠️ Both pseudo-elements set left/width/transform THEMSELVES rather than
   inheriting from a shared rule above them — a shared rule placed before
   these is simply overridden by their own `inset`, which is how the first
   attempt at this changed nothing at all. */

/* The atmosphere. A pair of soft coloured pools plus a fine grid, all behind
   the content and all pure CSS — a hero image would be another 300 KB on a
   phone, and the card art below is the page's photography already. */
.hero::before {
  content: "";
  position: absolute;
  top: -20%; bottom: 0;
  left: 50%; width: 100vw; transform: translateX(-50%);
  z-index: -1;
  background:
    radial-gradient(52% 46% at 50% 22%, rgba(106,165,255,.16), transparent 70%),
    radial-gradient(38% 40% at 78% 10%, rgba(232,189,91,.13), transparent 70%),
    radial-gradient(40% 44% at 20% 16%, rgba(176,124,232,.11), transparent 70%);
  filter: blur(6px);
}
.hero::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: 50%; width: 100vw; transform: translateX(-50%);
  z-index: -1;
  background-image:
    linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.035) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(62% 58% at 50% 34%, #000 0%, transparent 78%);
  -webkit-mask-image: radial-gradient(62% 58% at 50% 34%, #000 0%, transparent 78%);
}

.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 20px;
  padding: 5px 13px 5px 8px;
  border-radius: 999px;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--line);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-mid);
  letter-spacing: .01em;
}
.hero-eyebrow .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 3px var(--good-dim);
}

.hero h1 {
  margin: 0 auto 16px;
  font-size: clamp(30px, 5.6vw, 58px);
  font-weight: 800;
  letter-spacing: -.035em;
  max-width: 21ch;
  /* Stops at 88% so the final line is still near-white. At 100% the gradient
     bottomed out mid-word and the last line of the headline read as disabled
     text — the one place on the site where washed-out IS the wrong look. */
  background: linear-gradient(172deg, #ffffff 28%, #b9c4da 88%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero .sub {
  margin: 0 auto;
  max-width: 54ch;
  color: var(--text-mid);
  font-size: clamp(14.5px, 1.5vw, 17.5px);
}
.hero .xgame { text-align: center; }

@media (max-width: 720px) {
  .hero-eyebrow { margin-bottom: 14px; font-size: 11.5px; padding: 4px 11px 4px 7px; }
  .hero h1 { max-width: 16ch; }
  .herosearch { margin-top: 20px; }
  /* Two buttons stacked full-width is a form, not a choice. Side by side they
     read as the two things you can do next, which is what they are. */
  .hero-cta { gap: 9px; }
  .hero-cta .btn { flex: 1 1 0; min-width: 0; padding: 11px 10px; font-size: 13.5px; }
  .statstrip { margin-top: 26px; grid-template-columns: repeat(2, 1fr); }
  .statstrip div { padding: 15px 16px; }
  /* Two-line labels made neighbouring cells different heights in a 2-up grid,
     which reads as a misalignment rather than as two facts. */
  .statstrip .k { font-size: 10px; letter-spacing: .08em; }
  .statstrip .v { font-size: 20px; }
  /* The fx date is reference detail. On one line it fits; wrapped onto a
     second it made "2026-09-" / "25" and turned a number into a mess. */
  .statstrip .v small { display: block; font-size: 11px; margin-top: 1px; }
}

/* Below 430px the Search button's label costs ~70px, and the placeholder —
   the thing that tells a first-time visitor what to type — was being cut off
   mid-example to pay for it. The magnifier already says "search". */
@media (max-width: 430px) {
  .herosearch { padding-left: 13px; }
  .herosearch .btn { padding: 10px 14px; font-size: 0; }
  .herosearch .btn::after { content: "→"; font-size: 17px; line-height: 1; }
}

/* The hero search. The single most-used control on a card site, so it is the
   biggest thing under the headline rather than one field among five in a grey
   box — which is what it was, and what made the page read as a form. */
.herosearch {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 580px;
  margin: 28px auto 0;
  padding: 6px 6px 6px 16px;
  background: var(--bg-2);
  border: 1px solid var(--line-lit);
  border-radius: 999px;
  box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,.05);
  transition: border-color var(--fast), box-shadow var(--fast);
}
.herosearch:focus-within {
  border-color: var(--gold);
  box-shadow: var(--shadow-lg), 0 0 0 4px var(--gold-glow);
}
.herosearch svg { width: 18px; height: 18px; color: var(--text-dim); flex-shrink: 0; }
.herosearch input {
  flex: 1;
  border: 0;
  background: none;
  box-shadow: none;
  padding: 10px 2px;
  min-height: 40px;
  font-size: 15.5px;
}
.herosearch input:hover, .herosearch input:focus {
  border: 0; background: none; box-shadow: none;
}
.herosearch .btn { border-radius: 999px; padding: 10px 22px; }

.hero-cta { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; margin-top: 22px; }

/* Search suggestions. Absolutely positioned against the hero section (which
   is `position: relative`) so it floats over the stat strip instead of
   shoving the page down on every keystroke. */
.suggest {
  position: absolute;
  z-index: 25;
  left: 50%;
  transform: translateX(-50%);
  width: min(580px, calc(100% - 32px));
  margin-top: 8px;
  padding: 6px;
  text-align: left;
  background: rgba(12, 16, 24, .97);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--line-lit);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  max-height: min(60vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.suggest-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 10px;
  border-radius: 10px;
  transition: background var(--fast);
}
.suggest-row:hover, .suggest-row.is-cursor { background: rgba(255,255,255,.07); }
.suggest-row img, .suggest-noart {
  width: 34px; height: 48px; flex-shrink: 0;
  border-radius: 5px; object-fit: cover;
  background: var(--bg-sunk);
  border: 1px solid var(--line-soft);
}
.suggest-body { flex: 1; min-width: 0; display: grid; }
.suggest-name {
  font-size: 13.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Clamped like the name above it. Promo sub-variants carry labels as long as
   "Origins · Promo · Origins Launch Event", which wrapped onto a second line
   and made one row in seven twice the height of the others. */
.suggest-meta {
  font-size: 11.5px; color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.suggest-price {
  font-size: 13px; font-weight: 650; flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.suggest-all {
  display: block;
  padding: 10px;
  margin-top: 4px;
  border-top: 1px solid var(--line-soft);
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  border-radius: 10px;
  transition: background var(--fast);
}
.suggest-all:hover, .suggest-all.is-cursor { background: rgba(255,255,255,.07); }
.suggest-none { margin: 0; padding: 16px 10px; text-align: center; color: var(--text-dim); font-size: 13.5px; }

/* Stat strip */
.statstrip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1px;
  margin: 40px 0 8px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.statstrip div {
  padding: 20px 22px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  display: grid;
  gap: 4px;
}
.statstrip .k {
  font-size: 11px; text-transform: uppercase; letter-spacing: .11em;
  color: var(--text-dim); font-weight: 700;
}
.statstrip .v {
  font-family: var(--font-display);
  font-size: clamp(22px, 2.6vw, 28px);
  font-weight: 700;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.statstrip .v small { font-size: 13px; font-weight: 500; color: var(--text-dim); letter-spacing: 0; }

/* Section header with a "see all" on the right */
.sec-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin: 46px 0 16px;
  flex-wrap: wrap;
}
.sec-head h2 {
  margin: 0;
  font-size: clamp(19px, 2.2vw, 24px);
  font-weight: 700;
}
.sec-head .sec-note { color: var(--text-dim); font-size: 13px; margin: 4px 0 0; flex-basis: 100%; }

/* Movers */
.movers {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
}
.movers a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px 10px 10px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--fast), transform var(--fast), background var(--fast);
}
.movers a:hover { border-color: var(--line-lit); transform: translateY(-2px); }
.mv-art {
  width: 40px; height: 56px; border-radius: 6px; flex-shrink: 0;
  object-fit: cover; background: var(--bg-sunk);
  border: 1px solid var(--line-soft);
}
.mv-art.mv-art--none { display: grid; place-items: center; font-size: 9px; color: var(--text-faint); }
.mv-body { flex: 1; min-width: 0; }
.mv-name {
  font-size: 13.5px; font-weight: 600; margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mv-meta { font-size: 11.5px; color: var(--text-dim); margin: 2px 0 0; }
.mv-right { text-align: right; flex-shrink: 0; }
.mv-price { font-size: 13.5px; font-weight: 650; font-variant-numeric: tabular-nums; }
.mv-pct { font-size: 12px; font-weight: 700; margin-top: 2px; }
.mv-pct.up   { color: var(--good); }
.mv-pct.down { color: var(--bad); }
.mv-pct.flat { color: var(--text-faint); }

/* Set browser */
.setgrid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px;
}
.setgrid a {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 15px 17px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--fast), transform var(--fast);
}
.setgrid a:hover { border-color: var(--gold-deep); transform: translateY(-2px); }
.setgrid .s-name { font-weight: 650; font-size: 14.5px; }
.setgrid .s-n { color: var(--text-dim); font-size: 12.5px; font-variant-numeric: tabular-nums; }

/* How it works */
.steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px;
  counter-reset: step;
}
.steps li {
  padding: 22px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  counter-increment: step;
}
.steps li::before {
  content: counter(step);
  display: grid; place-items: center;
  width: 30px; height: 30px; margin-bottom: 12px;
  border-radius: 9px;
  background: var(--accent-dim);
  border: 1px solid rgba(106,165,255,.3);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 700; font-size: 14px;
}
.steps h3 { margin: 0 0 6px; font-size: 15.5px; font-weight: 650; letter-spacing: -.01em; }
.steps p { margin: 0; color: var(--text-dim); font-size: 13.5px; line-height: 1.6; }

/* ── 6b. Catalog ─────────────────────────────────────────────────────────── */

.catalog-head { padding: 34px 0 4px; }
.catalog-head h1 { margin: 0 0 6px; font-size: clamp(25px, 3.2vw, 34px); }

.filters {
  display: grid;
  /* Six controls + the switch, sized per column rather than with one shared
     `repeat()`. They do not hold equal-length text: adding the price pair
     squeezed the shared track until the sort select read "Price, low to
     hig" — a truncation that reads as a rendering fault, in the one control
     whose entire job is to be read. Sort gets its own wider track; the price
     pair a narrow one, because two short numbers need less room than a
     sentence. */
  grid-template-columns:
    minmax(170px, 1.4fr)             /* search                */
    repeat(3, minmax(104px, .95fr))  /* set, rarity, printing */
    minmax(158px, 1.15fr)            /* sort - longest label  */
    minmax(126px, .8fr)              /* price range           */
    auto;                            /* in-stock switch       */
  gap: 9px;
  align-items: center;
  padding: 12px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  margin: 18px 0 16px;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,.04);
  position: sticky;
  top: calc(var(--topbar-h) + 10px);
  z-index: 15;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
/* `display: contents` lets the wrapper exist for the phone toggle while its
   children still participate in the form's grid on desktop — so the collapse
   feature costs the desktop layout nothing at all. */
.filter-fields { display: contents; }
.filter-toggle { display: none; }

@media (max-width: 900px) {
  .filters {
    grid-template-columns: 1fr 1fr;
    position: static;
    gap: 9px;
  }
  .filter-search { grid-column: 1 / -1; }
  .filters .check { grid-column: 1 / -1; }

  .filter-toggle {
    grid-column: 1 / -1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 42px;
    padding: 9px 14px;
    background: var(--bg-sunk);
    border: 1px solid var(--line);
    border-radius: 10px;
    color: var(--text-mid);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: border-color var(--fast), color var(--fast);
  }
  .filter-toggle:hover { border-color: var(--line-lit); color: var(--text); }
  .filter-toggle svg { width: 16px; height: 16px; }
  .filter-toggle .pill { margin-left: 2px; }

  /* Collapsed by default, and `display: none` rather than height:0 so the
     selects are genuinely out of the tab order while hidden — a keyboard
     user tabbing into an invisible dropdown is worse than no toggle. */
  .filter-fields { display: none; }
  .filters.is-open .filter-fields { display: contents; }
  .filters.is-open .filter-toggle { border-color: var(--accent); color: var(--text); }
}

/* 🔴 With JS off the toggle cannot do anything, so the fields must never be
   the thing it hides. This puts them back unconditionally. */
@media (max-width: 900px) {
  .no-js .filter-fields { display: contents !important; }
  .no-js .filter-toggle { display: none !important; }
}

.filter-search { position: relative; }
.filter-search input[type="search"] { padding-left: 38px; }
.filter-search svg {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-faint); pointer-events: none;
}

/* A switch, not a checkbox. The native control is 13px, unstyleable across
   browsers, and was the only unpolished pixel left in the filter row. */
.check {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  font-weight: 550;
  color: var(--text-mid);
  cursor: pointer;
  user-select: none;
  padding: 0 4px;
  white-space: nowrap;
}
.check input {
  appearance: none; -webkit-appearance: none;
  width: 38px; height: 22px; min-height: 0; padding: 0;
  border-radius: 999px;
  background: var(--bg-sunk);
  border: 1px solid var(--line-lit);
  position: relative;
  cursor: pointer;
  transition: background var(--fast), border-color var(--fast);
  flex-shrink: 0;
}
.check input::after {
  content: "";
  position: absolute; top: 3px; left: 3px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--text-dim);
  transition: transform var(--fast), background var(--fast);
}
.check input:checked { background: var(--good-dim); border-color: var(--good); }
.check input:checked::after { transform: translateX(16px); background: var(--good); }

/* Active filter chips — the answer to "why am I seeing 12 results". Each one
   removes itself; the whole set clears. */
.activefilters { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin: 0 0 14px; }
.activefilters .fchip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 6px 4px 11px;
  border-radius: 999px;
  background: var(--accent-dim);
  border: 1px solid rgba(106,165,255,.3);
  color: #c6dbff;
  font-size: 12.5px; font-weight: 550;
}
.activefilters .fchip .x {
  display: grid; place-items: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: rgba(255,255,255,.08); color: #c6dbff;
  font-size: 12px; line-height: 1;
  transition: background var(--fast);
}
.activefilters .fchip .x:hover { background: rgba(255,255,255,.2); }

.resultbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin: 0 0 16px; flex-wrap: wrap;
}
.count { color: var(--text-dim); font-size: 13px; margin: 0; }
.count strong { color: var(--text); font-weight: 650; font-variant-numeric: tabular-nums; }

/* The grid. 2 columns on a phone, not 1: a single 350px-tall tile per row
   meant three cards per screen and a scroll that never ended, on the surface
   that matters most. */
.grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: clamp(10px, 1.4vw, 18px);
}
@media (max-width: 560px) {
  .grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
}

/* A landing strip is a FIXED number of columns, never auto-fill. The landing
   page shows exactly 6 tiles, and auto-fill gave 5 across at 1280px — leaving
   a single orphan tile on its own row, which is the most conspicuous way a
   page can look unfinished. Every breakpoint below divides 6. */
.grid--strip { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 1180px) { .grid--strip { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px)  { .grid--strip { grid-template-columns: repeat(2, 1fr); } }

.card {
  position: relative;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform var(--med), border-color var(--med), box-shadow var(--med);
}
.card:hover {
  transform: translateY(-4px);
  border-color: var(--line-lit);
  box-shadow: var(--shadow-lg);
}
.card:focus-within { border-color: var(--accent); }
.card--stock { border-color: rgba(67, 201, 141, .42); }
.card--stock:hover { border-color: var(--good); box-shadow: 0 18px 42px -12px rgba(67,201,141,.28); }

.art {
  position: relative;
  aspect-ratio: 5 / 7;
  background: var(--bg-sunk);
  overflow: hidden;
}
.art img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .45s var(--ease);
}
.card:hover .art img { transform: scale(1.055); }

/* A floor shadow under the art so the info block below it doesn't read as a
   separate rectangle stuck to a picture. */
.art::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 38%;
  background: linear-gradient(180deg, transparent, rgba(7,9,16,.55));
  pointer-events: none;
}

/* 🔴 THE MISSING-ART BOX IS A SHOPFRONT, NOT AN ERROR STATE. It started as an
   invisible ~500px void (no background, no border) and was then a grey hatched
   panel stamped "NO IMAGE" — Adian's verdict on seeing that grid was "why are
   there empty pictures, WE CANT HAVE THAT", and he was right: a wall of NO
   IMAGE on a page asking S$1,900 for a card reads as a half-built site.
   The art itself was fixed at source (riftcomp/sources.py now matches promo
   printings across set prefixes, and mirrors what Riftcodex lacks), which took
   this from 141 printings to 28 — every one of those 28 a tournament/signing
   promo whose art Riot has never published anywhere.
   So this must look DELIBERATE: a card-shaped plate carrying the card's own
   name and code, captioned as unreleased art rather than as a failure. */
.art-missing {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 9px;
  height: 100%;
  color: var(--text);
  text-align: center;
  padding: 16px;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(106, 165, 255, .16), transparent 62%),
    linear-gradient(180deg, var(--bg-raise), var(--bg-sunk));
  border-radius: var(--radius);
  position: relative;
}
/* A faint card-frame inside the plate, so an artless printing still reads as
   a card rather than as a caption on a box. */
.art-missing::before {
  content: "";
  position: absolute;
  inset: 10px;
  border: 1px solid rgba(255,255,255,.07);
  border-radius: calc(var(--radius) - 5px);
  pointer-events: none;
}
.art-missing__name {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 650;
  line-height: 1.3;
  /* Long champion names must not spill out of a 5:7 plate on a phone. */
  overflow-wrap: anywhere;
}
.art-missing__code {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}
.art-missing::after {
  content: "artwork not released";
  font-size: 8.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.art-missing.big .art-missing__name { font-size: 18px; }
.art-missing.big .art-missing__code { font-size: 12px; }
.art-missing.big {
  /* Capped, not stretched to the column: an empty 5:7 box at full phone width
     pushed the name, the price and the buy control below the fold. */
  aspect-ratio: 5 / 7;
  max-height: 420px;
  margin: 0 auto;
  font-size: 14px;
  border: 1px solid var(--line);
}

.badge {
  position: absolute;
  top: 9px; left: 9px;
  background: rgba(10, 30, 21, .82);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(67, 201, 141, .5);
  color: var(--good);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .03em;
  padding: 3px 9px;
  border-radius: 999px;
  z-index: 2;
}
.badge.sm { position: static; margin-left: 8px; backdrop-filter: none; }

/* Rarity rail — a 3px bar down the left edge of the art. It is the catalogue's
   only at-a-glance taxonomy; a legendary and a common were otherwise
   identical rectangles. */
.rarity-rail {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 3px;
  z-index: 2;
  background: var(--r-common);
}
.r-uncommon  .rarity-rail { background: var(--r-uncommon); }
.r-rare      .rarity-rail { background: var(--r-rare); }
.r-epic      .rarity-rail { background: var(--r-epic); }
.r-legendary .rarity-rail { background: linear-gradient(180deg, var(--r-legendary), var(--gold-deep)); }

.info { padding: 11px 12px 13px; }
.info h2 {
  margin: 0 0 7px;
  font-family: var(--font);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.005em;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  min-height: 2.6em;
}

.tags {
  margin: 0 0 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  font-size: 11px;
  color: var(--text-dim);
}
.tags span {
  background: rgba(255,255,255,.05);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  padding: 2px 7px;
  white-space: nowrap;
}
.tag-hot {
  background: rgba(232, 189, 91, .14) !important;
  border-color: rgba(232, 189, 91, .3) !important;
  color: var(--gold);
  font-weight: 600;
}

.price { margin: 0; font-size: 14px; display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.price strong { font-weight: 700; font-size: 15px; letter-spacing: -.01em; }
.price .usd { color: var(--text-faint); font-size: 11.5px; }
.nodata { color: var(--text-faint); font-size: 13px; font-weight: 500; }

.ask {
  margin: 7px 0 0;
  font-size: 13px;
  color: var(--good);
  font-weight: 650;
  display: flex; align-items: center; gap: 6px;
}
.ask::before {
  content: "";
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 3px var(--good-dim);
}

.empty {
  color: var(--text-dim);
  padding: 64px 24px;
  text-align: center;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px dashed var(--line-lit);
  border-radius: var(--radius-lg);
}
.empty strong { color: var(--text); font-size: 16px; font-family: var(--font-display); }
.empty .fine { max-width: 54ch; margin: 10px auto 0; }
.empty-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; justify-content: center; }

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 40px;
}
.pageno { color: var(--text-dim); font-size: 13px; font-variant-numeric: tabular-nums; }

/* ── 6c. Card detail ─────────────────────────────────────────────────────── */

.crumbs { padding: 26px 0 14px; color: var(--text-dim); font-size: 13.5px; }
.crumbs a { transition: color var(--fast); }
.crumbs a:hover { color: var(--text); }

.detail { display: grid; grid-template-columns: minmax(280px, 380px) 1fr; gap: clamp(24px, 4vw, 52px); align-items: start; }
@media (max-width: 860px) { .detail { grid-template-columns: 1fr; gap: 24px; } }

/* The art follows you down a long page of history and printings, instead of
   scrolling away and leaving a wall of numbers with no subject. */
.detail-art { position: sticky; top: calc(var(--topbar-h) + 22px); }
@media (max-width: 860px) { .detail-art { position: static; } }

.detail-art img {
  width: 100%;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  display: block;
  box-shadow: var(--shadow-lg);
}
.detail-art .art-missing.big { max-height: none; }

.detail-body h1 {
  margin: 0 0 6px;
  font-size: clamp(24px, 3.6vw, 36px);
  font-weight: 750;
}
.cn-name { margin: 0 0 12px; color: var(--text-dim); font-size: 16px; }
.detail-body .tags { font-size: 12px; margin-bottom: 22px; gap: 6px; }
.detail-body .tags span { padding: 4px 10px; font-size: 12px; }

.pricebox {
  padding: 22px 24px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  margin-bottom: 16px;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,.04);
  position: relative;
  overflow: hidden;
}
.pricebox::before {
  content: "";
  position: absolute; inset: 0 0 auto; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-deep), transparent);
  opacity: .5;
}
.pricebox .k {
  font-size: 11px; text-transform: uppercase; letter-spacing: .12em;
  color: var(--text-dim); font-weight: 700; margin: 0 0 6px;
}
.big-price {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(30px, 4.4vw, 42px);
  font-weight: 750;
  letter-spacing: -.03em;
  line-height: 1.05;
}
.pricebox .fine { margin-top: 10px; }
/* Baseline, not centre: the move chip has to sit on the price's baseline or
   it floats in the middle of a 42px number and reads as a separate element. */
.priceline { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.priceline .chip { font-size: 12.5px; padding: 4px 10px; font-weight: 700; }

.stock-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.others-note { margin-bottom: 12px; }

.stockbox {
  padding: 20px 22px;
  background:
    radial-gradient(120% 140% at 0% 0%, rgba(67,201,141,.13), transparent 60%),
    linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid rgba(67, 201, 141, .38);
  border-radius: var(--radius-lg);
  margin-bottom: 24px;
  box-shadow: var(--shadow);
}
.stockbox-muted {
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border-color: var(--line);
}

/* 🔴 THE SOURCING STATE, and on this site it is the DEFAULT one. The catalogue
   tracks every printing in the game and `stock_items` is empty, so for all but
   a handful of pages this box is the entire call to action — and it was
   rendering as `.stockbox-muted`, the styling reserved for "nothing to do
   here". The one control we want pressed was the quietest thing on the page.

   Gold, NOT the green of `.stockbox`: on this site green means "you can buy
   this right now", and dressing a card we do not own in it would be a lie the
   buyer only discovers after tapping. Gold is the brand accent and reads as
   "this is an action" without claiming availability. */
.stockbox-source {
  background:
    radial-gradient(120% 140% at 0% 0%, rgba(232, 189, 91, .12), transparent 60%),
    linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border-color: rgba(232, 189, 91, .34);
}
.stockbox-source .stock-line strong { color: var(--gold); }

/* On a phone the sourcing CTA is the page's purpose; a 42px pill floating in a
   full-width box reads as an afterthought next to it. */
@media (max-width: 560px) {
  .stockbox-source .stock-actions .btn { width: 100%; }
}
.stock-line { margin: 0 0 4px; font-size: 15px; }
.stock-line strong { font-family: var(--font-display); font-weight: 700; }
.stock-ask { margin: 6px 0 0; font-size: 17px; }

.buyrow {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 0; border-top: 1px solid var(--line-soft);
  margin: 0;
}
.buyrow:first-of-type { border-top: 0; padding-top: 8px; }
.buy-cond { flex: 1; min-width: 110px; font-weight: 550; }
.buy-price { font-weight: 700; font-size: 17px; letter-spacing: -.01em; }
.buy-qty { color: var(--text-dim); font-size: 12.5px; }
.inline-form { display: flex; gap: 9px; align-items: center; margin: 16px 0 24px; flex-wrap: wrap; }
.inline-form input[type=number] { width: 78px; }

.chart-wrap { margin: 34px 0; }
.chart-card {
  padding: 18px 20px 12px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,.04);
}
#chart { width: 100%; height: auto; display: block; touch-action: pan-y; }
.chart-empty { margin: 10px 0; }

/* The hover readout. `pointer-events: none` is load-bearing: the tooltip
   tracks the cursor, so without it the tooltip sits under the pointer, eats
   the next mousemove, and the chart flickers between hovered and not. */
.charttip {
  position: absolute;
  pointer-events: none;
  z-index: 3;
  display: grid;
  gap: 1px;
  padding: 7px 11px;
  min-width: 104px;
  text-align: center;
  background: rgba(7, 9, 16, .94);
  border: 1px solid var(--line-lit);
  border-radius: 9px;
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  line-height: 1.35;
}
.charttip b { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
.charttip span { color: var(--text-dim); font-size: 11px; }

.other-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.other-list a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 15px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 14px;
  transition: border-color var(--fast), transform var(--fast);
}
.other-list a:hover { border-color: var(--line-lit); transform: translateX(3px); }
.other-list .ov { flex: 1; font-weight: 550; }
.other-list .op { font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── 6d. Sign-in ─────────────────────────────────────────────────────────── */

.door { padding: 56px 0; max-width: 520px; margin: 0 auto; }
.door h1 { margin: 0 0 10px; font-size: clamp(26px, 3.4vw, 34px); }
.widget-wrap { margin: 26px 0 10px; min-height: 48px; }
.or {
  color: var(--text-faint); margin: 22px 0; font-size: 12px;
  text-transform: uppercase; letter-spacing: .14em; font-weight: 700;
  display: flex; align-items: center; gap: 14px;
}
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.botdoor {
  padding: 22px 24px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.botdoor h2 { margin: 0 0 12px; font-size: 16px; }
.botdoor ol { margin: 0; padding-left: 20px; color: var(--text-mid); line-height: 2; font-size: 14px; }

/* ── 6e. Cart, portfolio, orders ─────────────────────────────────────────── */

.cartlist, .pflist { list-style: none; margin: 20px 0 0; padding: 0; display: grid; gap: 10px; }
.cartrow, .pflist li {
  display: flex; gap: 15px; align-items: center;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius); padding: 13px;
  transition: border-color var(--fast);
}
.cartrow:hover, .pflist li:hover { border-color: var(--line-lit); }
.cartrow--bad { border-color: rgba(240,105,94,.48); background: var(--bad-dim); }
.cart-art, .pf-art { flex: 0 0 56px; }
.cart-art img, .pf-art img {
  width: 56px; border-radius: 7px; display: block;
  border: 1px solid var(--line-soft);
}
.cart-info, .pf-info { flex: 1; min-width: 0; }
.cart-info h2, .pf-info h2 { margin: 0 0 5px; font-size: 15px; font-weight: 650; }
.cart-right, .pf-right { text-align: right; display: grid; gap: 8px; justify-items: end; }
.cart-price, .pf-value { margin: 0; font-weight: 700; font-size: 15px; }
.qtyform { display: flex; gap: 6px; }
.qtyform input { width: 66px; }

.checkout-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; margin-top: 26px; padding: 20px 22px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  flex-wrap: wrap;
}
.total { margin: 0; font-size: 15px; color: var(--text-dim); }
.total strong {
  font-family: var(--font-display); font-size: 26px; font-weight: 750;
  letter-spacing: -.02em; color: var(--text); margin-left: 8px;
}

.orderlist { list-style: none; margin: 20px 0; padding: 0; display: grid; gap: 7px; }
.orderlist li {
  display: flex; gap: 12px; align-items: center; padding: 12px 15px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius);
}
.oi-label { flex: 1; font-weight: 550; }
.oi-qty { color: var(--text-dim); font-size: 13px; }
.oi-price { font-weight: 700; }

.orderindex, .deckindex { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.orderindex a, .deckindex a {
  display: flex; gap: 12px; align-items: center; padding: 14px 16px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius);
  transition: border-color var(--fast), transform var(--fast);
}
.orderindex a:hover, .deckindex a:hover { border-color: var(--line-lit); transform: translateX(3px); }
.oi-ref { flex: 1; font-weight: 600; font-family: var(--font-mono); font-size: 13.5px; }
.oi-count { color: var(--text-dim); font-size: 13px; }
.oi-total { font-weight: 700; }

.status {
  font-size: 11px; padding: 4px 10px; border-radius: 999px;
  background: rgba(255,255,255,.06); color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .06em; font-weight: 700;
  white-space: nowrap; border: 1px solid var(--line-soft);
}
.status--awaiting_payment { background: rgba(232,189,91,.14); color: var(--gold); border-color: rgba(232,189,91,.32); }
.status--paid      { background: var(--good-dim); color: var(--good); border-color: rgba(67,201,141,.34); }
.status--shipped,
.status--collected { background: var(--accent-dim); color: var(--accent); border-color: rgba(106,165,255,.32); }
.status--cancelled,
.status--expired   { background: var(--bad-dim); color: var(--bad); border-color: rgba(240,105,94,.32); }

.order-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 4px;
}
.order-head .page-h1 { margin: 38px 0 0; }
.order-head .status { margin-top: 38px; }

/* Order progress track. Three steps, connected by a rule that fills in as
   the order advances. */
.track {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  margin: 26px 0 30px;
  padding: 0;
  counter-reset: t;
}
.track li {
  position: relative;
  padding: 34px 10px 0 0;
  counter-increment: t;
  color: var(--text-faint);
}
/* The connector sits BEHIND the dot and stops at the last step — a rail that
   runs off the end of the final step implies a fourth thing that never
   arrives. */
.track li::before {
  content: "";
  position: absolute;
  top: 10px; left: 11px; right: 0;
  height: 2px;
  background: var(--line);
}
.track li:last-child::before { display: none; }
.track li::after {
  content: counter(t);
  position: absolute;
  top: 0; left: 0;
  display: grid; place-items: center;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--bg-2);
  border: 2px solid var(--line);
  font-size: 11px;
  font-weight: 800;
  color: var(--text-faint);
}
.track li.done::after {
  content: "✓";
  background: var(--good);
  border-color: var(--good);
  color: #06120c;
}
.track li.done::before { background: var(--good); }
.track li.now::after {
  background: var(--gold);
  border-color: var(--gold);
  color: #1a1305;
  box-shadow: 0 0 0 4px var(--gold-glow);
}
.track li.done, .track li.now { color: var(--text); }
.track .t-k { display: block; font-weight: 650; font-size: 13.5px; }
.track .t-v { display: block; font-size: 11.5px; color: var(--text-dim); margin-top: 1px; }
@media (max-width: 480px) {
  .track .t-v { display: none; }
  .track .t-k { font-size: 12px; }
}

/* PayNow */
.paybox {
  margin: 30px 0; padding: 24px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.paybox h2 { margin: 0 0 18px; font-size: 17px; }
.payrow { display: flex; gap: 28px; flex-wrap: wrap; align-items: flex-start; }
.qr { background: #fff; padding: 11px; border-radius: 12px; box-shadow: var(--shadow); line-height: 0; }
.paydetails { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 9px 20px; align-content: start; }
.paydetails dt { color: var(--text-dim); font-size: 13px; }
.paydetails dd { margin: 0; font-weight: 650; font-variant-numeric: tabular-nums; }
.hold-line { margin: 16px 0 0; font-size: 14px; color: var(--text-dim); }

.deskbox {
  margin-top: 30px; padding: 20px 22px;
  border: 1px dashed var(--line-lit); border-radius: var(--radius-lg);
  background: rgba(232,189,91,.03);
}
.deskbox h2 { color: var(--gold); margin-top: 0; }
.deskbox h2::after { background: linear-gradient(90deg, rgba(232,189,91,.3), transparent); }
.deskactions { display: flex; gap: 9px; flex-wrap: wrap; }
.deskactions form { margin: 0; }

.tablewrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  margin-top: 18px;
  background: var(--bg-2);
}
.desktable { width: 100%; border-collapse: collapse; font-size: 14px; }
.desktable th {
  text-align: left; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--text-dim); padding: 12px 14px;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  background: var(--bg-raise); font-weight: 700;
}
.desktable td { padding: 13px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.desktable tr:last-child td { border-bottom: 0; }
.desktable tbody tr { transition: background var(--fast); }
.desktable tbody tr:hover { background: rgba(255,255,255,.02); }

/* ── 6f. Decks + account ─────────────────────────────────────────────────── */

.deckform textarea { min-height: 190px; }
.deckform-actions { display: flex; gap: 12px; align-items: center; margin-top: 14px; flex-wrap: wrap; }
.saveas { display: flex; gap: 8px; }
.deckstats {
  display: flex; gap: 1px; flex-wrap: wrap; margin: 26px 0 18px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.deckstats div {
  display: grid; gap: 3px; flex: 1 1 150px; padding: 18px 20px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
}
.deckstats .k { font-size: 10.5px; text-transform: uppercase; letter-spacing: .11em; color: var(--text-dim); font-weight: 700; }
.deckstats .v { font-family: var(--font-display); font-size: 22px; font-weight: 700; letter-spacing: -.02em; }

.decklines { list-style: none; margin: 18px 0 0; padding: 0; display: grid; gap: 6px; }
.decklines li {
  display: flex; gap: 12px; align-items: center; padding: 11px 15px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: 14px; flex-wrap: wrap;
  transition: border-color var(--fast);
}
.decklines li:hover { border-color: var(--line-lit); }
.dl-qty { color: var(--text-dim); min-width: 32px; font-variant-numeric: tabular-nums; font-weight: 600; }
.dl-name { flex: 1; min-width: 140px; font-weight: 550; transition: color var(--fast); }
.dl-name:hover { color: var(--accent); }
.dl-tags { color: var(--text-faint); font-size: 12px; }
.dl-price { font-weight: 700; min-width: 80px; text-align: right; }
.dl-buy form, .dl-buy { margin: 0; }

.acct-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px,1fr)); gap: 11px; margin: 26px 0; }
.acct-card {
  display: grid; gap: 5px; padding: 20px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  transition: border-color var(--fast), transform var(--fast);
}
.acct-card:hover { border-color: var(--line-lit); transform: translateY(-2px); }
.acct-card .k { font-size: 10.5px; text-transform: uppercase; letter-spacing: .11em; color: var(--text-dim); font-weight: 700; }
.acct-card .v { font-family: var(--font-display); font-size: 26px; font-weight: 700; letter-spacing: -.02em; }

.importbox {
  margin: 22px 0; padding: 16px 20px;
  background: linear-gradient(180deg, var(--bg-raise), var(--bg-2));
  border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.importbox summary { cursor: pointer; font-weight: 650; list-style: none; display: flex; align-items: center; gap: 8px; }
.importbox summary::-webkit-details-marker { display: none; }
.importbox summary::before { content: "▸"; color: var(--text-dim); transition: transform var(--fast); }
.importbox[open] summary::before { transform: rotate(90deg); }
.importbox form { margin-top: 14px; display: grid; gap: 11px; justify-items: start; }

/* ── 6g. 404 ─────────────────────────────────────────────────────────────── */

.notfound { padding: 110px 0; text-align: center; }
.notfound h1 { margin: 0 0 10px; font-size: clamp(28px, 4vw, 38px); }
.notfound .sub { color: var(--text-dim); margin: 0 auto 26px; }

/* ── 7. Inside the Telegram Mini App ─────────────────────────────────────────
   Set by miniapp.js on <html> when Telegram.WebApp.initData is non-empty.
   The SAME pages serve the website and the Mini App — this only adapts the
   chrome, because Telegram already draws a header with the app's name and a
   close button, so the site's own brand row is a second header in a 60px-tall
   space that has none to spare. */

.in-telegram .topbar .brand { display: none; }
.in-telegram .topbar {
  padding-top: max(4px, env(safe-area-inset-top));
  height: auto;
  min-height: 40px;
  /* Right, not centre. Everything that was in this row moved to the tab bar
     except the cart, and a lone cart icon centred in a full-width header
     reads as a mistake. Against the right edge it reads as a cart. */
  justify-content: flex-end;
}
.in-telegram .topnav { justify-content: flex-end; gap: 4px; }

/* ⚠️ Signed OUT inside Telegram there is genuinely nothing left in this row:
   the brand is hidden (Telegram draws its own header above it), every
   .nav-dup is hidden at phone width, .signin is hidden because the Mini App
   signs you in automatically, and .topcart never renders without a user. An
   empty 40px bar above the fold on a 600px screen is pure loss, so it
   collapses entirely rather than being reserved for nothing. */
.in-telegram .topbar:not(:has(.topcart)) { display: none; }

/* The Mini App signs the buyer in automatically, so a "Sign in" link is a dead
   control — it would send them to a widget that cannot run in here. */
.in-telegram .topnav .signin { display: none; }

/* Same reasoning, one step worse: the cross-game link points at an ORIGIN
   outside this mount, so in here it would hand the buyer to an external
   browser mid-session and drop their Mini App state. */
.in-telegram .xgame { display: none; }

/* Telegram's own chrome overlaps the bottom of the viewport on iOS. */
.in-telegram .footer { padding-bottom: max(20px, env(safe-area-inset-bottom)); }

/* The legal notice stays — it is Riot's requirement, not decoration — but the
   fx line is reference detail that costs a whole screen on a phone. */
.in-telegram .footer .meta { font-size: 11px; }

/* Inside Telegram the tab bar IS the navigation, so the top row collapses to
   nothing but the account chip. Telegram draws its own header above it. */
.in-telegram .tabbar { background: rgba(7, 9, 16, .96); }

/* ── 8. Narrow phones ────────────────────────────────────────────────────── */

/* On a phone the brand ("Riftbound @ Bunker TCG") and a nowrap nav compete for
   the same row, and the brand was already wrapping onto two lines. Drop the
   "@ Bunker TCG" suffix below 430px — the footer's legal notice still names
   the business on every page, so nothing is lost but the duplication. */
@media (max-width: 430px) {
  .brand-text em { display: none; }
  .topbar { gap: 10px; padding: 0 14px; }
  .info { padding: 9px 10px 11px; }
  .info h2 { font-size: 12.5px; }
  .tags { gap: 4px; font-size: 10px; }
  .tags span { padding: 1px 5px; }
  .price strong { font-size: 14px; }
}

/* Deck line art. `aria-hidden` + `tabindex="-1"` on the thumbnail's anchor:
   it points at the same card as the name beside it, so without that a
   keyboard user tabs through a 60-card deck twice and a screen reader reads
   every card name two times. */
.dl-art { flex: 0 0 30px; line-height: 0; }
.dl-art img {
  width: 30px; border-radius: 4px; display: block;
  border: 1px solid var(--line-soft);
}

.related { margin-top: 44px; padding-top: 8px; border-top: 1px solid var(--line); }

/* Price range. The dash is a separator, not a control — it must not be
   reachable by tab or announced by a screen reader, which is why the labels
   on either side are real (visually hidden) <label>s and this is aria-hidden
   in the markup. */
.pricerange { display: flex; align-items: center; gap: 6px; }
.pricerange input {
  min-width: 0;
  padding: 10px 8px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* The spinner arrows eat ~20px of a 60px field and nobody nudges a price
   range one dollar at a time. */
.pricerange input::-webkit-outer-spin-button,
.pricerange input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pricerange input { -moz-appearance: textfield; appearance: textfield; }
.pr-dash { color: var(--text-faint); font-size: 13px; flex-shrink: 0; }

@media (max-width: 900px) {
  .filters .pricerange { grid-column: 1 / -1; }
}
