/* ============================================================
   Storefront - style.css
   White-label TCG storefront. Brand colors + fonts are driven by
   tcg_brand_inline_style() (see hq/inc/tcg-db.php) which emits
   --primary, --primary-dark, --accent, --accent-dark, --bg, --bg-alt,
   --text, --text-dim, --border, --font-heading, --font-body. The
   defaults below render a usable site if no theme config exists.
   ============================================================ */

/* ------------------------------------------------------------
   1. CSS Custom Properties
   ------------------------------------------------------------ */
:root {
    /* ── Brand-driven (tcg_brand_inline_style overrides these) ── */
    --primary:      #1d4ed8;
    --primary-dark: #1e40af;
    --accent:       #dc2626;
    --accent-dark:  #b91c1c;
    --bg:           #ffffff;
    --bg-alt:       #f8fafc;
    --text:         #0f172a;
    --text-dim:     #64748b;
    --border:       #e2e8f0;
    --font-heading: 'Barlow Condensed', sans-serif;
    --font-body:    'Inter', sans-serif;

    /* ── Internal aliases (not brand-overridden) ── */
    --bg2: var(--bg-alt);
    --bg3: #f1f5f9;
    --bg4: #e8edf2;
    --border2: #cbd5e1;
    --muted: var(--text-dim);
    --dim:   #94a3b8;

    /* Secondary accent - used for "gold" buttons, foil shimmer, etc. */
    --accent2: #f59e0b;

    /* Semantic colors (status badges, alerts) - intentionally fixed */
    --gold:  #f59e0b;
    --green: #16a34a;
    --amber: #d97706;
    --red:   #dc2626;

    /* Radii */
    --radius:    8px;
    --radius-lg: 14px;

    /* Legacy alias */
    --font-head: var(--font-heading);

    /* Game brand colors - slightly richer for light bg */
    --c-pokemon:  #e63900;
    --c-mtg:      #a07830;
    --c-yugioh:   #1d4ed8;
    --c-onepiece: #ea580c;
    --c-lorcana:  #6d28d9;
    --c-dbs:      #b91c1c;
    --c-fab:      #92400e;
}

:root[data-theme="dark"] {
    /* Dark theme overrides - applied after brand vars so brand colors
       stay accurate; only neutrals/backgrounds flip. */
    --bg:       #0f172a;
    --bg-alt:   #1a2336;
    --bg3:      #1e2a42;
    --bg4:      #243050;
    --border:   #2d3a52;
    --border2:  #3d4e6a;
    --text:     #f1f5f9;
    --text-dim: #94a3b8;
    --dim:      #4d5562;

    /* Semantic - slightly brighter for dark backgrounds */
    --green: #22c55e;
    --amber: #f59e0b;
    --red:   #ef4444;

    /* Game brand colors - brighter for dark bg */
    --c-pokemon:  #e63900;
    --c-mtg:      #c4a44a;
    --c-yugioh:   #2563eb;
    --c-onepiece: #f97316;
    --c-lorcana:  #7c3aed;
    --c-dbs:      #dc2626;
    --c-fab:      #b45309;
}

/* ------------------------------------------------------------
   2. Reset & Base
   ------------------------------------------------------------ */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Enforce [hidden] - author display rules can override the UA stylesheet */
[hidden] { display: none !important; }

html {
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-body);
    background-color: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    line-height: 1.6;
    min-height: 100vh;
}

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

ul, ol {
    list-style: none;
}

img {
    max-width: 100%;
    display: block;
}

button {
    cursor: pointer;
    font-family: inherit;
    border: none;
    background: none;
}

input, textarea {
    font-family: inherit;
}

/* ------------------------------------------------------------
   3. Layout Utilities
   ------------------------------------------------------------ */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

.section {
    padding: 60px 0;
}

.section + .section {
    padding-top: 0;
}

main {
    padding-top: 0;
}

/* ------------------------------------------------------------
   4. Site Header
   ------------------------------------------------------------ */
.site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    /* hide-on-scroll transition was tried 2026-05-23 and reverted —
       caused a "two-stage" hide feeling on operator's device. */
    /* --header-bg is a per-tenant override channel for the navbar
       background, paralleling --header-text from the same 2026-05-18
       commit. Defaults to var(--bg2) so existing tenants are unchanged.
       Tenants whose logo has a non-neutral edge color (Retro Tabletop's
       yellow #ffd42d, e.g.) override this to match the logo edge for
       gestalt continuity — WITHOUT yellowifying every other surface
       that uses --bg2 (shop cards, event cards, hero subsections, etc.).
       The earlier attempt set theme.bg2 directly and made every elevated
       card the same yellow as the navbar; this knob solves the problem
       at the right level. */
    background: var(--header-bg, var(--bg2));
    border-bottom: 1px solid var(--border);
    /* min-height = logo-base + 2× operator-controlled vertical padding.
       Base 44px is the default wordmark height; default pad 13px keeps
       the historical 70px header (44+26=70). Operator-tunable via
       --logo-pad-y (HQ → Brand Assets "Logo padding" slider).
       min-height (not height) so a tall per-tenant logo can grow the
       bar instead of being clipped — perfect-circle-games runs
       logo_height:52 with logo_padding_y:4 and intentionally relies on
       the logo dictating bar height. */
    min-height: calc(44px + 2 * var(--logo-pad-y, 13px));

    /* Make .site-header itself the flex container that vertically
       centers .header-inner. Previously vertical centering lived on
       .header-inner (`height: 100%; align-items: center`) and depended
       on .site-header having an EXPLICIT height that the `100%` could
       resolve against. From 2026-05-20 20:00 (when the desktop
       `height: 70px` pin was removed in favor of pure min-height) to
       2026-05-23, that resolution chain was broken: .header-inner
       collapsed to content height, the fixed bar painted to its 70px
       min-height floor, and the nav row stuck to the top with ~26px
       of empty filled space underneath — "buttons floating to the
       top of the screen." Centering at this level removes the
       percentage-height dependency entirely; header-inner sizes
       naturally and is centered by the parent regardless of bar
       height. Mobile breakpoint keeps the same display:flex but
       overrides min-height to auto for content-sized chrome. */
    display: flex;
    align-items: center;
}

.header-inner {
    /* .site-header is now the flex container that vertically centers
       this row (see comment on .site-header above). As a flex item
       here, we need width: 100% to fill the bar (margin: 0 auto then
       centers the constrained max-width). The old `height: 100%` is
       gone — height comes from content + .site-header's centering. */
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 32px;
    display: flex;
    align-items: center;
    gap: 32px;
}

/* Logo - brand name is the highest-priority element in the header.
   Never shrinks under flex pressure; never truncates. If anything in the
   header has to give, the search input drops first (hidden at <=1440 in
   the media block below), then nav links collapse to hamburger. The logo
   only allows itself to wrap as a phone-only fallback (see <=480 block). */
.logo {
    display: flex;
    flex-direction: row;          /* mark sits on the left of the text-stack */
    align-items: center;
    gap: 10px;
    line-height: 1.1;
    text-decoration: none;
    flex-shrink: 0;
}
/* Optional brand-mark icon; rendered when $store['brand_mark_url'] is set
   (per-tenant, uploaded via HQ → Storefront → Brand Assets).
   Height-controlled (width: auto) so per-tenant emphasis via
   theme.logo_mark_height scales the mark proportionally — previously
   width was hardcoded to 36px which left a tenant-set height stretching
   the image vertically. Default 36px height visually pairs with the
   typographic wordmark cap-height. */
.logo-mark {
    height: 36px;
    width: auto;
    display: block;
    flex-shrink: 0;
    object-fit: contain;
}
/* Inner column wrapper around the wordmark + tagline. Carries the column
   layout that .logo had before the brand-mark icon was added - preserves
   the existing two-line wordmark/tagline behavior whether or not a mark
   is present. */
.logo-text {
    display: flex;
    flex-direction: column;
    line-height: 1.1;
    min-width: 0;
}
/* Mobile mark sizing — auto-scaled width via prior height-controlled
   change, so this rule trimmed off the old hardcoded width override. */

/* Full brand-lockup image - replaces the typographic wordmark + small mark
   icon entirely when config_json.use_logo_as_wordmark is true and
   logo_url is set. Height-controlled (width auto-scales) so wide-aspect
   logos fit cleanly into the header band without overflowing the row.
   Operator request 2026-05-07: cardceus's logo PNG includes the wordmark
   + tagline as part of the artwork; rendering text alongside it would
   double up. */
.logo--image { padding: 6px 0; }
.logo-wordmark {
    display: block;
    /* Per-tenant height override via --logo-h (inline style in header.php
       from config_json.theme.logo_height). Falls back to 44px desktop;
       mobile + phone breakpoints scale the operator's value by the same
       ratio as the platform default progression (44→38→34, i.e. ×0.86
       and ×0.77) so a desktop bump doesn't blow out the mobile chrome.
       The mobile chrome is `height: auto`, so an unscaled --logo-h on a
       tiny screen would push the navbar visibly taller; the per-viewport
       calc() keeps mobile chrome growth proportional and modest.
       Operator request 2026-05-11: pegasus needed a larger logo so the
       tagline read clean. Per-viewport scaling fix: 2026-05-17 after
       multiple iterations where bumping --logo-h grew mobile chrome but
       didn't visibly change the logo (the ratio of logo:chrome stayed
       similar because both grew together — looked like "navbar grew,
       logo same"). */
    height: var(--logo-h, 44px);
    width: auto;
    max-width: 320px;
    object-fit: contain;
    flex-shrink: 0;
}
@media (max-width: 768px) {
    .logo-wordmark {
        /* Scale --logo-h by 0.86 (= 38/44 default-ratio) so an operator
           bump of e.g. 50px renders as ~43px here, ~5px above the
           unbumped 38px — proportional, not runaway. Fallback 44px ×
           0.86 = 38px = same as the prior hardcoded default. */
        height: calc(var(--logo-h, 44px) * 0.86);
        max-width: 240px;
    }
}
@media (max-width: 480px) {
    .logo-wordmark {
        /* Same idea, ×0.77 (= 34/44) for phone. Default 44 × 0.77 = ~34px. */
        height: calc(var(--logo-h, 44px) * 0.77);
        max-width: 200px;
    }
}

/* Per-tenant override for the small mark next to the typographic
   wordmark (mark + text mode), via --logo-mark-h. Width auto-scales
   to preserve aspect ratio (most marks are square; some — like Thunder
   Kitty's cat-on-cards lockup — are taller than wide and benefit from
   the wider headroom). Default 36px height set in the base rule above;
   this rule only applies when the inline-style --logo-mark-h is set. */
.logo-mark {
    height: var(--logo-mark-h, 36px);
}

/* Per-viewport scaling of the operator-set --logo-mark-h, mirroring
   the .logo-wordmark calc() pattern (same fix, same rationale):
   without this, setting --logo-mark-h: 56 on a tenant would render
   a 56px mark on phones too, blowing out the mobile header (which is
   height: auto). Tablet ratio 0.86 (= 30/35 ish) and phone 0.77 keep
   operator bumps proportional. Defaults match the prior hardcoded
   pattern: 36 → ~31 tablet → ~28 phone. */
@media (max-width: 768px) {
    .logo-mark {
        height: calc(var(--logo-mark-h, 36px) * 0.86);
    }
}
@media (max-width: 480px) {
    .logo-mark {
        height: calc(var(--logo-mark-h, 36px) * 0.77);
    }
}

/* Brand wordmark in the header logo. Uses --font-display (set in inc/header.php
   from $store['theme']['font_display'], defaulting to Pirata One blackletter)
   so the brand line gets a distinct character vs the rest of the page typography
   - matches Laughing Dragon Tavern's wooden-sign aesthetic. Letter-spacing is
   tightened to 0 because blackletter letters are visually denser than Barlow
   Condensed; size bumped slightly because lowercase blackletter glyphs read
   smaller per pixel than sans-serif.

   .logo-name--stacked is added when the auto-split heuristic in inc/header.php
   detected a brand+descriptor pair (e.g. "Laughing Dragon Tavern" / "Games &
   Collectibles"), and the inner spans render as two stacked lines like the
   wooden-sign reference image. .logo-name-descriptor sits at ~70% of brand
   line size and shares the gradient. */
.logo-name {
    font-family: var(--font-display, var(--font-head));
    font-size: 1.55rem;
    /* Default 400 was picked for blackletter Google Fonts (Pirata One)
       that only ship one weight. Sans-serif wordmarks (Plus Jakarta,
       Bebas, Montserrat) often want a heavier weight to balance the
       mark icon. Per-tenant override via theme.logo_weight emits a
       `--logo-weight` style on .site-header (inc/header.php). Operator
       caught the thin look on bananahandles 2026-05-23. */
    font-weight: var(--logo-weight, 400);
    color: #fff;
    letter-spacing: 0;
    /* line-height bumped from 1.05 → 1.2 + padding-bottom for descenders.
       background-clip: text below paints the gradient ONLY inside the line
       box, so g/j/p/q/y descenders get cropped at the bottom when
       line-height is tight - exact same bug pattern we fixed earlier on
       .hero-title. Operator screenshot 2026-05-13 (Farmville header). */
    line-height: 1.2;
    padding-bottom: 0.08em;
    background: linear-gradient(90deg, var(--text) 60%, var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    white-space: nowrap;
}
.logo-name--stacked {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.05;
    white-space: normal; /* allow the two child lines to break */
}
.logo-name-brand {
    display: block;
    /* Was 1.02 - same descender-clip story. Bump to match parent's 1.2. */
    line-height: 1.2;
    white-space: nowrap;
}
.logo-name-descriptor {
    display: block;
    font-size: 0.72em;
    line-height: 1.05;
    margin-top: 2px;
    opacity: 0.95;
    letter-spacing: 0.01em;
    white-space: nowrap;
}
/* background-clip: text only clips the bg to text *in that exact element*,
   not nested children. When .logo-name--stacked wraps two child spans, the
   gradient on the parent has no text to clip to. Re-apply the same gradient
   to each child span so both lines pick up the brand wash. Dark-mode override
   below mirrors this. */
.logo-name--stacked .logo-name-brand,
.logo-name--stacked .logo-name-descriptor {
    background: linear-gradient(90deg, var(--text) 60%, var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.logo-sub {
    font-size: 0.6rem;
    color: var(--muted);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    -webkit-text-fill-color: var(--muted);
    white-space: nowrap;
}

/* Main nav */
.main-nav {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1;
    /* min-width:0 lets the flex item shrink below its content's natural
       width - without this, the nav refuses to shrink past its longest
       item and pushes .header-actions off-screen instead. Combined with
       overflow:hidden, items the JS hasn't yet hidden get clipped instead
       of forcing layout. Result: cart/account/hamburger stay visually
       locked to the right edge no matter how cramped. */
    min-width: 0;
    overflow: hidden;
}

.main-nav a {
    font-size: 0.85rem;
    font-family: var(--font-head);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* --header-text is a per-tenant override channel for text rendered
       inside the .site-header (nav links, hamburger icon, etc.). Defaults
       to var(--muted) so existing tenants are unchanged. Tenants whose
       navbar bg is a high-saturation color via theme.bg2 (Retro Tabletop's
       yellow #ffd42d, e.g.) override this to a dark value (#1c1c1c) for
       legibility; tenants with dark navbars leave it unset and inherit
       --muted. Added 2026-05-18 alongside the retro-tabletop edge-match. */
    color: var(--header-text, var(--muted));
    padding: 6px 10px;
    border-radius: var(--radius);
    transition: color 0.15s, background 0.15s;
    position: relative;
    /* Never wrap an individual label across two lines. Multi-word items
       like "GIFT CARDS" should stay on one line; if they don't fit, the
       fix belongs at the layout level (collapse extras into the hamburger),
       not by breaking a label mid-word. */
    white-space: nowrap;
}

.main-nav a:hover {
    color: var(--text);
    background: var(--bg3);
}

.main-nav a.active {
    color: var(--text);
}

.main-nav a.active::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 10px;
    right: 10px;
    height: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent2));
    border-radius: 2px;
}

/* Header actions - rigid right-side cluster.
   flex-shrink: 0 + margin-left: auto pins this group to the visible right
   edge and prevents the search/cart/account/hamburger from being squeezed
   off-screen no matter how narrow the viewport gets. The .logo on the
   left absorbs all compression by wrapping its text. */
.header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex-shrink: 0;
}

.header-icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius);
    color: var(--muted);
    transition: color 0.15s, background 0.15s;
    text-decoration: none;
}

.header-icon-btn:hover {
    color: var(--text);
    background: var(--bg3);
}

.header-icon-btn.discord-btn:hover {
    color: #5865f2;
}

.hamburger-btn {
    display: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius);
    /* Same --header-text knob as .main-nav a so the hamburger icon stays
       readable on tenants with bright/saturated header backgrounds. */
    color: var(--header-text, var(--muted));
    transition: color 0.15s, background 0.15s;
}

.hamburger-btn:hover {
    color: var(--text);
    background: var(--bg3);
}

/* ------------------------------------------------------------
   5. Mobile Nav Overlay
   ------------------------------------------------------------ */
.mobile-nav {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 300px;
    max-width: 85vw;
    background: var(--bg2);
    border-left: 1px solid var(--border);
    z-index: 200;
    transform: translateX(100%);
    transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
    padding: 0;
    /* Operator report 2026-05-08: when nav links fill the drawer past
       viewport height, the bottom rows were unreachable - the drawer
       had no overflow-y declared, so children got clipped at the
       viewport edge and couldn't be scrolled. Adding scrollable Y
       on the container fixes that; the close button header below
       sticks to the top so it stays reachable while the rest scrolls. */
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.mobile-nav.open {
    transform: translateX(0);
}

.mobile-nav-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 20px 16px;
    border-bottom: 1px solid var(--border);
    /* Pin the close button so users can dismiss the drawer no matter
       how far they have scrolled into the link list. */
    position: sticky;
    top: 0;
    background: var(--bg2);
    z-index: 1;
}

.mobile-nav-title {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

.mobile-nav-close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius);
    color: var(--muted);
    transition: color 0.15s, background 0.15s;
}

.mobile-nav-close:hover {
    color: var(--text);
    background: var(--bg3);
}

/* Quick-action row in mobile drawer (Cart + Account). Phone-only -
   these are hidden in the bar at <=480 and surfaced here.
   Padding is symmetric (12px top + bottom) so the buttons sit equidistant
   between the search divider above and the quick-actions divider below.
   Was 0/12 - top flush against the search divider while bottom had room,
   which read as the buttons sliding off the top edge of their band. */
.mobile-nav-quick-actions {
    /* Stacked vertically so neither button has to compete for width;
       "My Account" + icon was wrapping to two lines on narrow drawers
       (operator report 2026-05-08). Each row gets full width now. */
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 18px;
    border-bottom: 1px solid var(--border, #e2e8f0);
    margin-bottom: 8px;
}
.mobile-nav-quick-action {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--border, #e2e8f0);
    border-radius: 8px;
    background: var(--bg-alt, #f8fafc);
    color: var(--text);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.15s, border-color 0.15s;
}
.mobile-nav-quick-action:hover {
    background: var(--bg3, #f1f5f9);
    border-color: var(--accent, #dc2626);
}
.mobile-nav-quick-badge {
    display: inline-block;
    background: var(--accent, #dc2626);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    border-radius: 999px;
    padding: 1px 7px;
    margin-left: 4px;
    line-height: 1.2;
    vertical-align: 1px;
}

.mobile-nav-links {
    display: flex;
    flex-direction: column;
    padding: 16px 12px;
    gap: 2px;
    flex: 1;
}

.mobile-nav-links a {
    font-family: var(--font-head);
    font-size: 1.2rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    padding: 12px 16px;
    border-radius: var(--radius);
    transition: color 0.15s, background 0.15s;
}

.mobile-nav-links a:hover {
    color: var(--text);
    background: var(--bg3);
}

/* Active item: brand accent fill + matching text color. Operator-reported
   2026-05-16: the drawer's active state was using var(--bg3) (the same
   pale elevated-surface color as hover), which made the selected item
   read as LIFTED instead of PRESSED — visually backwards for "I'm here
   now." Switching to the tenant brand accent gives the active row a
   saturated, committed look that maps to the existing primary CTA
   styling (.btn-primary uses the same gradient base + --btn-primary-text
   pairing). On tenants whose accent is dark — purple, red, navy — the
   active row reads as a strong dark chip; on light-accent tenants
   (cardceus gold) it still contrasts cleanly because --btn-primary-text
   is per-tenant. */
.mobile-nav-links a.active {
    color: var(--btn-primary-text, #fff);
    background: var(--accent);
}

.mobile-nav-footer {
    padding: 16px 20px;
    border-top: 1px solid var(--border);
}

/* Mobile nav search bar */
.mobile-nav-search {
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
}
.mobile-nav-search-wrap {
    display: flex;
    align-items: center;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    height: 40px;
}
.mobile-nav-search-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    flex-shrink: 0;
    color: var(--dim);
    pointer-events: none;
}
.mobile-nav-search-input {
    flex: 1;
    background: none;
    border: none;
    outline: none;
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.9rem;
    padding: 0;
    min-width: 0;
}
.mobile-nav-search-input::placeholder { color: var(--dim); }
.mobile-nav-search-submit {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 100%;
    flex-shrink: 0;
    background: none;
    border: none;
    border-left: 1px solid var(--border);
    color: var(--muted);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}
.mobile-nav-search-submit:hover {
    color: var(--text);
    background: var(--bg4);
}

/* Mobile nav social row (Instagram link) */
.mobile-nav-social {
    margin-top: 10px;
}
.mobile-nav-social-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-radius: var(--radius);
    background: rgba(225, 48, 108, 0.08);
    border: 1px solid rgba(225, 48, 108, 0.2);
    color: #e1306c;
    font-size: 0.9rem;
    font-weight: 500;
    transition: background 0.15s;
    text-decoration: none;
}
.mobile-nav-social-btn:hover {
    background: rgba(225, 48, 108, 0.15);
}

.mobile-nav-discord {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-radius: var(--radius);
    background: rgba(88, 101, 242, 0.12);
    border: 1px solid rgba(88, 101, 242, 0.3);
    color: #7289da;
    font-size: 0.9rem;
    font-weight: 500;
    transition: background 0.15s;
    text-decoration: none;
}

.mobile-nav-discord:hover {
    background: rgba(88, 101, 242, 0.2);
}

.mobile-nav-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    z-index: 199;
    display: none;
    backdrop-filter: blur(2px);
}

.mobile-nav-overlay.open {
    display: block;
}

/* ------------------------------------------------------------
   6. Buttons
   ------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: var(--radius);
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    transition: opacity 0.15s, transform 0.1s, box-shadow 0.15s;
    white-space: nowrap;
    cursor: pointer;
    border: none;
    text-decoration: none;
}

.btn:hover {
    opacity: 0.9;
    transform: translateY(-1px);
}

.btn:active {
    transform: translateY(0);
}

.btn-primary {
    /* Fallback chain for the gradient's second stop, in preference order:
         1. --accent2     (operator-set secondary accent — cardceus uses this)
         2. --accent-dark (operator-set darker shade of accent — most clean
                           single-color brands set this for hover states)
         3. --accent      (falls back to solid color if neither is set)
       Was just var(--accent2) with no fallback, which silently fell through
       to the style.css :root default of #f59e0b (amber). For a brown-accent
       brand like BananaHandles that produced a brown → amber muddy gradient.
       This chain keeps the gradient inside the operator's color family
       regardless of which dark-shade key they set. Operator caught
       bananahandles 2026-05-23. */
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent2, var(--accent-dark, var(--accent))) 100%);
    /* --btn-primary-text is a per-tenant override (set via
       tcg_brand_inline_style). Defaults to white, which works on most
       brand-color → gold gradients. Tenants whose accent is light (e.g.
       cardceus dark mode where the gradient runs gold→gold) set this
       to a near-black so the label stays legible across the whole
       gradient instead of vanishing on the lighter side. */
    color: var(--btn-primary-text, #fff);
    /* Auto-derived theme-color glow via color-mix(). Resolution order:
         1. --accent-glow         — explicit operator override (rare)
         2. color-mix(--accent…)  — automatic 25% tint of the brand --accent
         3. amber default          — last-resort if no theme at all
       Pre-2026-05-24 the glow was the hardcoded `rgba(220,38,38,0.25)`
       red regardless of brand. On green-accent tenants the red halo
       was a visible mismatch; on dark-bg tenants like CDC Games it
       was barely visible. color-mix() auto-tints the brand color so
       every tenant gets a brand-matched halo without needing an
       operator-set --accent-glow. color-mix shipped in all major
       browsers in 2023; universal by 2026.

       (--accent-glow itself is preserved as an escape hatch in case
       a tenant wants something other than the auto-derived 25% tint
       — eg a softer or saturated custom halo color.) */
    box-shadow: 0 4px 14px var(--accent-glow, color-mix(in srgb, var(--accent, #dc2626) 25%, transparent));
}

.btn-primary:hover {
    box-shadow: 0 6px 20px var(--accent-glow-hover, color-mix(in srgb, var(--accent, #dc2626) 40%, transparent));
}

.btn-outline {
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border2);
}

.btn-outline:hover {
    background: var(--bg3);
    border-color: var(--muted);
}

.btn-ghost {
    background: transparent;
    color: var(--muted);
    padding: 8px 14px;
}

.btn-ghost:hover {
    color: var(--text);
    background: var(--bg3);
}

.btn-sm {
    padding: 6px 14px;
    font-size: 0.85rem;
}

/* .btn-gold was originally a hardcoded amber gradient (#f59e0b →
   #d97706) used for the buylist CTA on every tenant. It clashed badly
   with tenants whose brand isn't yellow/gold — operator flagged on
   paradox-trading-post 2026-05-21 (deep purple brand, gold buylist
   button stuck out). Now uses --accent → --primary as the gradient
   stops, falling back to the original amber as a sensible default if
   neither is set (back-compat for tenants who never configured a theme).
   The class name stays "btn-gold" since it's referenced from PHP
   templates and tenant config; the visual just follows the theme now. */
.btn-gold {
    /* "Gold" is now a misnomer — this is the SECONDARY brand CTA (Buylist,
       etc.). Gradient stays WITHIN the --primary color family rather than
       crossing into --accent (which produced muddy two-color gradients on
       brands where the two are visually distinct — bananahandles caught
       2026-05-23 with brown→yellow). Falls back to solid --primary when
       --primary-dark isn't set, so single-color brands (cards2go) render
       clean solid gold and operators don't need to set a dark shade just
       to avoid mud. The amber/d97706 hardcoded fallbacks at the bottom
       are for tenants who never set a theme at all. */
    background: linear-gradient(135deg, var(--primary, #f59e0b), var(--primary-dark, var(--primary, #d97706)));
    /* TYPO FIX 2026-05-24: this var was `--btn_primary_text` (underscores)
       which doesn't exist — the brand-emitter in hq/inc/tcg-db.php writes
       `--btn-primary-text` (dashes), matching every other .btn-* consumer
       in this file. The mismatched name meant 177/184 chicards tenants had
       a working brand-emitted color silently ignored on the Buylist button,
       which then fell through to the hardcoded #1a1000 (near-black) default
       — unreadable on dark-primary tenants like CDC Games (dark navy
       Buylist button with near-black text). Operator caught via fleet audit
       2026-05-24. Single-character fix: underscore → dash.

       Also swapping the hardcoded `#1a1000` fallback to
       `light-dark(#1a1000, #fff)` so the 7/184 tenants whose brand block
       doesn't set --btn-primary-text get auto-contrast: near-black text on
       light --primary (cardceus gold), white text on dark --primary
       (CDC Games navy). light-dark() reads the `color-scheme` declared on
       :root — inc/header.php emits the per-tenant scheme via luminance,
       so this resolves to the tenant's intended brightness without
       conditional CSS. Browsers that don't support light-dark() ignore
       the fallback expression and stay on the old #1a1000 baseline. */
    color: var(--btn-primary-text, light-dark(#1a1000, #fff));
    /* Same auto-derived theme-color glow approach as .btn-primary above
       — see that block for the rationale. Resolution:
         1. --primary-glow         operator override
         2. color-mix(--primary…)  auto-tint of the brand --primary
         3. amber default          last-resort fallback
       Pre-2026-05-24: hardcoded amber `rgba(245,158,11,0.30)` at rest,
       0.45 at hover — louder than btn-primary's 0.25/0.35, which made
       the Buylist button look pre-hovered next to BROWSE SHOP. Operator
       caught on CDC Games where the amber halo on a dark-navy button
       was especially mismatched. Auto-derived glow + matched opacities
       resolve both issues at once. */
    box-shadow: 0 4px 14px var(--primary-glow, color-mix(in srgb, var(--primary, #f59e0b) 25%, transparent));
}

.btn-gold:hover {
    box-shadow: 0 6px 20px var(--primary-glow-hover, color-mix(in srgb, var(--primary, #f59e0b) 40%, transparent));
}

.btn-discord {
    background: #5865f2;
    color: #fff;
    box-shadow: 0 4px 14px rgba(88, 101, 242, 0.3);
}

.btn-discord:hover {
    box-shadow: 0 6px 20px rgba(88, 101, 242, 0.45);
}

/* ------------------------------------------------------------
   7. Hero Section
   ------------------------------------------------------------ */
.hero {
    /* Operator request 2026-05-08: hero tint was hardcoded rgba(220,38,38,...)
       (a baked-in red), so on tenants whose brand isn't red - cardceus
       (gold/black), gopher (blue), etc. - the page top read as off-color
       against the rest of their theme. Switched both the radial glow and
       the diagonal grid stripe to color-mix() over var(--accent) so each
       tenant's brand accent paints its own hero tint. The grid pattern
       (repeating 45deg stripes, 30px gap) is preserved - just colored
       correctly now. Falls back to a neutral white tint where color-mix
       isn't supported. */
    background:
        radial-gradient(ellipse 80% 60% at 50% -10%, color-mix(in srgb, var(--accent) 9%, transparent) 0%, transparent 65%),
        repeating-linear-gradient(
            45deg,
            transparent,
            transparent 29px,
            color-mix(in srgb, var(--accent) 4%, transparent) 30px
        ),
        var(--bg);
    position: relative;
    overflow: hidden;
}

.hero-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 90px 24px 80px;
    position: relative;
    z-index: 1;
}

.hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.7rem;
    font-family: var(--font-head);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    margin-bottom: 16px;
    padding: 4px 12px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg2);
}

.hero-eyebrow::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    display: inline-block;
    animation: pulse-dot 2s ease-in-out infinite;
}

@keyframes pulse-dot {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

.hero-title {
    font-family: var(--font-head);
    /* Fluid sizing - was fixed 4.5rem; long brand names like "Laughing Dragon
       Tavern Games & Collectibles" overflowed phones at the fixed size.
       The clamp keeps the desktop visual intact while bottoming out small
       enough that COLLECTIBLES fits on its own line at 360px. */
    font-size: clamp(2rem, 6vw, 4.5rem);
    font-weight: 700;
    /* Was 1 - bumped 2026-05-12 because letters with descenders (g, j, p,
       q, y) were getting clipped on hero taglines that use
       .accent-gradient. With background-clip:text + line-height near 1
       the gradient fill terminates at the line-box bottom, even though
       the glyph ink extends below the baseline. 1.1 reserves the bit of
       extra vertical room the descenders need without visibly loosening
       the headline. */
    line-height: 1.1;
    letter-spacing: -0.01em;
    color: var(--text);
    margin-bottom: 18px;
    /* Same wrap-safety treatment as .about-hero-title - let long single
       words break as a last resort + opt the parent flex/grid in to
       shrinking past intrinsic width. */
    overflow-wrap: anywhere;
    word-break: normal;
    hyphens: auto;
    min-width: 0;
    /* Browser distributes the words across lines for a balanced silhouette.
       Modern Chrome/Safari/FF support this; older browsers ignore. */
    text-wrap: balance;
}

/* Split brand: when the store name has a natural descriptor ("Games &
   Collectibles", "TCG", etc.), the homepage hero renders the brand line
   at full size and the descriptor at ~65% size on its own line. The split
   itself happens in inc/brand-split.php; classes get added to the .hero-title
   wrapper. .hero-title-tagline is the existing accent-gradient subtitle. */
.hero-title-brand,
.hero-title-descriptor,
.hero-title-tagline {
    display: block;
}
.hero-title-descriptor {
    font-size: 0.62em;
    opacity: 0.92;
    margin-top: 0.15em;
    line-height: 1.05;
}

.hero-title .accent-gradient {
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent2) 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    /* Belt-and-braces with the line-height bump on .hero-title:
       background-clip:text can crop descender ink right at the line-box
       even when there's enough leading. A few hundredths of an em on
       padding-bottom extends the paint area to cover the g/j/p/q/y
       descenders fully. Cumulatively invisible spacing-wise. */
    padding-bottom: 0.08em;
}

.hero-sub {
    font-size: 1.05rem;
    color: var(--muted);
    max-width: 520px;
    margin-bottom: 32px;
    line-height: 1.7;
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 40px;
}

/* ============================================================
   Hero background image (.hero--with-art) — TEMPLATE FEATURE
   ============================================================
   When a tenant sets config_json.hero_image, the URL is injected via
   the --hero-art-url custom property on the .hero section, and the
   image renders as a background layer BEHIND the text/CTAs.

   Two modes via the .hero--art-{mode} class:

   .hero--art-contain (default) — for small/medium brand artwork like
      character mascots, illustration cutouts, square logos. Image is
      contained at natural aspect ratio, anchored to the right of the
      hero, with a soft left-edge mask that fades the inner edge into
      the bg so text remains legible. Does NOT pixelate when the source
      is smaller than the hero width (object-fit:contain-style sizing).

   .hero--art-cover — for wide photographic banners like a storefront
      shot. Image fills the full hero with object-fit:cover behavior;
      a low opacity (0.35) acts as a built-in scrim so text stays
      readable across whatever's underneath.

   All knobs are CSS custom properties so the inline style can override
   any aspect (position, size, mask, opacity) per-tenant without CSS
   edits. Same mechanics work for future page banners — shop / events
   / buylist can reuse this once we wire them up.

   Mobile (≤900px): opacity drops sharply so text is the focal point.
   The image becomes a true background watermark on small screens.

   Without the .hero--with-art modifier, NONE of this applies — the
   hero falls back to the original centered text layout. Zero-risk
   for tenants who don't opt in. */

.hero--with-art {
    /* .hero already has position:relative + overflow:hidden; no change */
}
.hero--with-art::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: var(--hero-art-url);
    background-position: var(--hero-art-position, right center);
    background-repeat: no-repeat;
    background-size: var(--hero-art-size, contain);
    opacity: var(--hero-art-opacity, 0.92);
    pointer-events: none;
    z-index: 0;
    /* Radial-gradient mask: fades the image into the bg on ALL sides
       (not just the left edge facing the text). Centered slightly off
       to the right (78%/50%) since the image is anchored right-center
       via background-position. The ellipse keeps the image fully
       visible at the focal point with a soft falloff at all 4 edges -
       no hard crop where the image meets the hero boundary on top,
       right, or bottom. Tenants can override via --hero-art-mask in
       their inline style if they want a different falloff shape.
       Tuned for the typical case (square brand art contained right);
       cover-mode overrides this below with a different shape. */
    -webkit-mask-image: var(--hero-art-mask,
        radial-gradient(ellipse 50% 65% at 78% 50%,
            black 40%,
            rgba(0, 0, 0, 0.75) 65%,
            transparent 95%));
            mask-image: var(--hero-art-mask,
        radial-gradient(ellipse 50% 65% at 78% 50%,
            black 40%,
            rgba(0, 0, 0, 0.75) 65%,
            transparent 95%));
}
.hero--with-art .hero-inner {
    /* Hero content sits ABOVE the bg image */
    position: relative;
    z-index: 2;
    /* Constrain text width so it stays on the left side of the hero
       and doesn't visually run under the image on wide screens. Only
       applies in contain mode; cover mode keeps the full width since
       the image is full-bleed underneath anyway. */
}

/* ── Text readability over background art ───────────────────────────────
   When the hero has a background image, text can collide with busy areas
   of the artwork (dark patches, saturated colors). The classic
   bullet-proof fix is a stacked text-shadow halo: multiple identical
   shadows at low opacity compound into a clean halo around each glyph
   without harsh edges. Universally supported (no flags, no JS, works
   since IE10). The halo color is var(--bg) so it auto-adapts to light
   vs. dark mode without conditional CSS.
   The 1px solid offsets give a crisp 1px outline (the "outlined text"
   look); the blurred 8/16px shadows soften the outline into a halo so
   it doesn't read as a hard sticker stroke. */
.hero--with-art .hero-eyebrow,
.hero--with-art .hero-title,
.hero--with-art .hero-title-brand,
.hero--with-art .hero-title-descriptor,
.hero--with-art .hero-sub {
    text-shadow:
        -1px -1px 0 var(--bg),
         1px -1px 0 var(--bg),
        -1px  1px 0 var(--bg),
         1px  1px 0 var(--bg),
         0    0   8px var(--bg),
         0    0   8px var(--bg),
         0    0  16px var(--bg);
}
/* The accent-gradient tagline normally uses background-clip:text with
   a gradient fill. That technique reads beautifully over a solid bg,
   but over busy hero artwork the gradient gets visually swallowed by
   whatever's behind it (text fill is essentially transparent through
   the gradient). So in .hero--with-art context, we override back to a
   solid accent color - lets the same text-shadow halo we apply
   everywhere else actually do its job. */
.hero--with-art .hero-title .accent-gradient,
.hero--with-art .hero-title-tagline {
    /* Defeat the bg-clip:text gradient by making the text fill solid */
    background: none;
    -webkit-background-clip: initial;
            background-clip: initial;
    -webkit-text-fill-color: initial;
    color: var(--accent);
    text-shadow:
        -1px -1px 0 var(--bg),
         1px -1px 0 var(--bg),
        -1px  1px 0 var(--bg),
         1px  1px 0 var(--bg),
         0    0   8px var(--bg),
         0    0   8px var(--bg),
         0    0  16px var(--bg);
}

/* ── Per-tenant opt-out: hero text-shadow halo ──────────────────────────
   When .hero--with-art is combined with .hero--no-text-shadow (emitted
   by index.php when config_json.hero_disable_text_shadow = true), the
   stacked text-shadow halos above are suppressed. Use when the hero
   image is gentle / low-contrast enough that the halo reads as
   over-stroked or muddy. Operator caught on cards2go 2026-05-24: the
   halo made the brand wordmark look like a poster outline rather than
   clean typography. Default behavior unchanged for every other tenant.
   The HQ toggle lives in /hq/brand-assets.php on the Hero image panel,
   posting to /hq/api/save-hero-text-shadow.php. */
.hero--with-art.hero--no-text-shadow .hero-eyebrow,
.hero--with-art.hero--no-text-shadow .hero-title,
.hero--with-art.hero--no-text-shadow .hero-title-brand,
.hero--with-art.hero--no-text-shadow .hero-title-descriptor,
.hero--with-art.hero--no-text-shadow .hero-sub,
.hero--with-art.hero--no-text-shadow .hero-title .accent-gradient,
.hero--with-art.hero--no-text-shadow .hero-title-tagline {
    text-shadow: none;
}

/* ── Transparent CTAs over hero artwork ─────────────────────────────────
   When the hero has a background image, .btn-outline + .btn-ghost have
   transparent backgrounds by default — their labels can vanish against
   busy regions of the artwork. Operator-reported 2026-05-16: "See Events"
   (btn-outline) was unreadable on the Gastly hero. The fix is a thin
   glass-plate fill (78% of --bg, so it adapts to light/dark theme without
   conditional CSS) plus a backdrop-filter blur+saturate to soften and
   enrich whatever pixels sit behind the button. The plate stays
   translucent so the button still reads as "secondary action" rather
   than promoting itself to a primary-looking solid pill.
   Solid-fill buttons (.btn-primary, .btn-gold, .btn-discord) already
   carry their own opaque backgrounds and are intentionally left alone —
   they need to keep their brand color, not get tinted by a plate.
   backdrop-filter has 95%+ browser coverage as of 2026; the unprefixed
   -webkit- form is kept for older Safari versions still in the wild. */
.hero--with-art .btn-outline,
.hero--with-art .btn-ghost {
    background: color-mix(in srgb, var(--bg) 78%, transparent);
    -webkit-backdrop-filter: blur(6px) saturate(140%);
            backdrop-filter: blur(6px) saturate(140%);
}
.hero--with-art .btn-outline:hover,
.hero--with-art .btn-ghost:hover {
    background: color-mix(in srgb, var(--bg) 92%, transparent);
}
.hero--art-contain .hero-inner {
    max-width: 880px;
    margin-left: max(24px, calc((100% - 1200px) / 2));
    margin-right: auto;
}
/* Mobile (≤900px) — image fills the FULL hero height anchored to
   the right side. Acts as a brand background behind the text; the
   text content keeps its original layout and is NOT displaced. The
   left portion of the artwork may be cropped off-canvas (that's
   intentional - the focal subject for character-art logos sits on
   one half, so right-anchoring keeps the brand visible while the
   text-side stays readable).
   Operator request 2026-05-16: image is a branding element only,
   not a centerpiece that pushes content. */
@media (max-width: 900px) {
    .hero--art-contain .hero-inner {
        /* Drop the desktop content-constraint so text fills width
           again on mobile; do NOT add padding shift - the image is
           a backdrop, not a layout element. */
        max-width: none;
        margin: 0 auto;
    }
    .hero--with-art::after {
        /* Full hero height, right-anchored; auto width preserves
           aspect (image extends past left edge - clipped by hero
           overflow:hidden). */
        background-position: var(--hero-art-position-mobile, right center);
        background-size: var(--hero-art-size-mobile, auto 100%);
        opacity: var(--hero-art-opacity-mobile, 0.55);
        /* Right-anchored radial mask: image fully visible on the
           right where the focal art sits; fades leftward so the
           text overlay (left/center of the hero) stays readable
           without the text needing aggressive halos. Also softens
           top/bottom edges so the image never meets the hero
           boundary with a hard line. */
        -webkit-mask-image: var(--hero-art-mask-mobile,
            radial-gradient(ellipse 90% 90% at 85% 50%,
                black 30%,
                rgba(0, 0, 0, 0.65) 60%,
                transparent 100%));
                mask-image: var(--hero-art-mask-mobile,
            radial-gradient(ellipse 90% 90% at 85% 50%,
                black 30%,
                rgba(0, 0, 0, 0.65) 60%,
                transparent 100%));
    }
    /* Cover mode keeps full-bleed scrim behavior - already designed
       to fill the entire hero at low opacity, no changes needed. */
    .hero--art-cover.hero--with-art::after {
        background-size: cover;
        background-position: center;
        opacity: 0.32;
        -webkit-mask-image: none;
                mask-image: none;
    }
    /* Feature mode deliberately has NO mobile-specific overrides — the
       desktop rules (center + cover + opacity:1 + no mask) apply at every
       breakpoint so the hero looks consistent across desktop and mobile.
       Operator request 2026-05-16: "keep the hero image roughly in the
       same format regardless so it looks the same on desktop as it will
       on mobile." Mobile padding/sizing on the text container is handled
       by the base `.hero-inner` mobile rule (≈line 2344) — we let that
       cascade through unchanged. */
}

/* ── Feature mode (.hero--art-feature) ──────────────────────────────────
   The image IS the focal artwork — centered both axes, sized to cover so
   its edges reach the hero boundary, no radial mask. Use the upload-time
   "Advanced — edge fade" controls in /hq/brand-assets.php if you need an
   edge to dissolve into the page background; keeping the CSS mask-free
   means whatever fade the operator baked into the upload is what shows.

   Text container intentionally falls through to the base `.hero-inner`
   rule (max-width:1200px, centered, standard padding) — same content
   container as the rest of the page so the title doesn't stretch
   edge-to-edge over the artwork. Operator request 2026-05-16: "we don't
   want the content going to the edge of the screen after its past the
   margin." NO `.hero--art-feature .hero-inner` override here; cascade
   does the right thing.

   Replaces the right-anchored "contain" mode as the default for new
   tenants since 2026-05-16. The original right-anchor was a first-tenant
   special case (character art with all the focal mass on one side); for
   general hero artwork the centered + zoomed presentation reads better
   without cropping the subject. The legacy .hero--art-contain rules are
   preserved untouched above so any tenant explicitly opted into that
   mode keeps its current behavior. */
.hero--art-feature::after {
    /* Constrain horizontally to the standard 1200px content area so the
       image fills the same column as the rest of the page (matches the
       .container max-width). Outside that column we let the .hero's
       full-bleed background pattern show through. The base
       .hero--with-art::after sets `inset: 0`; we only override left+right
       so top/bottom stay at 0 and the image still fills the hero
       vertically. Operator request 2026-05-16: "lets not streak it to
       the edge of the screen - but to fill the content area of the page". */
    left:  max(0px, calc((100% - 1200px) / 2));
    right: max(0px, calc((100% - 1200px) / 2));
    background-position: var(--hero-art-position, center center);
    background-size:     var(--hero-art-size,     cover);
    opacity:             var(--hero-art-opacity,  1);
    -webkit-mask-image:  var(--hero-art-mask, none);
            mask-image:  var(--hero-art-mask, none);
}

.hero-games {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    padding-top: 28px;
    border-top: 1px solid var(--border);
}

.hero-games-label {
    font-size: 0.7rem;
    font-family: var(--font-head);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--dim);
    margin-right: 4px;
}

/* ------------------------------------------------------------
   8. Game Badges
   ------------------------------------------------------------ */
.game-badge {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-head);
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    border-radius: 4px;
    padding: 2px 8px;
    border: 2px solid var(--gc, var(--border));
    color: var(--gc, var(--muted));
    line-height: 1.5;
    white-space: nowrap;
}

.game-badge.mtg      { --gc: var(--c-mtg); }
.game-badge.pokemon  { --gc: var(--c-pokemon); }
.game-badge.onepiece { --gc: var(--c-onepiece); }
.game-badge.lorcana  { --gc: var(--c-lorcana); }
.game-badge.yugioh   { --gc: var(--c-yugioh); }
.game-badge.dbs      { --gc: var(--c-dbs); }
.game-badge.fab      { --gc: var(--c-fab); }

/* ------------------------------------------------------------
   9. Game Filter Strip
   ------------------------------------------------------------ */
.game-filter {
    display: flex;
    align-items: center;
    gap: 8px;
    overflow-x: auto;
    padding: 4px 0 8px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.game-filter::-webkit-scrollbar {
    display: none;
}

.game-filter-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-head);
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 6px 14px;
    border-radius: 5px;
    border: 1px solid var(--border);
    background: var(--bg2);
    color: var(--muted);
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
    text-decoration: none;
}

.game-filter-btn:hover {
    border-color: var(--border2);
    color: var(--text);
    background: var(--bg3);
}

.game-filter-btn.all-btn {
    border-color: var(--accent);
    color: var(--accent);
}

.game-filter-btn.all-btn.active,
.game-filter-btn.all-btn:hover {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}

.game-filter-btn[data-game].active {
    color: #fff;
}

.game-filter-btn[data-game="mtg"].active      { background: var(--c-mtg);      border-color: var(--c-mtg);      color: #1a1000; }
.game-filter-btn[data-game="pokemon"].active   { background: var(--c-pokemon);  border-color: var(--c-pokemon);  color: #fff; }
.game-filter-btn[data-game="onepiece"].active  { background: var(--c-onepiece); border-color: var(--c-onepiece); color: #fff; }
.game-filter-btn[data-game="lorcana"].active   { background: var(--c-lorcana);  border-color: var(--c-lorcana);  color: #fff; }
.game-filter-btn[data-game="yugioh"].active    { background: var(--c-yugioh);   border-color: var(--c-yugioh);   color: #fff; }
.game-filter-btn[data-game="dbs"].active       { background: var(--c-dbs);      border-color: var(--c-dbs);      color: #fff; }
.game-filter-btn[data-game="fab"].active       { background: var(--c-fab);      border-color: var(--c-fab);      color: #1a0800; }

/* Dot indicator on filter btn */
.game-filter-btn .filter-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.7;
    flex-shrink: 0;
}

/* ------------------------------------------------------------
   10. Section Headers
   ------------------------------------------------------------ */
.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}

.section-title {
    font-family: var(--font-head);
    font-size: 1.6rem;
    font-weight: 600;
    color: var(--text);
    letter-spacing: 0.02em;
    padding-left: 14px;
    border-left: 4px solid var(--accent);
    line-height: 1.2;
}

.section-subtitle {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 3px;
    font-family: var(--font-body);
    font-weight: 400;
    letter-spacing: 0;
}

.section-link {
    font-size: 0.85rem;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 4px;
    transition: color 0.15s;
    flex-shrink: 0;
}

.section-link:hover {
    color: var(--accent);
}

/* ------------------------------------------------------------
   11. Product Cards
   ------------------------------------------------------------ */
.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 14px;
}

.product-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s;
    display: flex;
    flex-direction: column;
}

.product-card:hover {
    border-color: var(--border2);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
}

.foil-card {
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--bg2) 92%, var(--accent)),
        color-mix(in srgb, var(--bg2) 92%, var(--accent2))
    );
    box-shadow: 0 0 20px rgba(220, 38, 38, 0.1);
    border-color: rgba(220, 38, 38, 0.2);
}

.foil-card:hover {
    box-shadow: 0 8px 30px rgba(220, 38, 38, 0.2);
    border-color: rgba(220, 38, 38, 0.35);
}

/* Card image placeholder */
.card-img {
    aspect-ratio: 5 / 7;
    height: auto;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
    cursor: zoom-in;
}

.card-img-inner {
    position: absolute;
    inset: 0;
    background: linear-gradient(160deg,
        color-mix(in srgb, var(--bg3) 65%, var(--gc, #333)) 0%,
        var(--bg3) 60%,
        color-mix(in srgb, var(--bg3) 80%, var(--gc, #333)) 100%
    );
}

/* Actual card photo - shown when image_url is present */
.card-img-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 6px;
    z-index: 1;
    transition: transform 0.25s ease;
    border-radius: 2px;
}
.product-card:hover .card-img-photo {
    transform: scale(1.07);
}

.set-code {
    position: relative;
    z-index: 1;
    font-family: var(--font-head);
    font-size: 3rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: rgba(0, 0, 0, 0.06);
    user-select: none;
    text-transform: uppercase;
}

.foil-card .card-img-inner {
    background: linear-gradient(160deg,
        rgba(220, 38, 38, 0.15) 0%,
        var(--bg3) 50%,
        rgba(245, 158, 11, 0.12) 100%
    );
}

.foil-sheen {
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg,
        transparent 30%,
        rgba(255, 255, 255, 0.03) 50%,
        transparent 70%
    );
    z-index: 2;
}

.foil-indicator {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    color: var(--accent);
    opacity: 0.8;
}

/* Card body */
.card-body {
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;
}

.card-game {
    /* game badge already styled */
}

.card-name {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text);
    line-height: 1.3;
}

.card-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.card-set {
    font-family: var(--font-head);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--dim);
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 1px 6px;
}

.card-rarity {
    font-size: 0.68rem;
    color: var(--muted);
    font-style: italic;
}

/* Condition badge */
.cond {
    font-family: var(--font-head);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 2px 7px;
    border-radius: 3px;
    background: var(--bg3);
    border: 1px solid var(--border);
    color: var(--muted);
}

.cond-nm {
    color: var(--green);
    border-color: rgba(34, 197, 94, 0.3);
    background: rgba(34, 197, 94, 0.08);
}

.cond-lp {
    color: var(--amber);
    border-color: rgba(245, 158, 11, 0.3);
    background: rgba(245, 158, 11, 0.08);
}

.cond-mp {
    color: var(--red);
    border-color: rgba(239, 68, 68, 0.3);
    background: rgba(239, 68, 68, 0.08);
}

/* Card footer */
.card-footer {
    padding: 12px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-top: 1px solid var(--border);
    gap: 10px;
    margin-top: auto;
}

.card-price {
    font-family: var(--font-head);
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--gold);
    letter-spacing: 0.02em;
    line-height: 1;
    white-space: nowrap;
}

/* Currency-symbol pseudo-element. This was added when card prices were bare
   number strings ("1.95"). singles.php now renders prices via
   currency_format_with_alts(), which already includes the $ symbol inside
   a .ce-price span - so for those cards we'd be double-stamping ($ $1.95).
   The :not(:has(.ce-price)) opt-out keeps the prefix for the remaining
   bare-number paths (index.php, shop.php, the second card-price block in
   singles.php) without touching them, and naturally retires itself as those
   paths migrate to currency_format_with_alts(). */
.card-price:not(:has(.ce-price))::before {
    content: '$';
    font-size: 0.85em;
    vertical-align: top;
    margin-top: 2px;
    margin-right: 1px;        /* tighten the inline-block gap so "$" sits next to digits */
    display: inline-block;
    opacity: 0.7;
}

/* Mobile: 1.5rem prices were crowding the 2-column phone card grid (~140px
   per card). Drop digit size + remove the $-symbol inline-block gap so
   "$6.50" stays inside the card instead of overflowing. Operator report
   2026-05-07: cardceus homepage prices were falling off the right edge. */
@media (max-width: 600px) {
    .card-price {
        font-size: 1.05rem;   /* was 1.5rem */
        letter-spacing: 0;    /* drop the 0.02em that adds up across 4-6 chars */
    }
    .card-price:not(:has(.ce-price))::before {
        font-size: 0.78em;
        margin-top: 1px;
        margin-right: 0;
    }
}

/* Stock badges */
.stock-badge {
    font-family: var(--font-head);
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    padding: 3px 7px;
    border-radius: 3px;
    border: 1px solid currentColor;
    white-space: nowrap;
}

.stock-badge.in-stock {
    color: var(--green);
    background: rgba(34, 197, 94, 0.08);
}

.stock-badge.low-stock {
    color: var(--amber);
    background: rgba(245, 158, 11, 0.08);
}

.stock-badge.out-of-stock {
    color: var(--dim);
    border-color: var(--border);
    background: transparent;
}

/* ------------------------------------------------------------
   12. Event Cards
   ------------------------------------------------------------ */
.events-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 16px;
}

.event-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-left: 4px solid var(--gc, var(--accent));
    border-radius: var(--radius-lg);
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    transition: border-color 0.2s, box-shadow 0.15s, transform 0.15s;
}

.event-card:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
    transform: translateY(-1px);
}

.event-card[data-game="mtg"]      { --gc: var(--c-mtg); }
.event-card[data-game="pokemon"]  { --gc: var(--c-pokemon); }
.event-card[data-game="onepiece"] { --gc: var(--c-onepiece); }
.event-card[data-game="lorcana"]  { --gc: var(--c-lorcana); }
.event-card[data-game="yugioh"]   { --gc: var(--c-yugioh); }
.event-card[data-game="dbs"]      { --gc: var(--c-dbs); }
.event-card[data-game="fab"]      { --gc: var(--c-fab); }

.event-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.event-header-left {
    display: flex;
    flex-direction: column;
    /* Pin children to start so .game-badge etc. don't stretch to the full
       column width inside this column-flex container (operator report
       2026-05-09). The badge has its own inline-flex sizing; in a column
       flex the implicit 'align-items: stretch' was forcing it full-width. */
    align-items: flex-start;
    gap: 6px;
}

.event-name {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text);
    line-height: 1.3;
}

.event-type-badge {
    font-family: var(--font-head);
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 2px 7px;
    border-radius: 3px;
    align-self: flex-start;
}

.event-type-badge.recurring {
    color: var(--dim);
    background: var(--bg3);
    border: 1px solid var(--border);
}

.event-type-badge.one-off {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}

/* Event meta grid */
.event-meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.event-meta-item {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.82rem;
    color: var(--muted);
}

.event-meta-item svg {
    flex-shrink: 0;
    opacity: 0.6;
}

.event-meta-item strong {
    color: var(--text);
    font-weight: 500;
}

.event-prize {
    font-size: 0.82rem;
    color: var(--muted);
    font-style: italic;
    padding: 8px 10px;
    background: var(--bg3);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 7px;
}

.event-prize svg {
    flex-shrink: 0;
    color: var(--gold);
}

/* Event footer */
.event-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
    margin-top: auto;
}

.event-seats {
    font-size: 0.8rem;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 5px;
}

.event-seats.seats-low {
    color: var(--amber);
}

.event-seats.seats-open {
    color: var(--muted);
}

/* ------------------------------------------------------------
   13. Events Row (horizontal scroll on homepage)
   ------------------------------------------------------------ */
.events-row {
    position: relative;
}

.events-scroll {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 16px;
}

/* ------------------------------------------------------------
   14. Buylist Callout
   ------------------------------------------------------------ */
.buylist-cta {
    background: var(--bg2);
    border-top: 4px solid var(--accent);
    border-bottom: 1px solid var(--border);
    padding: 48px 0;
}

.buylist-cta-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 40px;
    flex-wrap: wrap;
}

.buylist-cta-content {
    flex: 1;
    min-width: 280px;
}

.buylist-cta-eyebrow {
    font-family: var(--font-head);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--accent);
    margin-bottom: 8px;
}

.buylist-cta-title {
    font-family: var(--font-head);
    font-size: 2.4rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1;
    margin-bottom: 10px;
}

.buylist-cta-text {
    font-size: 0.92rem;
    color: var(--muted);
    line-height: 1.65;
    max-width: 480px;
    margin-bottom: 16px;
}

.buylist-games {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 12px;
}

.buylist-cta-actions {
    display: flex;
    flex-shrink: 0;
    gap: 12px;
    flex-wrap: wrap;
}

/* ------------------------------------------------------------
   15. Page Hero (inner pages)
   ------------------------------------------------------------ */
.page-hero {
    background: var(--bg2);
    padding: 88px 0 40px;
    border-bottom: 1px solid var(--border);
}

.page-hero-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

.page-hero-eyebrow {
    font-family: var(--font-head);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--accent);
    margin-bottom: 8px;
}

.page-hero-title {
    font-family: var(--font-head);
    font-size: 3rem;
    font-weight: 700;
    color: var(--text);
    /* Was 1 - see .hero-title note. Same descender-clip risk. */
    line-height: 1.1;
    margin-bottom: 10px;
}

.page-hero-sub {
    font-size: 0.95rem;
    color: var(--muted);
    max-width: 560px;
    margin-bottom: 24px;
    line-height: 1.65;
}

/* ------------------------------------------------------------
   16. Policies Box
   ------------------------------------------------------------ */
.policies-box {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-left: 4px solid var(--muted);
    border-radius: var(--radius-lg);
    padding: 24px 28px;
}

.policies-box-title {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.policies-box p {
    font-size: 0.88rem;
    color: var(--muted);
    line-height: 1.7;
}

.policies-box a {
    color: var(--accent);
    text-decoration: underline;
}

/* ------------------------------------------------------------
   17. Price Disclaimer
   ------------------------------------------------------------ */
.price-disclaimer {
    font-size: 0.72rem;
    color: var(--dim);
    font-style: italic;
    margin-top: 16px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.price-disclaimer::before {
    content: '※';
    opacity: 0.5;
}

/* ------------------------------------------------------------
   18. Newsletter Form
   ------------------------------------------------------------ */
.newsletter-form {
    display: flex;
    gap: 8px;
    margin-top: 14px;
}

.newsletter-form input[type="email"] {
    flex: 1;
    min-width: 0;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    padding: 8px 14px;
    font-size: 0.88rem;
    transition: border-color 0.15s;
    outline: none;
}

.newsletter-form input[type="email"]::placeholder {
    color: var(--dim);
}

.newsletter-form input[type="email"]:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}

/* ------------------------------------------------------------
   19. Footer
   ------------------------------------------------------------ */
.site-footer {
    background: var(--bg2);
    border-top: 1px solid var(--border);
    padding: 48px 0 24px;
    margin-top: auto;
}

.footer-grid {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
    display: grid;
    grid-template-columns: 1.2fr 1fr 1.2fr;
    gap: 48px;
}

.footer-col-title {
    font-family: var(--font-head);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--dim);
    margin-bottom: 16px;
}

.footer-store-name {
    font-family: var(--font-head);
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 10px;
}

.footer-contact-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.footer-contact-item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--muted);
    line-height: 1.4;
}

.footer-contact-item svg {
    flex-shrink: 0;
    margin-top: 1px;
    opacity: 0.5;
}

.footer-contact-item a {
    color: var(--muted);
    transition: color 0.15s;
}

.footer-contact-item a:hover {
    color: var(--accent);
}

/* Hours table */
.hours-table {
    width: 100%;
    border-collapse: collapse;
}

.hours-table tr td {
    padding: 5px 0;
    font-size: 0.85rem;
}

/* Day column shrinks to fit its 3-letter label ("Mon", "Tue"...).
   Previously fixed at width: 55% which left the much longer time string
   ("8:30am - 5:30pm", ~15 chars) only 45% of the table and forced two-
   line wraps in narrow footer columns. Operator caught on bananahandles
   2026-05-23 (Mon/Wed/Fri 8:30am - 5:30pm wrapped). Now: day column
   sizes to content + small breathing pad; time column gets everything
   else and stays on a single line via nowrap. Falls back to natural
   wrap on truly cramped viewports (footer narrower than ~190px), but
   that's well below where typical footer columns end up. */
.hours-table tr td:first-child {
    color: var(--muted);
    width: 1%;
    white-space: nowrap;
    padding-right: 12px;
}

.hours-table tr td:last-child {
    color: var(--text);
    text-align: right;
    /* white-space: nowrap was tried 2026-05-23 for visual alignment but
       on narrow phone viewports (<340px footer col) it pushed the table
       wider than container → page-level horizontal overflow + a thin
       scrollbar-gutter on the right edge that wouldn't go away. The
       padding alignment fix on first-child still solves 99% of the
       original wrap problem (Mon vs Wednesday width balance); allowing
       a soft wrap in extreme-narrow cases is a cleaner failure mode
       than the gutter artifact. */
}

.hours-table tr + tr td {
    border-top: 1px solid var(--border);
}

/* Footer social list */
.footer-social-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.footer-social-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    transition: color 0.15s;
}

.footer-social-link svg { flex-shrink: 0; opacity: 0.7; }

.footer-social-link:hover { color: var(--text); }
.footer-social-link--ig:hover, .footer-social-link--ig:hover svg            { color: #e1306c; opacity: 1; }
/* Brand-color hover for the additional platforms tcg_social_links() emits.
   Each :hover only triggers if the helper produced that class for this
   tenant; tenants without that platform skip the rule entirely. */
.footer-social-link--fb:hover, .footer-social-link--fb:hover svg            { color: #1877f2; opacity: 1; }
.footer-social-link--tk:hover, .footer-social-link--tk:hover svg            { color: #ff0050; opacity: 1; }
.footer-social-link--twitch:hover, .footer-social-link--twitch:hover svg    { color: #9146ff; opacity: 1; }
.footer-social-link--whatnot:hover, .footer-social-link--whatnot:hover svg  { color: #ffec05; opacity: 1; }
.footer-social-link--tcgplayer:hover, .footer-social-link--tcgplayer:hover svg { color: #f04e23; opacity: 1; }
.footer-social-link--ebay:hover, .footer-social-link--ebay:hover svg        { color: #e53238; opacity: 1; }
.footer-social-link--discord:hover, .footer-social-link--discord:hover svg  { color: #5865f2; opacity: 1; }
.footer-social-link--yt:hover, .footer-social-link--yt:hover svg            { color: #ff0000; opacity: 1; }
.footer-social-link--x:hover, .footer-social-link--x:hover svg              { color: #ffffff; opacity: 1; }
.footer-social-link--yelp:hover, .footer-social-link--yelp:hover svg        { color: #d32323; opacity: 1; }
.footer-social-link--bsky:hover, .footer-social-link--bsky:hover svg        { color: #0085ff; opacity: 1; }

.footer-tagline {
    font-size: 0.78rem;
    color: var(--dim);
    font-style: italic;
}

/* Footer bottom */
.footer-bottom {
    max-width: 1200px;
    margin: 0 auto;
    padding: 16px 24px 0;
    border-top: 1px solid var(--border);
    margin-top: 40px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

/* Slim footer - used on pages that already show contact/hours/social in the body */
.site-footer--slim {
    padding: 0;
}
.site-footer--slim .footer-bottom {
    margin-top: 0;
    border-top: 1px solid var(--border);
    padding: 20px 24px;
}

.footer-copy {
    font-size: 0.78rem;
    color: var(--dim);
}

.footer-disclaimer,
.footer-tagline {
    font-size: 0.78rem;
    color: var(--dim);
    font-style: italic;
}

/* ------------------------------------------------------------
   20. Drop-in note / info banners
   ------------------------------------------------------------ */
.dropin-note {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    color: var(--muted);
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 6px 12px;
    margin-bottom: 16px;
}

.dropin-note::before {
    content: '✓';
    color: var(--green);
    font-weight: 700;
}

/* ------------------------------------------------------------
   21. Empty / hidden states for filter
   ------------------------------------------------------------ */
.event-card.hidden {
    display: none;
}

.no-results-msg {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--muted);
    font-size: 0.9rem;
    padding: 40px;
    display: none;
}

.no-results-msg.visible {
    display: block;
}

/* ------------------------------------------------------------
   21b. Adaptive header overflow (Priority Plus pattern)
   ------------------------------------------------------------
   Replaces the old "hide entire nav below 1440px" rule with progressive
   overflow handled by JS in inc/header.php. Operator request 2026-05-10:
   even at full 1920px desktop, tenants with 8+ nav items push the search
   and right-side icons off-screen. The new behavior:
     - Show as many nav items as fit, left-to-right.
     - Items that don't fit get .nav-overflow (display:none) and remain
       reachable via the existing hamburger drawer (drawer always carries
       the full nav list, so no markup duplication).
     - Hamburger shows whenever ANY item is hidden - desktop or mobile.
     - Search is the lowest-priority bar element; hidden first if needed.

   Below 480px the existing rules in section 22 take over (cart/account
   icons collapse into the drawer too). The 768px breakpoint formerly
   hid the nav block entirely; that's now JS-driven for any width.
*/
.main-nav a.nav-overflow,
.ss-wrap.nav-overflow {
    display: none !important;
}
.site-header.has-overflow .hamburger-btn {
    display: flex;
}

/* The standalone overlay-search-button is redundant once the drawer has
   its own search form. Hide it everywhere; drawer or inline input only. */
.header-search-btn { display: none !important; }

/* ------------------------------------------------------------
   22. Responsive - 768px
   ------------------------------------------------------------ */
@media (max-width: 768px) {
    /* Discord button collapses into the drawer on mobile */
    .header-discord-btn {
        display: none;
    }

    .product-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .events-grid {
        grid-template-columns: 1fr;
    }

    .events-scroll {
        grid-template-columns: 1fr;
    }

    .hero-title {
        font-size: 3.2rem;
    }

    .footer-grid {
        grid-template-columns: 1fr;
        gap: 32px;
    }

    .buylist-cta-inner {
        flex-direction: column;
        gap: 24px;
    }

    .buylist-cta-actions {
        flex-direction: row;
    }

    .event-meta {
        grid-template-columns: 1fr;
    }

    .page-hero-title {
        font-size: 2.2rem;
    }

    .footer-bottom {
        flex-wrap: wrap;
        gap: 6px;
    }
}

/* ------------------------------------------------------------
   23. Responsive - 480px
   ------------------------------------------------------------ */
@media (max-width: 480px) {
    /* Tagline below the brand wordmark drops off on phones - saves a row */
    .logo-sub {
        display: none;
    }
    /* Phone-only safety net: if the brand name still can't fit alongside
       the hamburger, allow it to wrap and shrink. Above 480px the brand
       stays single-line. */
    .logo {
        flex-shrink: 1;
        min-width: 0;
    }
    .logo-name {
        white-space: normal;
        overflow-wrap: break-word;
        font-size: 1.25rem; /* blackletter reads smaller per-pixel; bumped from 1.1rem */
    }
    /* Phone: only the brand + hamburger fit. Cart/account/theme drop into
       the drawer (mobile-nav-quick-actions block). Selectors are doubled
       up (.header-icon-btn.theme-toggle etc.) to beat the later base
       rules in source order. */
    .header-icon-btn.header-cart-btn,
    .header-icon-btn.header-account-btn,
    .header-icon-btn.theme-toggle,
    .theme-toggle.header-icon-btn {
        display: none;
    }

    .hero-title {
        font-size: 1.9rem;  /* tightened from 2.8rem so long brand names fit on 360px phones */
    }

    /* Hold the 2-column product grid down to phone widths so the New
       Arrivals strip stays scannable instead of becoming one tall column
       the user has to thumb-scroll through. The 768px breakpoint above
       already sets repeat(2, 1fr); we keep it here and just tighten the
       gap a touch so 320px-wide phones still get ~140px-per-card cards
       instead of crushed. Operator request 2026-05-07: "let's have them
       attempt to be at least two wide on mobile." */
    .product-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 10px;
    }

    .hero-actions {
        flex-direction: column;
    }

    .hero-actions .btn {
        justify-content: center;
    }

    .header-inner {
        padding: 0 16px;
    }

    .container {
        padding: 0 16px;
    }

    .hero-inner {
        padding: 90px 16px 60px;
    }

    .section {
        padding: 44px 0;
    }

    .buylist-cta-title {
        font-size: 2rem;
    }
}

/* ------------------------------------------------------------
   24. Scrollbar (webkit)
   ------------------------------------------------------------ */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: var(--bg);
}

::-webkit-scrollbar-thumb {
    background: var(--border2);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--muted);
}

/* ------------------------------------------------------------
   25. Focus visible accessibility
   ------------------------------------------------------------ */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ------------------------------------------------------------
   26. Selection
   ------------------------------------------------------------ */
::selection {
    background: rgba(220, 38, 38, 0.2);
    color: var(--text);
}

/* ------------------------------------------------------------
   21. Theme Toggle Button
   ------------------------------------------------------------ */
.theme-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius);
    color: var(--muted);
    background: none;
    border: none;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
    position: relative;
}

.theme-toggle:hover {
    color: var(--text);
    background: var(--bg3);
}

.theme-toggle .icon-sun,
.theme-toggle .icon-moon {
    position: absolute;
    transition: opacity 0.2s, transform 0.2s;
}

/* Light theme: show moon (to switch to dark) */
.theme-toggle .icon-sun  { opacity: 0; transform: scale(0.6) rotate(90deg); }
.theme-toggle .icon-moon { opacity: 1; transform: scale(1) rotate(0deg); }

/* Dark theme: show sun (to switch to light) */
[data-theme="dark"] .theme-toggle .icon-sun  { opacity: 1; transform: scale(1) rotate(0deg); }
[data-theme="dark"] .theme-toggle .icon-moon { opacity: 0; transform: scale(0.6) rotate(-90deg); }

/* ------------------------------------------------------------
   22. Dark Theme - Component Overrides
   ------------------------------------------------------------ */

/* Hero - dark dramatic.
   Operator request 2026-05-08 (round 2): this block was hardcoded to a slate
   navy gradient (#0f172a -> #1a2336) with bluish-gray (rgba(45,58,82)) grid
   stripes and a red radial glow - completely off-theme for any tenant whose
   brand isn't red+navy. Cardceus (gold/black) read as a navy page with a
   white-ish grid, which is what prompted the fix. Now drives every layer
   from the dark theme's own vars: bg/bg2 for the diagonal base gradient,
   --accent at low opacity for the radial top glow + grid stripes, --border
   as a fallback so the lines are still visible if the accent is too close
   to the background to be seen. Same treatment for .page-hero below. */
[data-theme="dark"] .hero {
    /* Hero glow is tunable per-tenant via CSS custom props with sensible
       defaults (centered just above the section, soft accent tint). Cardceus
       (operator request 2026-05-08) wants the glow originating from the
       upper-left where the Arceus head / C-mark sits in their lockup, so
       their config_json.theme.dark.hero_glow_* override these defaults via
       tcg_brand_inline_style(). Other dark-mode tenants keep the centered
       default. Ranges that work well: x 0-100%, y -30% to 30%, w 40-120%,
       h 30-100%, strength 5-30%. */
    background:
        radial-gradient(
            ellipse var(--hero-glow-w, 80%) var(--hero-glow-h, 50%)
            at var(--hero-glow-x, 50%) var(--hero-glow-y, -10%),
            color-mix(in srgb, var(--accent) var(--hero-glow-strength, 12%), transparent) 0%,
            transparent 60%
        ),
        repeating-linear-gradient(
            0deg,
            transparent,
            transparent 39px,
            color-mix(in srgb, var(--accent) 7%, transparent) 40px
        ),
        repeating-linear-gradient(
            90deg,
            transparent,
            transparent 39px,
            color-mix(in srgb, var(--accent) 7%, transparent) 40px
        ),
        linear-gradient(135deg, var(--bg) 0%, var(--bg2, var(--bg)) 50%, var(--bg) 100%);
}

/* Page hero - dark. Same theming treatment as the hero above. */
[data-theme="dark"] .page-hero {
    background:
        radial-gradient(ellipse 60% 80% at 20% -20%, color-mix(in srgb, var(--accent) 10%, transparent) 0%, transparent 55%),
        linear-gradient(180deg, var(--bg2, var(--bg)) 0%, var(--bg) 100%);
}

/* Dark text that should be white */
[data-theme="dark"] .hero-title       { color: #fff; }
[data-theme="dark"] .page-hero-title  { color: #fff; }
[data-theme="dark"] .buylist-cta-title{ color: #fff; }
[data-theme="dark"] .footer-store-name{ color: #fff; }

/* Dark logo - gradient from white to accent */
[data-theme="dark"] .logo-name {
    background: linear-gradient(90deg, #fff 60%, var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}
/* Same gradient on each child span when stacked - parent's bg-clip:text
   only clips to text in that exact element, so nested spans need their own. */
[data-theme="dark"] .logo-name--stacked .logo-name-brand,
[data-theme="dark"] .logo-name--stacked .logo-name-descriptor {
    background: linear-gradient(90deg, #fff 60%, var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* Dark nav active */
[data-theme="dark"] .main-nav a.active         { color: #fff; }
/* Dark drawer active: brand-accent fill consistent with light theme
   (see .mobile-nav-links a.active in section 1 for rationale). The
   pre-2026-05-16 rule pointed at --bg3 which was the same drawer
   surface color on dark mode too, so the active row vanished into
   the panel; brand accent restores the "I'm here" emphasis. */
[data-theme="dark"] .mobile-nav-links a.active { color: var(--btn-primary-text, #fff); background: var(--accent); }

/* Dark set code - white ghost.
   Was 0.07 (matched the light theme's 0.06 numerically) but white-on-dark
   reads visually heavier than black-on-light at equivalent opacities — the
   eye is more sensitive to bright values against dark bg. Operator caught
   on the singles grid (DMU, 151, EVS, FLOD codes too prominent). Halved
   to 0.035 which lands visually closer to the light theme's subtle
   watermark intent. */
[data-theme="dark"] .set-code { color: rgba(255, 255, 255, 0.035); }

/* Dark foil - more glow */
[data-theme="dark"] .foil-card {
    box-shadow: 0 0 20px rgba(220, 38, 38, 0.18);
    border-color: rgba(220, 38, 38, 0.3);
}
[data-theme="dark"] .foil-card:hover {
    box-shadow: 0 8px 30px rgba(220, 38, 38, 0.32);
    border-color: rgba(220, 38, 38, 0.5);
}
[data-theme="dark"] .foil-card .card-img-inner {
    background: linear-gradient(160deg,
        rgba(220, 38, 38, 0.2) 0%,
        var(--bg3) 50%,
        rgba(245, 158, 11, 0.15) 100%
    );
}

/* Dark card/event hover - deeper shadow */
[data-theme="dark"] .product-card:hover { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); }
[data-theme="dark"] .event-card:hover   { box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); }

/* Dark-theme component overrides for events.
   History:
     2026-05-10: bumped UP from var(--dim)/var(--muted) to #cbd5e1
                 (Tailwind slate-300) because the cardfellas card had the
                 badge + prize-pill text dropping out at <2:1 contrast on
                 dark theme — looked like empty UI.
     2026-05-24: walked the bump back. Operator caught the new color was
                 OVER-corrected — slate-300 reads as primary-text-bright,
                 making secondary metadata (RECURRING badge, prize hints)
                 compete with the event title for attention.

   Right level: text at ~55% white (clearly muted, ~5:1 contrast — passes
   AA but doesn't dominate), bg + border unchanged. Achieves the original
   readability fix without the visual over-weight. */
[data-theme="dark"] .event-type-badge.recurring {
    color: rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .event-prize {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.55);
}

/* Header nav: dark-mode color was --muted (#94a3b8) which read as plain
   undifferentiated text. Bump to a higher-contrast neutral + add a stronger
   hover lift so the nav reads as a navigation cluster, not body copy. */
[data-theme="dark"] .main-nav a {
    color: #cbd5e1;
}
/* Hover was pure white text on white-tinted pill, both too bright —
   the hovered item out-shouted the active item. 2026-05-24: muted text
   to 0.85 white, halved bg lift opacity. Still clearly hoverable, no
   longer demanding attention. Operator caught on CONTACT hover. */
[data-theme="dark"] .main-nav a:hover {
    color: rgba(255, 255, 255, 0.85);
    background: rgba(255, 255, 255, 0.03);
}

/* Mobile drawer + search — same over-bright issue from .var(--bg3) and
   .var(--text) cascading to high-contrast values on dark themes. */
[data-theme="dark"] .mobile-nav-links a {
    color: rgba(255, 255, 255, 0.65);
}
[data-theme="dark"] .mobile-nav-links a:hover {
    color: rgba(255, 255, 255, 0.9);
    background: rgba(255, 255, 255, 0.04);
}
[data-theme="dark"] .mobile-nav-search-wrap {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.10);
}
[data-theme="dark"] .mobile-nav-search-input::placeholder {
    color: rgba(255, 255, 255, 0.40);
}
[data-theme="dark"] .mobile-nav-search-submit:hover {
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.9);
}

/* Buylist tables + disclaimer: same low-contrast issue as the event-prize
   pill - operator hit it on cardfellas 2026-05-10. var(--bg3) is dark in
   dark mode but tenants whose own --bg3 collides with their --bg get a
   washed-out look. Explicit white-tinted lift makes the table headers and
   disclaimer pop reliably regardless of tenant brand vars. */
[data-theme="dark"] .buylist-rate-table th {
    background: rgba(255, 255, 255, 0.05);
    color: #cbd5e1;
    border-bottom-color: rgba(255, 255, 255, 0.10);
}
[data-theme="dark"] .buylist-rate-table td {
    border-bottom-color: rgba(255, 255, 255, 0.06);
}
[data-theme="dark"] .buylist-disclaimer {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] .buylist-disclaimer p {
    color: #cbd5e1;
}
[data-theme="dark"] .buylist-table-header {
    background: rgba(255, 255, 255, 0.03);
    border-bottom-color: rgba(255, 255, 255, 0.08);
}

/* Dark header - no box-shadow, let border do the work */
[data-theme="dark"] .site-header { box-shadow: none; }

/* Light header - subtle lift */
.site-header { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); }

/* .site-header--hidden was for the hide-on-scroll feature reverted
   2026-05-23 — kept the empty rule slot here as a comment marker. */

/* ============================================================
   23. Shop Catalog Page
   ============================================================ */

/* --- Shop hero --- */
.shop-hero {
    background: var(--bg2);
    border-bottom: 1px solid var(--border);
    padding: 88px 0 32px;
}
.shop-hero-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
}
.shop-hero-title {
    font-family: var(--font-head);
    font-size: 2rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text);
    margin: 0 0 4px;
    line-height: 1.1;
}
.shop-hero-sub {
    font-size: 0.9rem;
    color: var(--muted);
    margin: 0;
}
.shop-hero-search {
    flex: 0 1 360px;
    min-width: 240px;
}
.shop-search-wrap {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--bg);
    transition: border-color 0.15s;
}
.shop-search-wrap:focus-within {
    border-color: var(--accent);
}
.shop-search-input {
    flex: 1;
    background: transparent;
    border: none;
    outline: none;
    padding: 9px 12px;
    font-family: var(--font-body);
    font-size: 0.9rem;
    color: var(--text);
}
.shop-search-input::placeholder {
    color: var(--dim);
}
.shop-search-btn {
    background: var(--accent);
    border: none;
    padding: 9px 14px;
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    transition: background 0.15s;
}
.shop-search-btn:hover {
    background: #b91c1c;
}

/* --- Filter bar --- */
/* Sticky offset is bound to --header-h, which inc/header.php measures from the
   actual rendered header on load + resize + after web fonts swap. Hardcoded
   "top: 70px" used to leave a gap on mobile (where the long brand wraps to
   2 lines and the real header is ~90-110px tall) - product cards above the
   filter bar would peek through that gap as the user scrolled. */
.shop-filter-bar {
    position: sticky;
    top: var(--header-h, 70px);
    z-index: 90;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: 12px 0;
}
.shop-filter-bar .container {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Light mode: add subtle lift */
.shop-filter-bar {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] .shop-filter-bar {
    box-shadow: none;
}

/* --- Filter pill rows --- */
.filter-pill-row {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    padding: 4px 0;
    scrollbar-width: none;
}
.filter-pill-row::-webkit-scrollbar {
    display: none;
}
.filter-pill {
    font-family: var(--font-head);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 5px 12px;
    border-radius: 4px;
    border: 1px solid var(--border);
    background: var(--bg2);
    color: var(--muted);
    white-space: nowrap;
    text-decoration: none;
    transition: all 0.15s;
    cursor: pointer;
}
.filter-pill:hover {
    background: color-mix(in srgb, var(--accent) 60%, var(--bg2));
    border-color: var(--accent);
    color: #fff;
}
/* Active pill needs a stronger visual signal than hover. The chip stays
   filled with the accent color AND adds a thicker border + an inset ring
   so the operator can spot the selected category at a glance, even when
   the row scrolls horizontally on phones. Operator report 2026-05-08:
   "whenever I poke one of the categories it isn't visible after we poke
   it so you don't really know what area you're on after that page loads." */
.filter-pill.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--btn-primary-text, #fff);
    font-weight: 800;
    box-shadow: inset 0 0 0 2px var(--bg2),
                inset 0 0 0 3px var(--accent),
                0 2px 8px color-mix(in srgb, var(--accent) 40%, transparent);
    /* Provide a scroll-snap target on the row so the auto-scroll-to-active
       JS in shop.php has something to snap into the centered position. */
    scroll-snap-align: center;
    scroll-margin-inline: 24px;
}

/* --- Sort row --- */
.filter-sort-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}
.filter-count {
    font-family: var(--font-body);
    font-size: 0.8rem;
    color: var(--muted);
    white-space: nowrap;
}
.filter-sort-right {
    display: flex;
    align-items: center;
    gap: 8px;
}
.filter-sort-form {
    display: flex;
    align-items: center;
    gap: 6px;
}
.filter-sort-label {
    font-family: var(--font-head);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    white-space: nowrap;
}
.filter-sort-select {
    background: var(--bg2);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 6px 10px;
    border-radius: var(--radius);
    font-family: var(--font-head);
    font-size: 0.85rem;
    cursor: pointer;
    transition: border-color 0.15s;
}
.filter-sort-select:focus {
    outline: none;
    border-color: var(--accent);
}
.filter-clear {
    font-family: var(--font-body);
    font-size: 0.8rem;
    color: var(--muted);
    text-decoration: none;
    transition: color 0.15s;
}
.filter-clear:hover {
    color: var(--accent);
}

/* --- Shop main / grid --- */
.shop-main {
    padding: 32px 0 64px;
}
.shop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: 10px;
}

/* --- Shop card --- */
.shop-card {
    display: flex;
    flex-direction: column;
    cursor: default;
    transition: transform 0.18s, box-shadow 0.18s, border-color 0.18s;
}
.shop-card-img {
    aspect-ratio: 5 / 7;
    height: auto;
    overflow: hidden;
    background: var(--bg3);
    position: relative;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.shop-card-img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 6px;
    transition: transform 0.2s;
}
.shop-card:hover .shop-card-img img {
    transform: scale(1.04);
}
.shop-card-img-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--bg3), var(--bg4));
}
.placeholder-type {
    font-family: var(--font-head);
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--dim);
}

/* --- Era badges --- */
.era-badge {
    display: inline-block;
    font-family: var(--font-head);
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 2px 7px;
    border-radius: 3px;
}
.era-wotc {
    color: #a16207;
    background: rgba(161, 98, 7, 0.1);
    border: 1px solid rgba(161, 98, 7, 0.3);
}
.era-neo {
    color: #a16207;
    background: rgba(161, 98, 7, 0.1);
    border: 1px solid rgba(161, 98, 7, 0.3);
}
.era-e {
    color: #7c3aed;
    background: rgba(124, 58, 237, 0.1);
    border: 1px solid rgba(124, 58, 237, 0.3);
}
.era-ex-era {
    color: #7c3aed;
    background: rgba(124, 58, 237, 0.1);
    border: 1px solid rgba(124, 58, 237, 0.3);
}
.era-dp {
    color: #1d4ed8;
    background: rgba(29, 78, 216, 0.1);
    border: 1px solid rgba(29, 78, 216, 0.3);
}
.era-hgss {
    color: #0369a1;
    background: rgba(3, 105, 161, 0.1);
    border: 1px solid rgba(3, 105, 161, 0.3);
}
.era-bw {
    color: #374151;
    background: rgba(55, 65, 81, 0.1);
    border: 1px solid rgba(55, 65, 81, 0.3);
}
.era-xy {
    color: #15803d;
    background: rgba(21, 128, 61, 0.1);
    border: 1px solid rgba(21, 128, 61, 0.3);
}
.era-sm {
    color: #b45309;
    background: rgba(180, 83, 9, 0.1);
    border: 1px solid rgba(180, 83, 9, 0.3);
}
.era-swsh {
    color: #dc2626;
    background: rgba(220, 38, 38, 0.1);
    border: 1px solid rgba(220, 38, 38, 0.3);
}
.era-sv {
    color: #9333ea;
    background: rgba(147, 51, 234, 0.1);
    border: 1px solid rgba(147, 51, 234, 0.3);
}
.era-japanese {
    color: #be123c;
    background: rgba(190, 18, 60, 0.1);
    border: 1px solid rgba(190, 18, 60, 0.3);
}
.era-chinese {
    color: #c2410c;
    background: rgba(194, 65, 12, 0.1);
    border: 1px solid rgba(194, 65, 12, 0.3);
}
.era-other {
    color: #4b5563;
    background: rgba(75, 85, 99, 0.1);
    border: 1px solid rgba(75, 85, 99, 0.3);
}

/* --- Shop card body / footer --- */
.shop-card-body {
    padding: 8px 9px 6px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1;
}
.shop-card-era {
    display: flex;
}
.shop-card-name {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text);
    line-height: 1.25;
    /* keep long names from blowing up narrow cards */
    overflow-wrap: break-word;
}
.shop-card-type {
    font-size: 0.68rem;
    color: var(--muted);
    font-family: var(--font-body);
}
.shop-card-footer {
    padding: 5px 9px;
    border-top: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
    /* Breathing room between price + Add button. With cards at the 130-180px
       grid floor and the price at 1.15rem (see override below) the two
       elements now have room to coexist instead of visually colliding
       (operator screenshot 2026-05-12). flex-wrap lets the button drop to
       a second line on the very narrowest cards. */
    gap: 8px;
    flex-wrap: wrap;
}
/* Shrink the global .card-price (1.5rem) specifically inside the shop
   grid. The 1.5rem looks good on standalone card pages and in cart/
   checkout, but in a 130-180px-wide card cell it dominates the footer
   and crowds the Add button. Keep at ~1.15rem here. */
.shop-card .card-price {
    font-size: 1.15rem;
    line-height: 1.05;
}
.shop-card-toast {
    font-size: 0.65rem;
    color: var(--green);
    font-weight: 600;
    font-family: var(--font-head);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

/* --- Pagination --- */
.pagination {
    display: flex;
    gap: 4px;
    justify-content: center;
    margin-top: 40px;
    flex-wrap: wrap;
}
.page-btn {
    font-family: var(--font-head);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 5px 12px;
    border-radius: 4px;
    border: 1px solid var(--border);
    background: var(--bg2);
    color: var(--muted);
    white-space: nowrap;
    text-decoration: none;
    transition: all 0.15s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    cursor: pointer;
}
.page-btn:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.page-btn.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    cursor: default;
}
.page-btn.disabled {
    opacity: 0.4;
    pointer-events: none;
    cursor: default;
}

/* --- Empty state --- */
.catalog-empty {
    text-align: center;
    padding: 80px 24px;
    color: var(--muted);
}
.catalog-empty-msg {
    font-size: 1.05rem;
    margin: 0 0 20px;
}

/* --- Dark mode adjustments for shop --- */
[data-theme="dark"] .shop-hero {
    background: var(--bg2);
}
[data-theme="dark"] .era-bw {
    color: #9ca3af;
    background: rgba(156, 163, 175, 0.1);
    border: 1px solid rgba(156, 163, 175, 0.3);
}
[data-theme="dark"] .era-other {
    color: #9ca3af;
    background: rgba(156, 163, 175, 0.1);
    border: 1px solid rgba(156, 163, 175, 0.3);
}

/* --- Responsive --- */
@media (max-width: 640px) {
    /* Mobile header height: --header-h is 56px on mobile, but .site-header
       has a desktop-default min-height of 70px. Without this override the
       rendered header is 70px tall while body padding-top is only 56px,
       sliding 14px of content under the header. */
    .site-header { min-height: 56px; }

    /* Secondary page-hero top-padding trim. Operator request 2026-05-16:
       "we need to trim up that empty space at the top — on mobile that
       is definitely not entirely necessary." The .shop-hero / .page-hero
       / .careers-hero / .buylist-hero / .singles-hero rules all carry
       72–100px top padding tuned for desktop airiness; on a 56px-header
       mobile viewport that's a chunk of dead space above the title
       before any content shows. 24px gives the title a clean breath
       below the header without burning a thumb-scroll of nothing.
       Bottom + side padding inherit from each rule's desktop value
       since those don't suffer the same "above-the-fold" cost. */
    .shop-hero,
    .page-hero,
    .careers-hero,
    .buylist-hero,
    .singles-hero {
        padding-top: 24px;
    }

    .shop-hero-inner {
        flex-direction: column;
        align-items: flex-start;
    }
    .shop-hero-search {
        width: 100%;
        flex: none;
    }
    .filter-sort-row {
        gap: 8px;
    }
    .shop-grid {
        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
        gap: 12px;
    }
    .shop-card-img {
        height: 160px;
    }
}

/* ============================================================
   CAREERS PAGE
   ============================================================ */

/* ============================================================
   LIGHTBOX
   ============================================================ */

.lb-trigger {
    cursor: zoom-in;
    position: relative;
}
.lb-trigger::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    transition: background 0.15s;
}
.lb-trigger:hover::after {
    background: rgba(255, 255, 255, 0.06);
}

.lb-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: rgba(5, 10, 20, 0.94);
    align-items: center;
    justify-content: center;
    padding: 60px 24px max(20px, env(safe-area-inset-bottom, 20px));
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    overflow-y: auto;
}
.lb-overlay.open {
    display: flex;
    animation: lbFadeIn 0.18s ease;
}
@keyframes lbFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.lb-close {
    position: absolute;
    top: 14px;
    right: 14px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.14);
    color: rgba(255, 255, 255, 0.85);
    border-radius: 10px;
    width: 42px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    flex-shrink: 0;
}
.lb-close:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: rgba(255, 255, 255, 0.28);
}

/* Body: image + caption as one tight unit, centered in the overlay */
.lb-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    max-height: 100%;
}

.lb-stage {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.lb-img {
    max-width:  min(640px, calc(100vw - 48px));
    max-height: min(calc(100svh - 180px), calc(100dvh - 180px));
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 10px;
    box-shadow: 0 16px 64px rgba(0, 0, 0, 0.6);
    animation: lbImgIn 0.22s ease;
    transform-origin: center center;
    will-change: transform;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
    cursor: zoom-in;
}
.lb-img.lb-zoomed {
    cursor: grab;
    border-radius: 4px;
}
.lb-img.lb-zoomed:active {
    cursor: grabbing;
}
@keyframes lbImgIn {
    from { opacity: 0; transform: scale(0.93); }
    to   { opacity: 1; transform: none; }
}

.lb-no-img {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    width: 280px;
    height: 280px;
    background: #1a2336;
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 14px;
    color: rgba(255, 255, 255, 0.3);
}
.lb-no-img-type {
    font-family: var(--font-head);
    font-size: 1.1rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: rgba(255, 255, 255, 0.45);
    text-align: center;
    padding: 0 20px;
}

.lb-caption {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    text-align: center;
    max-width: 560px;
    width: 100%;
}
.lb-caption-era {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--gold);
}
.lb-caption-name {
    font-family: var(--font-head);
    font-size: clamp(1rem, 3vw, 1.4rem);
    font-weight: 700;
    color: #fff;
    line-height: 1.2;
}
.lb-caption-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin-top: 4px;
}
.lb-caption-type {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.4);
}
.lb-caption-price {
    font-family: var(--font-head);
    font-size: 1.45rem;
    font-weight: 700;
    color: var(--gold);
    letter-spacing: -0.01em;
}

/* ============================================================
   EVENT REGISTRATION MODAL
   ============================================================ */

.reg-modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: rgba(0, 0, 0, 0.55);
    align-items: center;
    justify-content: center;
    padding: 16px;
}
.reg-modal-overlay.open {
    display: flex;
}
.reg-modal {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    width: 100%;
    max-width: 620px;
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.22);
    animation: modalIn 0.2s ease;
}
@keyframes modalIn {
    from { opacity: 0; transform: translateY(12px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}
.reg-modal-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    padding: 28px 28px 20px;
    border-bottom: 1px solid var(--border);
}
.reg-modal-eyebrow {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--accent);
    margin: 0 0 6px;
}
.reg-modal-title {
    font-family: var(--font-head);
    font-size: 1.55rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 4px;
    line-height: 1.2;
}
.reg-modal-event-name {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    margin: 0 0 2px;
}
.reg-modal-event-meta {
    font-size: 0.82rem;
    color: var(--muted);
    margin: 0;
}
.reg-modal-close {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--muted);
    padding: 4px;
    border-radius: 6px;
    flex-shrink: 0;
    transition: color 0.15s, background 0.15s;
}
.reg-modal-close:hover {
    color: var(--text);
    background: var(--bg3);
}

/* Form inside modal */
.reg-form {
    padding: 24px 28px 28px;
    display: flex;
    flex-direction: column;
    gap: 18px;
}
.reg-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}
@media (max-width: 520px) {
    .reg-form-row { grid-template-columns: 1fr; }
    .reg-modal-header { padding: 20px 20px 16px; }
    .reg-form { padding: 18px 20px 22px; }
}
.reg-form-field {
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.reg-form-field label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--muted);
}
.reg-required { color: var(--accent); margin-left: 2px; }
.reg-optional  { color: var(--dim);  font-weight: 400; }
.reg-form input,
.reg-form select,
.reg-form textarea {
    width: 100%;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.9375rem;
    padding: 9px 13px;
    box-sizing: border-box;
    transition: border-color 0.15s, box-shadow 0.15s;
    appearance: none;
    -webkit-appearance: none;
}
.reg-form select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 34px;
}
.reg-form input:focus,
.reg-form select:focus,
.reg-form textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}
.reg-form textarea { min-height: 88px; resize: vertical; }
.reg-form-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    padding-top: 4px;
}

/* Registration flash banners (events.php + about.php) */
.reg-flash {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 20px;
    font-size: 0.9rem;
    line-height: 1.5;
    margin: 0;
}
.reg-flash--success {
    background: #f0fdf4;
    color: #15803d;
    border-bottom: 1px solid #bbf7d0;
}
.reg-flash--error {
    background: #fef2f2;
    color: #dc2626;
    border-bottom: 1px solid #fecaca;
}
[data-theme="dark"] .reg-flash--success {
    background: rgba(22, 163, 74, 0.12);
    color: #4ade80;
    border-bottom-color: rgba(74, 222, 128, 0.2);
}
[data-theme="dark"] .reg-flash--error {
    background: rgba(220, 38, 38, 0.12);
    color: #f87171;
    border-bottom-color: rgba(248, 113, 113, 0.2);
}

/* --- Hero --- */
.careers-hero {
    background: linear-gradient(135deg, var(--bg3) 0%, var(--bg4) 100%);
    padding: 100px 0 64px;
    border-bottom: 1px solid var(--border);
}
[data-theme="dark"] .careers-hero {
    background: linear-gradient(135deg, #0f172a 0%, #1a2336 100%);
}
.careers-hero-inner {
    max-width: 680px;
}
.careers-hero-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 12px;
    border-radius: 999px;
    margin-bottom: 20px;
}
.careers-hero-title {
    font-family: var(--font-head);
    font-size: clamp(2.2rem, 5vw, 3.4rem);
    font-weight: 700;
    line-height: 1.1;
    color: var(--text);
    margin: 0 0 16px;
    letter-spacing: -0.01em;
}
.careers-hero-sub {
    font-size: 1.05rem;
    color: var(--muted);
    line-height: 1.65;
    margin: 0;
    max-width: 580px;
}

/* --- Positions grid --- */
.positions-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
    margin-top: 12px;
}
@media (max-width: 900px) {
    .positions-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}
@media (max-width: 600px) {
    .positions-grid {
        grid-template-columns: 1fr;
    }
}

/* --- Position card --- */
.position-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    display: flex;
    flex-direction: column;
    transition: transform 0.18s ease, box-shadow 0.18s ease;
    overflow: hidden;
}
.position-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 32px rgba(0,0,0,0.10);
}
.position-card--featured {
    border-color: var(--gold);
    box-shadow: 0 0 0 1px var(--gold);
}
.position-card--featured:hover {
    box-shadow: 0 8px 32px rgba(245, 158, 11, 0.18), 0 0 0 1px var(--gold);
}
.position-card-icon {
    width: 48px;
    height: 48px;
    border-radius: 10px;
    background: var(--bg3);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
    margin: 24px 24px 0;
    flex-shrink: 0;
}
.position-card--featured .position-card-icon {
    background: rgba(245, 158, 11, 0.12);
    color: var(--gold);
}
.position-card-body {
    padding: 16px 24px 20px;
    flex: 1;
}
.position-meta-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}
.position-type-badge {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    background: rgba(220, 38, 38, 0.1);
    color: var(--accent);
    padding: 3px 8px;
    border-radius: 4px;
    border: 1px solid rgba(220, 38, 38, 0.2);
}
.position-type-badge--part {
    background: rgba(100, 116, 139, 0.1);
    color: var(--muted);
    border-color: var(--border);
}
.position-type-badge--full {
    background: rgba(245, 158, 11, 0.1);
    color: var(--gold);
    border-color: rgba(245, 158, 11, 0.25);
}
.position-location {
    font-size: 0.8rem;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 3px;
}
.position-title {
    font-family: var(--font-head);
    font-size: 1.45rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 8px;
    line-height: 1.2;
}
.position-tagline {
    font-size: 0.9rem;
    color: var(--muted);
    line-height: 1.55;
    margin: 0 0 16px;
}
.position-perks {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.position-perks li {
    font-size: 0.85rem;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 7px;
}
.position-perks li svg {
    color: #16a34a;
    flex-shrink: 0;
}
.position-card-footer {
    padding: 0 24px 24px;
}

/* --- Application section --- */
.application-section {
    background: var(--bg2);
    border-top: 1px solid var(--border);
    padding: 64px 0 80px;
    scroll-margin-top: calc(var(--header-h) + 16px);
}
.apply-form-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 36px 40px;
    max-width: 800px;
}
@media (max-width: 640px) {
    .apply-form-card {
        padding: 24px 20px;
    }
}

/* --- Apply form --- */
.apply-form {
    display: flex;
    flex-direction: column;
    gap: 20px;
}
.apply-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}
@media (max-width: 600px) {
    .apply-form-row {
        grid-template-columns: 1fr;
    }
}
.apply-form-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.apply-form label {
    font-family: var(--font-body);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--muted);
}
.apply-required {
    color: var(--accent);
    margin-left: 2px;
}
.apply-optional {
    color: var(--dim);
    font-weight: 400;
}
.apply-form input,
.apply-form select,
.apply-form textarea {
    width: 100%;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.9375rem;
    padding: 9px 13px;
    transition: border-color 0.15s, box-shadow 0.15s;
    box-sizing: border-box;
    appearance: none;
    -webkit-appearance: none;
}
.apply-form select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 34px;
}
.apply-form input:focus,
.apply-form select:focus,
.apply-form textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}
.apply-form textarea {
    min-height: 100px;
    resize: vertical;
}
.apply-form input::placeholder,
.apply-form textarea::placeholder {
    color: var(--dim);
}

/* --- Availability day chips --- */
.avail-checkboxes {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 6px;
}
.avail-item {
    display: inline-flex;
    align-items: center;
    font-size: 0.875rem !important;
    font-weight: 500 !important;
    color: var(--muted) !important;
    cursor: pointer;
    background: var(--bg2);
    border: 1.5px solid var(--border);
    border-radius: 999px;
    padding: 6px 16px;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
    user-select: none;
}
.avail-item:hover {
    border-color: var(--accent);
    color: var(--text) !important;
    background: var(--bg3);
}
.avail-item:has(input:checked) {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff !important;
}
.avail-item input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}

/* --- Form actions --- */
.apply-form-actions {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 4px;
}
.apply-form-note {
    font-size: 0.8rem;
    color: var(--dim);
    display: flex;
    align-items: center;
    gap: 5px;
}

/* --- Honeypot (hidden from real users) --- */
.apply-honeypot {
    position: absolute;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
    tab-size: 0;
}

/* --- Success / error banners --- */
.apply-success,
.apply-error {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 24px;
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.5;
    position: relative;
    z-index: 5;
}
.apply-success {
    background: #dcfce7;
    color: #14532d;
    border-bottom: 2px solid #16a34a;
}
.apply-success svg {
    color: #16a34a;
    flex-shrink: 0;
}
.apply-error {
    background: #fee2e2;
    color: #7f1d1d;
    border-bottom: 2px solid var(--accent);
}
.apply-error svg {
    color: var(--accent);
    flex-shrink: 0;
}
[data-theme="dark"] .apply-success {
    background: rgba(22, 163, 74, 0.15);
    color: #86efac;
    border-bottom-color: #16a34a;
}
[data-theme="dark"] .apply-error {
    background: rgba(220, 38, 38, 0.15);
    color: #fca5a5;
    border-bottom-color: var(--accent);
}

/* ============================================================
   ABOUT PAGE  (.about-*)
   ============================================================ */

/* --- Hero --- */
/* About hero - default gradient */
.about-hero {
    background: linear-gradient(135deg, var(--bg3) 0%, var(--bg4) 100%);
    padding: 100px 0 64px;
    border-bottom: 1px solid var(--border);
}
[data-theme="dark"] .about-hero {
    background: linear-gradient(135deg, #0f172a 0%, #1a2336 100%);
}

/* About hero - with real store image */
.about-hero--img {
    position: relative;
    padding: 100px 0 88px;
    border-bottom: none;
    background: none;
}
.about-hero-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center top;
    z-index: 0;
}
.about-hero-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to bottom,
        rgba(10, 15, 28, 0.72) 0%,
        rgba(10, 15, 28, 0.85) 100%
    );
    z-index: 1;
}
.about-hero--img .container { position: relative; z-index: 2; }
.about-hero--img .about-hero-eyebrow { color: var(--accent); }
.about-hero--img .about-hero-title { color: #fff; }
.about-hero--img .about-hero-sub { color: rgba(255,255,255,0.75); }

.about-hero-inner {
    max-width: 700px;
}
.about-hero-eyebrow {
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--accent);
    margin: 0 0 12px;
}
.about-hero-title {
    font-family: var(--font-head);
    /* Floor lowered from 2.4rem so COLLECTIBLES fits on its own line at
       360px without browser-forced mid-word break ("COLLEC / TIBLES"). */
    font-size: clamp(1.9rem, 5.5vw, 3.6rem);
    font-weight: 700;
    color: var(--text);
    margin: 0 0 16px;
    /* Was 1.05 - see .hero-title note. */
    line-height: 1.1;
    /* Long store names (e.g. "Laughing Dragon Tavern Games & Collectibles")
       at the 2.4rem mobile floor are wider than a phone viewport. Without
       the wrap hints below, an unbroken word like COLLECTIBLES escapes the
       container and pushes the entire page wider than the viewport - the
       hero stays full-bleed while the rest of the content fits, producing
       a horizontal scroll. overflow-wrap:anywhere lets the browser break
       inside a word as a last resort; min-width:0 keeps any flex/grid
       parent from refusing to shrink to its in-bounds intrinsic width. */
    overflow-wrap: anywhere;
    word-break: normal;
    hyphens: auto;
    min-width: 0;
    text-wrap: balance;
}
/* Same split treatment as the homepage hero - see inc/brand-split.php. */
.about-hero-title-brand,
.about-hero-title-descriptor {
    display: block;
}
.about-hero-title-descriptor {
    font-size: 0.62em;
    opacity: 0.92;
    margin-top: 0.15em;
    line-height: 1.05;
}
.about-hero-sub {
    font-size: 1.1rem;
    color: var(--muted);
    margin: 0;
    max-width: 580px;
    line-height: 1.65;
}

/* Grading section */
.about-grading-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
}
@media (max-width: 768px) { .about-grading-grid { grid-template-columns: 1fr; } }

.about-grading-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 28px 24px;
}
.about-grading-icon {
    color: var(--accent2);
    margin-bottom: 14px;
}
.about-grading-title {
    font-family: var(--font-head);
    font-size: 1.1rem;
    font-weight: 700;
    margin: 0 0 10px;
    color: var(--text);
}
.about-grading-desc {
    font-size: 0.9rem;
    color: var(--muted);
    line-height: 1.6;
    margin: 0;
}

/* In-store experience */
.about-experience-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 24px;
}
@media (max-width: 640px) { .about-experience-grid { grid-template-columns: 1fr; } }

.about-experience-block {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.about-experience-icon {
    color: var(--accent);
    margin-bottom: 4px;
}
.about-experience-label {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    margin: 0;
    color: var(--text);
}
.about-experience-desc {
    font-size: 0.88rem;
    color: var(--muted);
    line-height: 1.6;
    margin: 0;
}
.about-experience-desc a { color: var(--accent); }

/* Phone note (replaces email note).
   Operator report 2026-05-07: phone number was wrapping oddly on phones.
   Root cause: display:flex made every child (icon, text segment, <a>, more
   text) a separate flex item that wrapped its own contents independently
   when the row got too narrow - producing "Call / (425)" on one line and
   "us at / 243-4119" on the next. Switched to normal inline flow so the
   text wraps at word boundaries naturally; icon sits inline, phone link
   gets white-space:nowrap so the number itself never breaks. */
.about-phone-note {
    margin-top: 20px;
    font-size: 0.85rem;
    color: var(--muted);
    text-align: center;
    line-height: 1.55;
}
.about-phone-note svg {
    display: inline-block;
    vertical-align: -2px;      /* visual centering with the text x-height */
    margin-right: 6px;
}
.about-phone-note a {
    color: var(--accent);
    font-weight: 500;
    white-space: nowrap;       /* keep "(425) 243-4119" on a single line */
}

/* --- Story grid --- */
.about-story-grid {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 56px;
    align-items: start;
}
@media (max-width: 860px) {
    .about-story-grid {
        grid-template-columns: 1fr;
        gap: 40px;
    }
}
.about-section-title {
    font-family: var(--font-head);
    font-size: 1.7rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 20px;
}
.about-story-text p {
    color: var(--muted);
    line-height: 1.7;
    margin: 0 0 16px;
    font-size: 1rem;
}
.about-story-text p:last-child { margin-bottom: 0; }

.about-story-aside {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
.about-stat-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px 16px;
    text-align: center;
}
.about-stat-num {
    font-family: var(--font-head);
    font-size: 1.55rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1.1;
    margin-bottom: 4px;
}
.about-stat-label {
    font-size: 0.75rem;
    color: var(--muted);
    line-height: 1.35;
}
.about-stat-card.accent-card {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.accent-card .about-stat-label-top {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    display: block;
    margin-bottom: 4px;
}
.accent-card .about-stat-label {
    color: rgba(255,255,255,0.8);
}

/* --- Games section --- */
.about-games-section {
    background: var(--bg2);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
.about-games-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    gap: 20px;
    margin-bottom: 24px;
}
@media (max-width: 860px) {
    .about-games-grid { grid-template-columns: 1fr; }
}
.about-game-tile {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 24px 22px;
}
.about-game-tile--pokemon {
    border-top: 3px solid var(--c-pokemon, #e63900);
}
.about-game-tile--mtg {
    border-top: 3px solid var(--c-mtg, #a07830);
}
.about-game-tile--yugioh {
    border-top: 3px solid var(--c-yugioh, #1d4ed8);
}
.about-game-tile-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}
.about-game-tile-name {
    font-family: var(--font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
    margin: 0;
}
.about-game-tile-desc {
    font-size: 0.875rem;
    color: var(--muted);
    line-height: 1.6;
    margin: 0 0 12px;
}
.about-game-era-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.about-game-era-list li {
    font-size: 0.8rem;
    color: var(--muted);
    padding-left: 14px;
    position: relative;
}
.about-game-era-list li::before {
    content: '·';
    position: absolute;
    left: 0;
    color: var(--accent);
    font-weight: 700;
}

.about-sealed-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 18px;
    font-size: 0.875rem;
    color: var(--muted);
    margin: 0;
    line-height: 1.55;
}
.about-sealed-note svg {
    flex-shrink: 0;
    color: var(--accent2);
    margin-top: 1px;
}

/* --- Visit section --- */
.about-visit-section { background: var(--bg); }
.about-visit-grid {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 40px;
    align-items: start;
}
@media (max-width: 820px) {
    .about-visit-grid { grid-template-columns: 1fr; }
}
.about-visit-block {
    display: flex;
    gap: 16px;
    align-items: flex-start;
    padding: 16px 0;
    border-bottom: 1px solid var(--border);
}
.about-visit-block:last-child { border-bottom: none; }
.about-visit-block-icon {
    flex-shrink: 0;
    color: var(--accent);
    margin-top: 2px;
}
.about-visit-block-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    margin-bottom: 4px;
}
.about-visit-block-value {
    font-size: 0.9rem;
    color: var(--text);
    line-height: 1.6;
}
.about-visit-block-value a {
    color: var(--accent);
    text-decoration: none;
}
.about-visit-block-value a:hover { text-decoration: underline; }
.about-visit-block-sub {
    display: block;
    font-size: 0.8rem;
    color: var(--muted);
    margin-top: 2px;
}
.about-hours-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 24px;
}
.about-hours-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 16px;
}
.about-hours-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    margin-bottom: 20px;
}
.about-hours-table td {
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
    color: var(--text);
}
.about-hours-table td:last-child {
    text-align: right;
    color: var(--muted);
}
.about-hours-table tr:last-child td { border-bottom: none; }
.about-hours-table tr.hours-closed td { color: var(--dim); }
.about-directions-btn {
    width: 100%;
    justify-content: center;
}

/* --- Policy cards --- */
.about-policy-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
}
@media (max-width: 640px) {
    .about-policy-grid { grid-template-columns: 1fr; }
}
.about-policy-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 28px 24px;
}
.about-policy-icon {
    color: var(--accent);
    margin-bottom: 14px;
}
.about-policy-title {
    font-family: var(--font-head);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 10px;
}
.about-policy-text {
    font-size: 0.9rem;
    color: var(--muted);
    line-height: 1.65;
    margin: 0;
}
.about-policy-text a {
    color: var(--accent);
    text-decoration: none;
}
.about-policy-text a:hover { text-decoration: underline; }

/* --- Contact/social section --- */
.about-contact-section {
    background: var(--bg2);
    border-top: 1px solid var(--border);
}
.about-social-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
}
.about-social-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 18px;
    border-radius: var(--radius);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    border: 1px solid var(--border2);
    background: var(--bg);
    color: var(--text);
    transition: all 0.15s;
}
.about-social-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}
/* Per-platform brand hover colors - mirror the footer's
   .footer-social-link--* hover rules so the about-page social row reads
   the same way (Instagram pink, Facebook blue, Twitch purple, etc.).
   Operator request 2026-05-07: "the about page doesn't match the other
   follow us formats." Class names are namespaced about-social-btn-- to
   avoid colliding with footer rules. */
.about-social-btn--ig:hover      { border-color: #e1306c; color: #e1306c; }
.about-social-btn--fb:hover      { border-color: #1877f2; color: #1877f2; }
.about-social-btn--tk:hover      { border-color: #ff0050; color: #ff0050; }
.about-social-btn--twitch:hover  { border-color: #9146ff; color: #9146ff; }
.about-social-btn--discord:hover { border-color: #5865f2; color: #5865f2; }
.about-social-btn--yt:hover      { border-color: #ff0000; color: #ff0000; }
.about-social-btn--x:hover       { border-color: var(--text); color: var(--text); }
.about-social-btn--yelp:hover    { border-color: #d32323; color: #d32323; }
.about-social-btn--bsky:hover    { border-color: #0085ff; color: #0085ff; }
.about-social-handle {
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--muted);
    margin-left: 2px;
}
.about-email-note {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--muted);
    margin: 0;
}
.about-email-note a {
    color: var(--accent);
    text-decoration: none;
}
.about-email-note a:hover { text-decoration: underline; }


/* ============================================================
   BUYLIST PAGE  (.buylist-*)
   ============================================================ */

/* --- Hero --- */
.buylist-hero {
    background: linear-gradient(135deg, #1a0a0a 0%, #2d1010 100%);
    padding: 100px 0 64px;
    border-bottom: 3px solid var(--accent);
}
.buylist-hero-inner {
    max-width: 680px;
}
.buylist-hero-eyebrow {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--accent2);
    margin: 0 0 12px;
}
.buylist-hero-title {
    font-family: var(--font-head);
    font-size: clamp(2.5rem, 6vw, 4rem);
    font-weight: 700;
    color: #fff;
    margin: 0 0 16px;
    line-height: 1;
}
.buylist-hero-sub {
    font-size: 1.05rem;
    color: rgba(255,255,255,0.72);
    margin: 0 0 24px;
    line-height: 1.65;
    max-width: 540px;
}
.buylist-hero-games {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* --- Steps --- */
/* The chevron .buylist-step-divider markup + rules were removed (2026-05-05):
   read fine on desktop but felt out of place on mobile when the steps stacked
   vertically. Cards now sit directly next to / under each other with the gap
   from .buylist-steps.gap doing the visual separation. */
.buylist-steps {
    display: flex;
    align-items: flex-start;
    gap: 16px;
}
@media (max-width: 720px) {
    .buylist-steps {
        flex-direction: column;
        gap: 14px;
    }
}
.buylist-step {
    flex: 1;
    display: flex;
    gap: 16px;
    align-items: flex-start;
    padding: 24px;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.buylist-step-num {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}
.buylist-step-title {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 6px;
}
.buylist-step-desc {
    font-size: 0.875rem;
    color: var(--muted);
    line-height: 1.6;
    margin: 0;
}

/* --- Rate tables section --- */
.buylist-tables-section {
    background: var(--bg2);
    border-top: 1px solid var(--border);
}
.buylist-table-block {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    margin-bottom: 24px;
}
.buylist-table-block:last-of-type { margin-bottom: 0; }
.buylist-table-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border);
    background: var(--bg2);
}
.buylist-table-title {
    font-family: var(--font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
    margin: 0;
}
.buylist-table-wrap {
    overflow-x: auto;
}
.buylist-rate-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}
.buylist-rate-table th {
    text-align: left;
    padding: 10px 16px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    background: var(--bg3);
    border-bottom: 1px solid var(--border);
}
.buylist-rate-table th:not(:first-child) { text-align: center; }
.buylist-rate-table td {
    padding: 11px 16px;
    color: var(--text);
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.buylist-rate-table tr:last-child td { border-bottom: none; }
.buylist-rate-table td:not(:first-child) { text-align: center; }

.rate-cell {
    font-weight: 600;
    font-size: 0.875rem;
}
.rate-cell.rate-high { color: #16a34a; }
.rate-cell.rate-low  { color: var(--muted); }
.rate-cell.rate-na   { color: var(--dim); font-weight: 400; }
.rate-row-sealed td:first-child {
    font-style: italic;
    color: var(--muted);
}
[data-theme="dark"] .rate-cell.rate-high { color: #4ade80; }

.buylist-disclaimer {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 18px;
    margin-top: 24px;
}
.buylist-disclaimer svg {
    flex-shrink: 0;
    color: var(--accent2);
    margin-top: 2px;
}
.buylist-disclaimer p {
    font-size: 0.85rem;
    color: var(--muted);
    margin: 0;
    line-height: 1.55;
}

/* --- CTA card --- */
.buylist-cta-section { background: var(--bg); }
.buylist-cta-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 40px 36px;
    text-align: center;
    max-width: 680px;
    margin: 0 auto;
}
.buylist-cta-card-title {
    font-family: var(--font-head);
    font-size: 1.8rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 8px;
}
.buylist-cta-card-sub {
    font-size: 0.95rem;
    color: var(--muted);
    margin: 0 0 24px;
    line-height: 1.55;
}
.buylist-cta-details {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 28px;
    text-align: left;
}
.buylist-cta-detail-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 0.875rem;
    color: var(--muted);
}
.buylist-cta-detail-item svg { flex-shrink: 0; color: var(--accent); margin-top: 1px; }
.buylist-cta-detail-item a { color: var(--accent); text-decoration: none; }
.buylist-cta-detail-item a:hover { text-decoration: underline; }
.buylist-directions-btn { min-width: 200px; }


/* ============================================================
   SINGLES PAGE  (.singles-*)
   ============================================================ */

/* --- Hero --- */
.singles-hero {
    background: linear-gradient(135deg, var(--bg3) 0%, var(--bg4) 100%);
    padding: 72px 0 64px;
    border-bottom: 1px solid var(--border);
}
[data-theme="dark"] .singles-hero {
    background: linear-gradient(135deg, #0f172a 0%, #1a2336 100%);
}
.singles-hero-inner {
    max-width: 700px;
}
.singles-hero-eyebrow {
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--accent);
    margin: 0 0 12px;
}
.singles-hero-title {
    font-family: var(--font-head);
    font-size: clamp(2.4rem, 5.5vw, 3.6rem);
    font-weight: 700;
    color: var(--text);
    margin: 0 0 16px;
    line-height: 1.05;
}
.singles-hero-sub {
    font-size: 1.05rem;
    color: var(--muted);
    margin: 0 0 24px;
    max-width: 600px;
    line-height: 1.65;
}
.singles-hero-games {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* --- How to buy --- */
.singles-how-section {
    background: var(--bg2);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
.singles-how-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}
@media (max-width: 720px) {
    .singles-how-grid { grid-template-columns: 1fr; }
}
.singles-how-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 28px 24px;
    text-align: center;
}
.singles-how-icon {
    color: var(--accent);
    margin-bottom: 14px;
    display: flex;
    justify-content: center;
}
.singles-how-title {
    font-family: var(--font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 10px;
}
.singles-how-desc {
    font-size: 0.875rem;
    color: var(--muted);
    line-height: 1.65;
    margin: 0;
}

/* --- What we stock --- */
.singles-stock-section { background: var(--bg); }
.singles-stock-grid {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: 24px;
    align-items: start;
}
@media (max-width: 800px) {
    .singles-stock-grid { grid-template-columns: 1fr; }
}
.singles-stock-block {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 26px 24px;
}
.singles-stock-block--pokemon {
    border-top: 3px solid var(--c-pokemon, #e63900);
}
.singles-stock-block--other {
    display: flex;
    flex-direction: column;
    gap: 20px;
    background: transparent;
    border: none;
    padding: 0;
}
.singles-stock-sub-block {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 22px 20px;
}
.singles-stock-block-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.singles-stock-title {
    font-family: var(--font-head);
    font-size: 1rem;
    font-weight: 700;
    color: var(--text);
    margin: 0;
}
.singles-stock-desc {
    font-size: 0.875rem;
    color: var(--muted);
    margin: 0 0 14px;
    line-height: 1.55;
}
.singles-stock-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.singles-stock-list li {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--muted);
    line-height: 1.45;
}
.singles-era-badge {
    display: inline-block;
    flex-shrink: 0;
    font-size: 0.68rem;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: 4px;
    white-space: nowrap;
}
.singles-era-vintage {
    background: rgba(161, 98, 7, 0.12);
    color: #a16207;
    border: 1px solid rgba(161, 98, 7, 0.25);
}
[data-theme="dark"] .singles-era-vintage {
    background: rgba(234, 179, 8, 0.12);
    color: #facc15;
    border-color: rgba(234, 179, 8, 0.25);
}
.singles-era-mid {
    background: rgba(37, 99, 235, 0.1);
    color: #1d4ed8;
    border: 1px solid rgba(37, 99, 235, 0.2);
}
[data-theme="dark"] .singles-era-mid {
    background: rgba(96, 165, 250, 0.1);
    color: #93c5fd;
    border-color: rgba(96, 165, 250, 0.2);
}
.singles-era-modern {
    background: rgba(147, 51, 234, 0.1);
    color: #7c3aed;
    border: 1px solid rgba(147, 51, 234, 0.2);
}
[data-theme="dark"] .singles-era-modern {
    background: rgba(167, 139, 250, 0.1);
    color: #c4b5fd;
    border-color: rgba(167, 139, 250, 0.2);
}

/* --- Social CTA --- */
.singles-social-cta {
    background: var(--accent);
    padding: 56px 0;
}
.singles-social-cta-inner {
    max-width: var(--max-width);
    margin: 0 auto;
    padding: 0 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 32px;
    flex-wrap: wrap;
}
.singles-social-cta-title {
    font-family: var(--font-head);
    font-size: 1.8rem;
    font-weight: 700;
    color: #fff;
    margin: 0 0 8px;
}
.singles-social-cta-sub {
    font-size: 0.95rem;
    color: rgba(255,255,255,0.8);
    margin: 0;
    line-height: 1.6;
    max-width: 480px;
}
.singles-social-cta-actions {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    flex-shrink: 0;
}
.singles-social-cta .btn-primary {
    background: #fff;
    color: var(--accent);
    border-color: #fff;
}
.singles-social-cta .btn-primary:hover {
    background: rgba(255,255,255,0.9);
}
.singles-social-cta .btn-outline {
    border-color: rgba(255,255,255,0.6);
    color: #fff;
}
.singles-social-cta .btn-outline:hover {
    border-color: #fff;
    background: rgba(255,255,255,0.1);
}
@media (max-width: 640px) {
    .singles-social-cta-inner {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* =============================================================
   BUYLIST QUOTE FORM
   ============================================================= */
.buylist-quote-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 32px;
    max-width: 680px;
    /* Center within the section container - was anchored to the left at
       any viewport wider than 680px. Operator request 2026-05-10. */
    margin-left: auto;
    margin-right: auto;
}
.quote-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}
.quote-form-group {
    margin-bottom: 18px;
}
.quote-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 6px;
}
.quote-required {
    color: var(--accent);
    margin-left: 2px;
}
.quote-optional {
    font-weight: 400;
    color: var(--muted);
    font-size: 0.8rem;
}
.quote-input {
    display: block;
    width: 100%;
    padding: 10px 13px;
    background: var(--bg);
    border: 1px solid var(--border2);
    border-radius: 8px;
    color: var(--text);
    font-size: 0.92rem;
    font-family: inherit;
    transition: border-color 0.15s, box-shadow 0.15s;
    box-sizing: border-box;
    -webkit-appearance: none;
    appearance: none;
}
.quote-input::placeholder {
    color: var(--dim);
}
.quote-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}
.quote-textarea {
    resize: vertical;
    min-height: 90px;
}
.quote-form-actions {
    margin-top: 6px;
}
.quote-msg {
    margin-top: 14px;
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 0.88rem;
    font-weight: 500;
}
.quote-msg--success {
    background: rgba(34, 197, 94, 0.1);
    border: 1px solid rgba(34, 197, 94, 0.3);
    color: #15803d;
}
[data-theme="dark"] .quote-msg--success {
    background: rgba(34, 197, 94, 0.08);
    color: #4ade80;
}
.quote-msg--error {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    color: var(--accent);
}
@media (max-width: 580px) {
    .buylist-quote-card {
        padding: 22px 18px;
    }
    .quote-form-row {
        grid-template-columns: 1fr;
    }
}

/* ------------------------------------------------------------
   Fix: fixed-header body offset + responsive logo (white-label)
   The site-header is position:fixed. Without body padding the page
   content scrolls under it. On mobile, long shop names wrap to a
   second line rather than ellipsis-truncating - easier to read and
   doesn't lose information.
   ------------------------------------------------------------ */
body {
    padding-top: var(--header-h);
}

.logo {
    min-width: 0;
    max-width: 100%;
}
.logo-name {
    max-width: 100%;
    word-break: break-word;
}

/* Desktop: keep one line if it fits.
   The .site-header height was historically pinned to 70px here. That
   override defeated the per-tenant --logo-pad-y knob added 2026-05-20
   (operator dropped padding to 0 on perfect-circle-games and saw no
   change because this rule kept the header at 70px regardless of
   min-height). Removed the height pin; the rule at line ~164
   (`min-height: calc(44px + 2*var(--logo-pad-y, 13px))`) is now the
   single source of truth on desktop. Defaults still reproduce 70px. */
@media (min-width: 641px) {
    .logo-name { white-space: nowrap; }
}

/* Mobile: header sizes to its actual content - no min-height padding
   the empty space under the brand. Just enough breathing room. */
@media (max-width: 640px) {
    .site-header {
        position: fixed;
        height: auto;
    }
    .header-inner {
        gap: 12px;
        padding: 6px 16px;
        align-items: center;
    }
    .logo-name {
        font-size: 1.2rem; /* blackletter reads smaller per-pixel; bumped from 1.05rem */
        line-height: 1.15;
        letter-spacing: 0;
        white-space: normal;
    }
    .logo-sub { display: none; }
}

@media (max-width: 380px) {
    .logo-name { font-size: 1.1rem; } /* blackletter reads smaller; was 0.95rem */
}

/* ------------------------------------------------------------
   Fix: hero fills gap under fixed header without hiding content
   Use a CSS var for header height so body padding, hero pull-up,
   and hero internal padding all stay in sync. Mobile bumps the
   var to 100px because the long shop name wraps to 2 lines.
   ------------------------------------------------------------ */
/* --header-h is the live rendered height of .site-header. inc/header.php
   measures the element via ResizeObserver and writes the value to :root,
   so this var stays in sync as the header reflows (long brand wraps to
   2 lines on phones, web fonts swap in, etc.). The static fallback here
   covers the moment before JS runs (a sane desktop default) so body
   padding and .shop-filter-bar's sticky offset don't jump. */
:root { --header-h: 70px; }

/* .grand-open-hero used to overlap the fixed header with a negative
   margin-top so its background bled under the bar, but index.php's inline
   <style> block uses the `padding` shorthand which silently overrode the
   matching padding-top, causing content to vanish behind the header on
   mobile. No-overlap layout is more robust:
       body padding-top  › clears the fixed header
       hero padding (inline) › its own breathing room from the top
   The hero now starts cleanly below the header. */

/* Defensive body bg override */
html, body { background-color: var(--bg); }

/* Defensive guard: clip any horizontal overflow at the viewport. The about
   hero title was the first offender (long unbreakable words pushing the
   page wider than the viewport - "COLLECTIBLES" at 2.4rem on a 360px
   phone), and per-component fixes are in place there, but white-label
   tenants will keep introducing edge cases (long brand names, long event
   titles, untouched legacy widgets). overflow-x:clip is preferred over
   :hidden because it doesn't establish a new containing block, so it
   doesn't break sticky positioning or z-index stacking elsewhere on the
   page. Vertical scrolling is unaffected. */
html, body { overflow-x: clip; }

/* Scrollbar appearance.

   2026-05-23: an earlier attempt this session set `html, body {
   scrollbar-width: thin }` on the theory that it would just give Firefox
   a thinner vertical scrollbar. That declaration ALSO instructs Firefox
   to reserve gutter space for the scrollbar — and on Firefox Mobile,
   where scrollbars are rendered as transient overlays rather than
   persistent rails, the gutter is reserved but no scrollbar is drawn.
   Result: <html>/<body> compute 6px narrower than innerWidth, producing
   a permanent dead-zone strip on the right edge of every page on
   Firefox Mobile. Confirmed via _diag-width.php on a 360px FF Android
   viewport (innerWidth=360, html.scrollWidth=354) — operator-reported
   right-side gap that other browsers don't show.

   The right fix is to NOT set scrollbar-width at the document root.
   Firefox's `auto` default uses a thin scrollbar on touch / mobile
   anyway and doesn't reserve gutter. Per-component scrollbar-width
   declarations (eg `.filter-pill-row { scrollbar-width: none }`) are
   still in use elsewhere — those are scoped to overflowing containers
   where hiding the scrollbar is intentional and they don't trigger
   document-level gutter reservation. */
/* html, body { scrollbar-width: thin; }  // intentionally removed */

/* Hide the document-level vertical scrollbar widget on touch devices.

   Why: on Firefox Android (and any FF where the user has enabled
   "Always show scrollbar" in OS accessibility settings), the OS
   scrollbar widget renders as a dark/system-colored vertical rail
   at the right edge of the viewport. That rail is NOT recolorable
   by CSS or by <meta theme-color> — it's painted by the OS widget,
   not the page. On dark-themed tenants (paradox-trading-post,
   pegasus-games-madison) the dark rail blends invisibly into the
   dark page bg. On light-themed tenants (bananahandles cream,
   pegasus light-mode, any future light-brand tenant) the same dark
   rail produces a stark right-side strip that breaks the
   edge-to-edge feel of the brand.

   This rule eliminates the disparity by hiding the document-level
   scrollbar on any device whose primary input is touch (the
   `pointer: coarse` media query matches phones + tablets, and
   excludes any desktop browser even when the window is narrow).
   Mouse + trackpad users keep their familiar scrollbar — only
   touch users lose it, and touch users navigate by direct
   manipulation (swipe), not by clicking a scrollbar thumb.

   The trade-off is the loss of a visual scroll-position indicator
   on touch. Modern mobile UX accepts that (most native apps don't
   show a persistent scrollbar). If we later want a transient
   overlay-style position hint, the right tool is JavaScript
   bound to scroll events, not the OS widget.

   Per-component `scrollbar-width: none` declarations elsewhere
   (.filter-pill-row, .game-filter) are unchanged — those scope to
   horizontally-scrolling containers where hiding the scrollbar is
   already deliberate.

   Operator-decided 2026-05-23 after bananahandles vs paradox/
   pegasus comparison made the theme-dependent disparity visible. */
@media (pointer: coarse) {
    html, body { scrollbar-width: none; }  /* Firefox */
    html::-webkit-scrollbar,
    body::-webkit-scrollbar { display: none; }  /* WebKit / Blink */
}

/* ------------------------------------------------------------
   Address links - render the shop street address as a tappable
   block that opens Google Maps. Inherits parent color so it
   doesn't stand out as a generic blue link, but a subtle dotted
   underline on hover hints it's interactive. Used by tcg_address_link().
   ------------------------------------------------------------ */
a.tcg-address-link {
    color: inherit;
    text-decoration: none;
    transition: color 0.15s, text-decoration-color 0.15s;
}
a.tcg-address-link:hover,
a.tcg-address-link:focus-visible {
    color: var(--accent, #dc2626);
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}


/* ============================================================
   EVENT CALENDAR  (.event-calendar*)
   ============================================================
   Monthly grid renderer in inc/event-calendar.php. Gated per-tenant
   by config_json.features.event_calendar. First enabled for Pegasus
   2026-05-11. Operator request: replace hand-drawn monthly PNGs with
   auto-generated calendars driven from tcg_events + events_recurring.
   ============================================================ */

.event-calendar {
    margin: 32px 0 48px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg, 14px);
    padding: 24px;
}

.event-calendar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 18px;
    gap: 16px;
    flex-wrap: wrap;
}

.event-calendar-title {
    font-family: var(--font-head);
    font-size: 1.65rem;
    font-weight: 700;
    color: var(--text);
    margin: 0;
    line-height: 1.1;
}

.event-calendar-nav {
    display: flex;
    gap: 6px;
    align-items: center;
}
.event-calendar-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    height: 36px;
    padding: 0 12px;
    border-radius: var(--radius, 8px);
    border: 1px solid var(--border);
    background: var(--bg2);
    color: var(--text);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
    transition: background 0.15s, border-color 0.15s;
}
.event-calendar-nav-btn:hover {
    background: var(--bg3);
    border-color: var(--border2);
}

/* Weekday header row */
.event-calendar-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
    margin-bottom: 4px;
}
.event-calendar-weekday {
    text-align: center;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    padding: 6px 0;
}

/* Main grid
   ─────────
   Each row sizes to its tallest day (per-week, not per-month). Operator
   iteration 2026-05-12: a previous attempt floored every row at the
   busiest day of the entire month - that made quiet weeks visually
   oversized just because Sundays elsewhere were busy. Reverted: with
   grid-auto-rows: auto (CSS default), each row sizes to its own tallest
   cell, so a busy Sunday only grows ITS row. Empty cells inside that
   row still match its height (Grid equalizes within a row, not across
   rows). A floor lives on .event-calendar-cell so completely-empty
   weeks still have some height. */
.event-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-auto-rows: auto;
    gap: 4px;
}
.event-calendar-cell {
    /* Operator request 2026-05-12: pills hug the day cell with
       essentially zero padding. The day-number row keeps its own small
       inset (.event-calendar-cell-day) so the number doesn't kiss the
       border. overflow: hidden clips any pill that crowds the rounded
       cell corner. */
    min-height: 96px;
    padding: 0;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 5px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: relative;
    overflow: hidden;
    /* CRITICAL: grid items default to min-width: auto which sizes to
       min-content. Without min-width: 0 the cell can be pushed past 1fr
       by content, breaking the 7-col grid on mobile (operator screenshot
       2026-05-12). */
    min-width: 0;
}
.event-calendar-cell--blank {
    background: transparent;
    border-color: transparent;
}
.event-calendar-cell.is-empty {
    opacity: 0.7;
}
.event-calendar-cell.is-past {
    opacity: 0.55;
}
.event-calendar-cell.is-past .event-calendar-pill {
    /* Past-day pills link to #day-YYYY-MM-DD anchors that don't exist
       in the upcoming listing (which starts at today). Disable click so
       they don't quietly fail. The cell stays dimmed via .is-past
       opacity above so it's clear they're historical context. */
    pointer-events: none;
    cursor: default;
}
.event-calendar-cell.is-today {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}
.event-calendar-cell-day {
    /* Day-number keeps its own small inset since the cell padding is now
       0 (so pills hug edges) - without this the digit would touch the
       cell border. */
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--muted);
    text-align: right;
    line-height: 1;
    padding: 4px 5px 0;
}
.event-calendar-cell.is-today .event-calendar-cell-day {
    color: var(--accent);
}
.event-calendar-cell-events {
    display: flex;
    flex-direction: column;
    gap: 1px;                 /* tightened from 3px - pills sit nearly flush */
    overflow: hidden;
    min-width: 0;
    flex: 1;                  /* fill remaining cell height under the day-number */
}

/* ── Event pill (v2 - thumbnail-backed) ────────────────────────────────
   v1 used flat color (var(--pill-color)) based on game_slug. Scaled to
   ~4 categories; broke at 18 (operator 2026-05-12). v2 layers a
   per-type thumbnail as background-image, with a dark gradient overlay
   for text legibility. When no thumbnail file exists yet, the pill
   falls back to flat --pill-color so the system works end-to-end
   while artwork is being curated.

   Why two layers (image + gradient) instead of one solid color: a TCG
   shop's calendar wants visual specificity. "Smash Bros" should feel
   different from "Friday Night Magic" at a glance. The legend at the
   bottom shows the same artwork keyed to label - operators don't have
   to memorize color = meaning. */
.event-calendar-pill {
    /* v3 2026-05-12: color-only pill (thumbnail layering removed -
       operator "they're making the text hard to read"). The accent fills
       the pill via --pill-color, and the text color is the YIQ-derived
       high-contrast counterpart via --pill-text (computed in PHP by
       event_type_text_color). No text-shadow now - text-shadow only
       helped on the busy thumbnail; on a flat accent it muddies the type.

       Block-level layout so time + name spans inline-flow and wrap as a
       single text run. No ellipsis, no nowrap - long names like "Star
       Wars Unlimited Casual" fully wrap. */
    display: block;
    padding: 3px 5px;
    background: var(--pill-color, #6b7280);
    color: var(--pill-text, #fff);
    font-size: 0.62rem;          /* down from 0.7 - operator 2026-05-12 "a little smaller" */
    font-weight: 600;
    line-height: 1.18;
    min-height: 22px;
    white-space: normal;
    overflow-wrap: anywhere;
    overflow: hidden;
    border-radius: 0;
    min-width: 0;
    box-shadow: inset 0 -1px 0 rgba(0,0,0,0.18);
}
.event-calendar-pill-time {
    /* v4 2026-05-12: time pops to its own block-level row above the
       event name. Operator wanted the time visible "maybe at the top
       of the event text"; promoting to display:block accomplishes that
       and gives the name its own line to breathe + wrap into. Slight
       size + opacity drop differentiates it visually without competing
       with the name. */
    display: block;
    font-weight: 700;
    opacity: 0.85;
    font-size: 0.92em;       /* relative to pill font - ~0.57rem desktop */
    line-height: 1.1;
    letter-spacing: 0.01em;
    margin-bottom: 1px;
}
.event-calendar-pill-name {
    /* Block-level so it stacks below the time row. Inherits pill's
       word-wrap so long names wrap naturally on multiple lines. */
    display: block;
    font-weight: 600;
}

/* ── Legend (v2 - thumbnail-keyed) ─────────────────────────────────────
   Each item shows a small thumbnail tile next to its label. The same
   --pill-thumb file backs the pill artwork above, so the legend reads as
   a literal key (this artwork = this program). When a thumbnail isn't
   present, the swatch falls back to the accent color - the legend still
   renders cleanly. */
.event-calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 16px;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
    font-size: 0.78rem;
}
.event-calendar-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
}
.event-calendar-legend-swatch {
    /* 16:9 thumbnail tile, sized so each legend row stays compact but the
       artwork is recognizable. background-color is set inline (accent
       fallback); background-image is also inline (the thumb URL). */
    display: inline-block;
    width: 28px;
    height: 16px;
    border-radius: 3px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.18) inset;
    flex-shrink: 0;
}

/* Mobile: keep the 7-col grid (traditional calendar view) - operator
   request 2026-05-11 "I know the text would be tiny but can you give it
   a try." Pills are kept readable rather than ultra-tiny per operator
   feedback 2026-05-12 ("make each event card a little larger"). Same
   grid-auto-rows equation as desktop - we just redefine --cell-base and
   --pill-row-h so the formula resizes naturally on small viewports. */
@media (max-width: 760px) {
    .event-calendar {
        padding: 14px 8px;
        margin: 20px 0 32px;
    }
    .event-calendar-grid {
        gap: 2px;
    }
    .event-calendar-cell {
        padding: 0;            /* edge-to-edge pills on mobile too */
        gap: 1px;
        min-height: 60px;      /* per-week floor so empty weeks have presence */
    }
    .event-calendar-cell-day {
        font-size: 0.62rem;
        padding: 3px 4px 0;
    }
    .event-calendar-weekday {
        font-size: 0.6rem;
        padding: 4px 0;
        letter-spacing: 0.04em;
    }
    .event-calendar-pill {
        font-size: 0.6rem;
        padding: 2px 4px;
        line-height: 1.15;
        min-height: 18px;
        border-radius: 0;
    }
    .event-calendar-pill-time {
        /* Mobile: time stays visible (block-level above the name -
           operator 2026-05-12). Slightly smaller relative ratio because
           the pill itself shrank. The display:none rule that used to
           live here was for v1 inline-flex pills where time competed
           with name for horizontal space; with v4 block-stacked layout
           there's no competition. */
        font-size: 0.85em;
        opacity: 0.85;
    }
    .event-calendar-grid {
        gap: 2px;
    }
}
@media (max-width: 380px) {
    /* Smallest phones - shave a touch off but keep pills readable per
       operator 2026-05-12 ("make each event card a little larger").
       Grid-auto-rows formula handles the row equalization automatically
       once --cell-base and --pill-row-h are redeclared. */
    .event-calendar-cell {
        min-height: 52px;
    }
    .event-calendar-pill {
        font-size: 0.55rem;
        padding: 2px 3px;
        min-height: 16px;
    }
}

/* Subscribe row (Google / Apple / Outlook / .ics) under the calendar title */
.event-calendar-subscribe {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 0.78rem;
}
.event-calendar-subscribe-label {
    color: var(--muted);
    margin-right: 2px;
}
.event-calendar-subscribe-btn {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border-radius: 4px;
    background: var(--bg3);
    border: 1px solid var(--border);
    color: var(--muted);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.72rem;
    transition: background 0.15s, color 0.15s;
}
.event-calendar-subscribe-btn:hover {
    background: var(--bg4);
    color: var(--text);
}

/* Make pills clickable links (was a div before) - pointer cursor + hover lift */
a.event-calendar-pill {
    text-decoration: none;
    cursor: pointer;
}
a.event-calendar-pill:hover {
    filter: brightness(1.15);
    transform: translateY(-1px);
    transition: transform 0.1s, filter 0.1s;
}

/* ── Event card category badge (TCG / Tabletop RPG / Community / etc.) ─
   The broad-category badge on each event card, rendered by
   inc/event-card.php from the resolved EVENT_CATEGORIES entry.
   Replaces the legacy "OTHER" game-badge with a store-agnostic
   template label so events feel categorized, not store-specific
   (operator 2026-05-12). Color comes inline via --cat-color so all
   badges in one category share a hue regardless of which specific
   event-type fed in. */
.event-category-badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 999px;
    background: var(--cat-color, #6b7280);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    white-space: nowrap;
    box-shadow: 0 1px 2px rgba(0,0,0,0.18);
    align-self: flex-start;   /* don't stretch in the event-header-left flex column */
}

/* ── Upcoming-events filter chip strip (events.php) ────────────────────
   Operator request 2026-05-12: "search at the top so it defaults to all
   events but can be sorted by tournaments only or other categories
   only." Chip strip sits above the day-grouped listing; each chip flips
   an "active" state and JS filters .event-card-wrap elements by their
   data-tournament / data-type-key attributes. Empty day groups (no
   matching cards) get .is-hidden so the page collapses cleanly. */
.events-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0 0 22px;
    padding: 12px 14px;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius, 8px);
}
.events-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    line-height: 1.2;
    transition: background 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s;
    user-select: none;
}
.events-filter-chip:hover {
    background: var(--bg3);
    color: var(--text);
}
.events-filter-chip.is-active {
    background: var(--accent);
    color: var(--accent-text, #fff);
    border-color: var(--accent);
    box-shadow: 0 1px 4px rgba(0,0,0,0.15);
}
.events-filter-divider {
    width: 1px;
    height: 24px;
    background: var(--border);
    margin: 0 6px;
}
.events-filter-label {
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-right: 4px;
}

/* Type dropdown - replaces the 20-chip strip that overflowed. Independent
   filter axis; composes with the primary chip filter (operator 2026-05-12). */
.events-filter-select-wrap {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 2px;
}
.events-filter-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 6px 30px 6px 12px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    /* Custom chevron via background-image so the select reads like a chip */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}
.events-filter-select:hover {
    background-color: var(--bg3);
    color: var(--text);
}
.events-filter-select:focus {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.events-filter-select.has-value {
    /* Active-state look when a specific type is selected (JS toggles the
       has-value class). Echoes the chip is-active visual. */
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--accent-text, #fff);
}

/* ── Hidden / empty states ─────────────────────────────────────────── */
.event-card-wrap.is-hidden,
.day-group.is-hidden {
    display: none;
}

/* ── :target flash highlight (calendar pill → day anchor) ──────────── */
.day-group.is-flashed {
    /* Brief glow on the day group when the visitor lands on it via a
       calendar-pill anchor click. Re-trigger by removing+re-adding the
       class (events.php JS handles it). */
    animation: dayGroupFlash 1.5s ease-out;
}
@keyframes dayGroupFlash {
    0%   { background: var(--accent); background-clip: padding-box; }
    20%  { background: color-mix(in srgb, var(--accent) 25%, transparent); }
    100% { background: transparent; }
}

/* ── Event card footer actions (gcal button + Register) ───────────── */
.event-footer-actions {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.event-gcal-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius, 8px);
    border: 1px solid var(--border);
    background: var(--bg2);
    color: var(--muted);
    text-decoration: none;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.event-gcal-btn:hover {
    background: var(--bg3);
    color: var(--accent);
    border-color: var(--accent);
}

/* ── Upcoming-events day grouping (events.php) ─────────────────────────
   Operator request 2026-05-12: instead of repeating the date inside
   every event card, group cards under a per-day heading divider.
   "Today" gets a highlighted heading; the next N-1 days render
   expanded; the rest collapse into a <details> fold. The
   render_event_card() call site passes ['show_date' => false] so the
   date doesn't double up inside the card. */
.day-group {
    margin-bottom: 28px;
}
.day-group:last-child {
    margin-bottom: 0;
}
.day-group-heading {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin: 0 0 12px;
    padding: 8px 0 10px;
    border-bottom: 1px solid var(--border);
    font-family: var(--font-head, inherit);
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--text);
}
.day-group--today .day-group-heading {
    /* Visually anchor today's row - accent left bar + brighter text. */
    border-bottom-color: var(--accent);
    padding-left: 10px;
    border-left: 3px solid var(--accent);
}
.day-group-weekday {
    color: var(--text);
}
.day-group--today .day-group-weekday {
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.85rem;
}
.day-group-date {
    color: var(--muted);
    font-weight: 600;
    font-size: 0.95rem;
}
.day-group-count {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--muted);
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--bg2);
    border: 1px solid var(--border);
    flex-shrink: 0;
}
.day-group-events {
    /* Same card grid shape as before, just nested under the day heading. */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
}

/* Collapsible fold for days beyond the first N_VISIBLE_DAYS */
.day-group-fold {
    margin-top: 24px;
    border-top: 1px dashed var(--border);
    padding-top: 18px;
}
.day-group-fold-summary {
    /* <summary> needs list-style: none + cursor to look like a button */
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius, 8px);
    font-weight: 600;
    color: var(--text);
    transition: background 0.15s, border-color 0.15s;
    user-select: none;
}
.day-group-fold-summary::-webkit-details-marker {
    display: none;     /* hide default disclosure triangle in webkit */
}
.day-group-fold-summary:hover {
    background: var(--bg3);
    border-color: var(--border2, var(--accent));
}
.day-group-fold-summary svg {
    transition: transform 0.2s ease;
    color: var(--accent);
}
.day-group-fold[open] > .day-group-fold-summary svg {
    transform: rotate(180deg);
}
.day-group-fold-body {
    margin-top: 18px;
}

/* Always-accepting-applications note under the positions grid on careers.php.
   Operator request 2026-05-11: don't only highlight a single role; signal
   that resumes for any position are welcome year-round. */
.positions-always-hiring {
    margin: 32px auto 0;
    max-width: 720px;
    padding: 14px 18px;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--muted);
    font-size: 0.92rem;
    line-height: 1.55;
    text-align: center;
    display: flex;
    gap: 10px;
    align-items: flex-start;
    justify-content: center;
}
.positions-always-hiring svg {
    flex-shrink: 0;
    color: var(--accent);
    margin-top: 2px;
}
.positions-always-hiring strong {
    color: var(--text);
}

/* Position status eyebrow ('Now Hiring' / 'Always Accepting') above the
   role title on careers.php. Driven by config_json.careers.positions.<key>.state.
   Operator request 2026-05-11. */
.position-status {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 10px;
}
.position-status--active {
    background: rgba(220, 38, 38, 0.10);
    color: var(--accent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.position-status--passive {
    background: var(--bg3);
    color: var(--muted);
    border: 1px solid var(--border);
}
[data-theme="dark"] .position-status--passive {
    background: rgba(255,255,255,0.05);
    border-color: rgba(255,255,255,0.10);
    color: #cbd5e1;
}

/* ============================================================
   CONTACT PAGE (.contact-*)
   ============================================================
   Two-column layout: visible store info on the left, form on the
   right. Mobile stacks. Mirrors the buylist quote-form pattern
   but as a dedicated /contact.php page. Operator request
   2026-05-11 - pain point from Pegasus demo lifts the template.
   ============================================================ */

.contact-grid {
    display: grid;
    grid-template-columns: 1fr 1.4fr;
    gap: 36px;
    align-items: start;
}
@media (max-width: 860px) {
    .contact-grid { grid-template-columns: 1fr; gap: 24px; }
}

/* LEFT column - info */
.contact-info-title,
.contact-form-title {
    font-family: var(--font-head);
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 16px;
}
.contact-info-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 22px;
}
.contact-info-icon {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 999px;
    background: var(--bg2);
    border: 1px solid var(--border);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
}
.contact-info-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    margin-bottom: 2px;
}
.contact-info-value {
    color: var(--text);
    text-decoration: none;
    font-size: 0.98rem;
    line-height: 1.45;
    font-weight: 500;
}
.contact-info-value:hover { color: var(--accent); }
.contact-info-meta {
    margin-top: 4px;
    font-size: 0.82rem;
    color: var(--muted);
}
.contact-info-meta a { color: var(--accent); text-decoration: none; }
.contact-info-meta a:hover { text-decoration: underline; }

.contact-hours-table {
    margin-top: 6px;
    font-size: 0.88rem;
    border-collapse: collapse;
}
.contact-hours-table td {
    padding: 2px 12px 2px 0;
    color: var(--text);
}
.contact-hours-table td:first-child {
    color: var(--muted);
    font-weight: 600;
    min-width: 50px;
}
.contact-hours-table tr.hours-closed td { color: var(--muted); font-style: italic; }

/* Contact-page socials list - replaces the hours table 2026-05-12.
   Each row: small icon + platform handle/label. Hover lifts the row to
   the accent color so it reads like an interactive contact channel
   (DM us on IG, ping us in Discord, etc.) rather than a passive list. */
.contact-socials {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.contact-social-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    color: var(--text);
    text-decoration: none;
    font-size: 0.92rem;
    transition: color 0.15s;
}
.contact-social-link:hover {
    color: var(--accent);
}
.contact-social-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: var(--muted);
    flex-shrink: 0;
}
.contact-social-link:hover .contact-social-icon {
    color: var(--accent);
}
.contact-social-label {
    word-break: break-word;
}

/* RIGHT column - form card */
.contact-form-card {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 28px 28px 24px;
}
.contact-form-sub {
    font-size: 0.92rem;
    color: var(--muted);
    margin: 0 0 22px;
    line-height: 1.5;
}
.contact-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}
@media (max-width: 520px) {
    .contact-form-row { grid-template-columns: 1fr; gap: 0; }
}
.contact-form-group { margin-bottom: 16px; }
.contact-label {
    display: block;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 6px;
}
.contact-required { color: var(--accent); margin-left: 2px; }
.contact-optional { color: var(--muted); font-weight: 400; font-size: 0.74rem; }
.contact-input {
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: 0.95rem;
    color: var(--text);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.contact-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12);
}
.contact-textarea {
    resize: vertical;
    min-height: 120px;
    font-family: inherit;
}
.contact-form-actions { margin-top: 4px; }

/* Honeypot - hidden from humans, picked up by bots */
.contact-honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
}

/* Inline form feedback message */
.contact-msg {
    margin-top: 14px;
    padding: 10px 14px;
    border-radius: 6px;
    font-size: 0.88rem;
    line-height: 1.45;
}
.contact-msg--success {
    background: rgba(34, 197, 94, 0.12);
    color: #16a34a;
    border: 1px solid rgba(34, 197, 94, 0.30);
}
.contact-msg--error {
    background: rgba(239, 68, 68, 0.12);
    color: #dc2626;
    border: 1px solid rgba(239, 68, 68, 0.30);
}
[data-theme="dark"] .contact-msg--success { color: #4ade80; }
[data-theme="dark"] .contact-msg--error   { color: #f87171; }
