/* =============================================
   Jaron Phillips — 2026 site styles
   Plain CSS, mobile-first, no build step.
   System fonts only (no font downloads = fast load).
   Edit the color variables below to re-theme the whole site.
============================================= */

:root {
  --bg: #14141c;
  --bg-panel: #1c1c27;
  --text: #f2f0e8;
  --text-dim: #b9b6ae;
  --accent: #ffb52e;   /* warm orange-gold, mascot color */
  --accent2: #4de3d1;  /* teal, secondary accent */
  --border: #33333f;
  /* Scales with the viewport instead of a fixed cap. Floor of 960px
     keeps it from feeling cramped on smaller desktop windows; ceiling
     of 1320px matches common convention (Bootstrap's widest
     container, Tailwind's typical max-width tokens) — beyond that,
     more width doesn't help, it just spreads the layout thin.
     Actual paragraph text still gets its own tighter max-width
     wherever it appears (see .hero p.tagline, ~640px), since that's
     a readability rule, not a layout one. */
  --maxw: clamp(960px, 70vw, 1320px);
  --radius: 10px;
  font-size: 17px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.55;
}

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

a { color: var(--accent); text-decoration: none; }
a:hover, a:focus { text-decoration: underline; }

h1, h2, h3 {
  line-height: 1.2;
  margin: 0 0 0.5em 0;
}

h1 { font-size: clamp(1.9rem, 5vw, 2.8rem); }
h2 { font-size: clamp(1.4rem, 4vw, 1.9rem); }
h3 { font-size: 1.15rem; }

p { margin: 0 0 1em 0; }

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 20px;
}

/* ---------- Header / Nav ---------- */

header.site-header {
  border-bottom: 1px solid var(--border);
  padding: 16px 0;
}

.site-header .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--text);
}

.logo img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--accent);
}

.logo:hover { text-decoration: none; color: var(--accent); }

nav.main-nav ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
}

nav.main-nav a {
  display: inline-block;
  padding: 8px 12px;
  color: var(--text);
  border-radius: 6px;
  font-size: 0.95rem;
}

nav.main-nav a:hover,
nav.main-nav a[aria-current="page"] {
  background: var(--bg-panel);
  color: var(--accent);
  text-decoration: none;
}

/* ---------- Hero ---------- */

.hero {
  padding: 60px 0 40px 0;
  text-align: center;
}

.hero img.mascot {
  width: 140px;
  margin: 0 auto 20px auto;
  border-radius: 50%;
  border: 3px solid var(--accent);
}

.hero p.tagline {
  font-size: 1.15rem;
  color: var(--text-dim);
  max-width: 640px;
  margin: 0 auto 28px auto;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  padding: 12px 22px;
  border-radius: 999px;
  background: var(--accent);
  color: #14141c;
  font-weight: 700;
  border: none;
  cursor: pointer;
  font-size: 1rem;
}

.btn:hover { background: #ffc75c; text-decoration: none; }

.btn.ghost {
  background: transparent;
  border: 2px solid var(--accent);
  color: var(--accent);
}

.btn.ghost:hover { background: rgba(255,181,46,0.1); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

/* ---------- Door / card grid (home page) ---------- */

.doors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin: 40px 0;
}

.door {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  text-align: left;
  color: var(--text);
}

.door:hover { border-color: var(--accent); text-decoration: none; }

.door h3 { color: var(--accent); margin-bottom: 8px; }
.door p { color: var(--text-dim); margin-bottom: 0; font-size: 0.95rem; }

/* ---------- Gallery grid (art page) ---------- */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
  margin: 30px 0;
}

.gallery figure {
  margin: 0;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.gallery img { width: 100%; }

.gallery figcaption {
  padding: 12px 14px;
}

.gallery figcaption .title {
  font-weight: 700;
  color: var(--accent);
  display: block;
  margin-bottom: 4px;
}

.gallery figcaption p {
  color: var(--text-dim);
  font-size: 0.9rem;
  margin: 0;
}

/* ---------- Sections / panels ---------- */

section { padding: 40px 0; }

section.alt {
  background: var(--bg-panel);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.panel {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 20px;
}

/* ---------- Services / pricing list ---------- */

.service {
  border-bottom: 1px solid var(--border);
  padding: 20px 0;
}

.service:last-child { border-bottom: none; }

.service .price {
  color: var(--accent2);
  font-weight: 700;
  font-size: 1.05rem;
}

.service h3 { margin-bottom: 4px; }
.service p { color: var(--text-dim); margin-bottom: 0; }

/* ---------- Client / proof strip ---------- */

.clients {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 20px 0;
}

.clients span {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.9rem;
  color: var(--text-dim);
}

/* ---------- Footer ---------- */

footer.site-footer {
  border-top: 1px solid var(--border);
  padding: 30px 0;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.9rem;
}

footer.site-footer a { color: var(--text-dim); }

/* ---------- Play / character creator page ---------- */

/* Outer column that holds the Randomize button, the colored panel,
   and the name/save controls, all centered. The button/input are
   capped narrow; the panel below gets its own wider cap (see .creator). */
.creator-shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.creator-shell > .btn,
.creator-shell > .field-row {
  width: 100%;
  max-width: 320px;
}

/* Shared "label prefix inside a bordered box" look for Name and
   Instagram, so they read as one consistent pair of fields instead of
   two different styles (used to be one bare <input> plus one
   separately-styled Instagram row — now both are a <label class=
   "field-row"> wrapping a prefix + a reset-to-plain <input>).
   Clicking anywhere in the row focuses the input, same as clicking a
   normal input's own padding would, since the row itself is the
   <label>, not just decoration sitting next to one. */
.field-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  cursor: text;
}

.field-prefix {
  color: var(--accent);
  font-weight: 700;
  flex-shrink: 0;
}

.instagram-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--accent);
}

.field-row input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  padding: 0;
  color: var(--text);
  font-size: 1rem;
}

.field-row input:focus { outline: none; }

/* Background + Fur pickers flank Randomize above the panel — they
   restyle the whole character rather than one part of it, so they
   don't belong in the left/right columns with everything else.

   This is the exact same grid as .creator below (auto | 1fr | auto,
   same gap, same padding, same breakpoint) rather than a separately
   tuned flex row — two independent layouts can be nudged to roughly
   line up by eye, but they drift the moment content changes (that's
   what happened here: this used to be hand-tuned to the old design
   and quietly stopped matching once the panel's own width/padding
   changed). Using the identical grid means bg/fur mathematically
   land in the same column as the picker directly below them, always,
   with no manual re-tuning. .trait-slot's own min-width (see below)
   is what makes that hold even though bg/fur have no label and the
   panel's slots do — without it, this row's "auto" column would size
   to the bare 60/92px icon instead of matching the wider column the
   labeled slots produce underneath it. */
.creator-top {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  justify-items: center;
  gap: 10px 4px;
  width: 100%;
  padding: 18px 6px;
  margin: 0 auto 5px auto;
  position: relative;
  z-index: 2;
}

@media (min-width: 1100px) {
  .creator-top {
    width: 828px;
    max-width: 100%;
    padding: 32px;
    gap: 24px 28px;
  }
}

@media (max-width: 1099px) {
  /* Background/Fur are now compact ~56px icons (see .trait-slot),
     not full label+strip blocks, so Randomize is the only thing wide
     enough to risk overflowing the row — trimming it down keeps
     bg / Randomize / fur comfortably fitting on real phones. */
  #randomize-btn {
    padding: 10px 16px;
    font-size: 0.9rem;
  }
}

/* Outlined in accent yellow with a black fill, instead of the solid
   .btn look — reads as a distinct "action" against the two pickers
   sitting beside it. */
#randomize-btn {
  background: #000;
  border: 2px solid var(--accent);
  color: var(--accent);
}

#randomize-btn:hover { background: #1a1a1a; }

/* The colored card itself — background-image is set live by
   js/play.js to match the selected "bg" trait, background-color is
   just the pre-JS fallback so there's no flash of nothing.

   Same 3-column CSS grid — auto | 1fr | auto — at every breakpoint
   now: the left/right trait-slot icons (see .trait-slot below) hug
   the edges, the stage fills whatever's left in the middle. Only the
   sizes change at 1100px (bigger icons, more padding, a wider fixed
   panel width) — see the media query below, and slotThumbSize() /
   popoverTierSizes() in js/play.js for the matching pixel values.
   Desktop's width is exact-math'd the same way this page always has:

     width  = padding, both sides                (32 * 2 =  64)
            + 2 slot columns (~label-width wide) (104 * 2 = 208)
            + 2 column gaps                       (28 * 2 =  56)
            + the stage                                   (500)
            = 828px (the columns are "auto" width, sized to their
              actual content by the browser — 104 is just the
              intended target via .trait-slot-label's max-width at
              that breakpoint, not a hard-coded column width)

   Desktop layout doesn't kick in until 1100px (a holdover from when
   this was a much wider flex layout — still a reasonable point to
   switch since a narrower window has no real room for flanking
   columns next to a 500px stage either way). */
.creator {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: repeat(3, 1fr);
  align-items: center;
  justify-items: center;
  gap: 10px 4px;
  width: 100%;
  border-radius: 20px;
  padding: 18px 6px;
  background-color: var(--bg-panel);
  background-size: cover;
  background-position: center;
}

.creator-preview {
  display: flex;
  justify-content: center;
  width: 100%;
  max-width: 500px;
  grid-column: 2;
  grid-row: 1 / span 3;
}

@media (min-width: 1100px) {
  .creator {
    width: 828px;
    max-width: 100%;
    padding: 32px;
    gap: 24px 28px;
    margin: 0 auto;
  }
}

.creator-preview .stage {
  width: 100%;
  max-width: 500px;
  aspect-ratio: 1 / 1;
  position: relative;
}

.creator-preview .stage img.layer {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  object-fit: contain;
}

.trait-groups {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* display:contents drops each wrapper div out of the grid (its
   children become direct grid items of .creator instead), then each
   individual .trait-slot gets placed at an explicit cell — column 1
   or 3, row matching its position in the group (1st/2nd/3rd child =
   top/middle/bottom). Same at every breakpoint now, since both use
   the same grid — only .trait-slot's own size changes (see the
   @media(min-width:1100px) override below it). */
#trait-groups-left,
#trait-groups-right {
  display: contents;
}
#trait-groups-left .trait-slot:nth-child(1) { grid-column: 1; grid-row: 1; }
#trait-groups-left .trait-slot:nth-child(2) { grid-column: 1; grid-row: 2; }
#trait-groups-left .trait-slot:nth-child(3) { grid-column: 1; grid-row: 3; }
#trait-groups-right .trait-slot:nth-child(1) { grid-column: 3; grid-row: 1; }
#trait-groups-right .trait-slot:nth-child(2) { grid-column: 3; grid-row: 2; }
#trait-groups-right .trait-slot:nth-child(3) { grid-column: 3; grid-row: 3; }

/* ---------- Trait picker: compact slot + popover ---------- */
/* A single icon showing just the current selection for one category.
   Tapping opens a small popover (.trait-popover further down) to
   swipe through the rest. Built by buildTraitSlot() in js/play.js —
   same component at every breakpoint, just bigger past 1100px (see
   the size override below and slotThumbSize() in js/play.js, which
   has to stay in sync with whatever .trait-slot-thumb's width/height
   actually is here). */
.trait-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  /* Matches .trait-slot-label's max-width below — see the long
     comment on .creator-top above for why this matters even for
     Background/Fur, which don't render a label at all. */
  min-width: 64px;
}

.trait-slot-thumb {
  display: block;
  width: 60px;
  height: 60px;
  border-radius: 10px;
  background-color: #0e0e14;
  background-repeat: no-repeat;
  border: 2px solid var(--accent);
}

.trait-slot-label {
  font-size: 0.7rem;
  font-weight: 700;
  color: #14141c;
  max-width: 64px;
  text-align: center;
  line-height: 1.15;
}

@media (min-width: 1100px) {
  .trait-slot { min-width: 104px; }
  .trait-slot-thumb { width: 92px; height: 92px; border-radius: 14px; }
  .trait-slot-label { font-size: 0.85rem; max-width: 104px; }
}

/* Reused by the popover's tiered thumbnails below — sizing comes from
   .tier-0/1/2 there, this is just the shared look (border, selected
   state, background). */
.thumb {
  width: 68px;
  height: 68px;
  padding: 0;
  border-radius: 8px;
  overflow: hidden;
  background: #0e0e14;
  border: 2px solid transparent;
  cursor: pointer;
  flex-shrink: 0;
  scroll-snap-align: center;
  transition: border-color 0.1s ease, transform 0.1s ease, width 0.15s ease, height 0.15s ease, opacity 0.15s ease;
}

.thumb:hover { border-color: var(--border); transform: translateY(-1px); }

.thumb.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(255, 181, 46, 0.25);
}

/* ---------- Trait popover ---------- */
/* Opens when a .trait-slot is tapped (see openPopover() in
   js/play.js). Shows 5 thumbnails at once — current selection at
   tier-0, two either side at tier-1, then tier-2 — sized/dimmed down
   with distance, while still being a fully swipeable strip through
   every option (tier is just recalculated after each swipe settles).
   Fixed + centered horizontally so it can never spill off-screen
   regardless of which slot triggered it. Bigger past 1100px, matching
   .trait-slot's own size bump above — see popoverTierSizes() in
   js/play.js for the pixel values these have to stay in sync with. */
.trait-popover {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  max-width: 92vw;
  /* Light gold, not the dark panel color — the popover can open over
     any selected background color (including dark ones), and dark
     thumbs on a dark popover on a dark bg was unreadable. */
  background: #fdedc4;
  border: 2px solid var(--accent);
  border-radius: 16px;
  padding: 16px 14px 12px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55);
}

.trait-popover-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  background: #000;
  color: var(--accent);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.trait-popover-label {
  font-size: 0.85rem;
  font-weight: 700;
  color: #14141c; /* dark text now that the popover background is light */
  padding-right: 20px; /* clears the close button */
}

.trait-popover-track-wrap { position: relative; }

/* A flex row inside align-items:center column parent has nothing to
   bound its width — left unset it just grows to fit every option at
   once, and overflow-x:auto never actually has anything to scroll
   (the same "nothing to measure against" trap .creator-preview hit
   earlier on this page). An explicit width, sized to fit exactly the
   5 visible tiers + side padding for the arrows, is what makes both
   the clipping and the swipe/scroll actually engage. */
.trait-popover-track {
  display: flex;
  align-items: center;
  gap: 8px;
  width: min(376px, 82vw);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding: 6px 30px; /* room for the overlaid arrows either side */
}

.trait-popover-track::-webkit-scrollbar { display: none; }

.trait-popover-track .thumb.tier-0 { width: 76px; height: 76px; opacity: 1; }
.trait-popover-track .thumb.tier-1 { width: 58px; height: 58px; opacity: 0.7; }
.trait-popover-track .thumb.tier-2 { width: 46px; height: 46px; opacity: 0.4; }

.trait-popover-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
}

.trait-popover-arrow.prev { left: -2px; }
.trait-popover-arrow.next { right: -2px; }

/* Bigger popover at the desktop breakpoint, matching .trait-slot's
   own size bump — tier sizes here have to stay in sync with
   popoverTierSizes() in js/play.js (crop math is computed per-tier
   from that array, not read back from CSS). */
@media (min-width: 1100px) {
  .trait-popover {
    gap: 10px;
    padding: 20px 18px 14px;
  }
  .trait-popover-close { width: 34px; height: 34px; font-size: 1.3rem; }
  .trait-popover-label { font-size: 1rem; }
  .trait-popover-track {
    width: 580px;
    max-width: 80vw;
    gap: 10px;
    padding: 8px 40px;
  }
  .trait-popover-track .thumb.tier-0 { width: 126px; height: 126px; }
  .trait-popover-track .thumb.tier-1 { width: 96px; height: 96px; }
  .trait-popover-track .thumb.tier-2 { width: 72px; height: 72px; }
  .trait-popover-arrow { width: 40px; height: 40px; font-size: 1.5rem; }
  .trait-popover-arrow.prev { left: -4px; }
  .trait-popover-arrow.next { right: -4px; }
}

.saved-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 14px;
  margin-top: 16px;
}

/* Not scoped to .saved-gallery — Top 3 (below) reuses this same
   card look outside the gallery grid, via buildCharacterCard() in
   js/play.js. */
.card {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px;
  text-align: center;
}

.card p {
  margin: 8px 0 0 0;
  font-size: 0.85rem;
  color: var(--text-dim);
}

/* Optional credit link — only rendered at all if a character was
   saved with a handle attached (see buildCharacterCard in
   js/play.js). Sits between the name and the like button, in Top 3
   cards too since they share the same card markup. */
.instagram-credit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin-top: 4px;
  color: var(--accent2);
  font-size: 0.8rem;
  font-weight: 600;
}

.instagram-credit:hover { text-decoration: underline; }

.instagram-credit svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* Big preview of any saved character (main gallery or Top 3) — the
   real behavior is split by (hover:) media query below, since "hover
   to preview, no close button needed" and "tap to preview, needs a
   close button" are genuinely different interactions, not the same
   one adapted. This base rule just covers what's shared: hidden by
   default, sits on top of everything, fixed to the viewport so it's
   never clipped near the edge of the grid regardless of where the
   card lives. */
.card-preview {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  z-index: 200;
  transition: opacity 0.15s ease;
}

/* The sized square box — image, name, and location/date all live on
   top of each other inside this, not on top of each other in the
   image file itself. The saved JPEG (what you'd hand to a
   print-on-demand API later) never has text drawn into it; this is
   purely a viewer overlay. */
.card-preview-frame {
  position: relative;
  border: 2px solid var(--accent);
  border-radius: var(--radius);
  background: var(--bg-panel);
  overflow: hidden;
}

.card-preview-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* cover, not contain — the saved image is always a square JPEG and
     this frame is always square too, so there's no real cropping
     either way, but contain leaves a hairline gap at one edge if the
     two squares don't land on exactly the same sub-pixel size (which
     showed up as a thin sliver of the frame's own background peeking
     through at the top). cover always fills the box completely. */
  object-fit: cover;
}

/* Scoped to .card-preview-frame (not just the bare class) on purpose:
   both of these are <p> elements inside .card, and .card p (below,
   under "Not scoped to .saved-gallery") sets its own color at
   specificity (0,1,1) — one class + one element. A bare
   ".card-preview-name { color: #000 }" is only (0,1,0), which loses
   to that regardless of source order, so the color silently fell
   back to .card p's dim gray no matter what it said here. Two
   classes — (0,2,0) — unambiguously outranks it. */
.card-preview-frame .card-preview-name,
.card-preview-frame .card-preview-meta {
  position: absolute;
  left: 0;
  right: 0;
  margin: 0;
  padding: 10px 14px;
}

/* Black text with a light halo instead of white-on-a-dark-gradient —
   no gradient bar needed behind it, which was the simplest fix for a
   persistent visual glitch right at the top edge of the preview that
   a couple of other approaches didn't fully clear up. */
.card-preview-frame .card-preview-name {
  top: 0;
  font-weight: 700;
  font-size: 1.1rem;
  text-align: left;
  color: #000;
}

.card-preview-frame .card-preview-meta {
  bottom: 0;
  font-size: 0.8rem;
  text-align: right;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
}

/* Real mouse + hover support (desktop/laptop): the original
   float-on-hover behavior, no backdrop, no close button — moving the
   mouse off the card is the "close." */
@media (hover: hover) and (pointer: fine) {
  .card-preview {
    background: transparent;
    transform: scale(0.94);
    transition: opacity 0.15s ease, transform 0.15s ease;
  }
  .card-preview-frame {
    width: 500px;
    height: 500px;
    max-width: 90vw;
    max-height: 90vw;
    /* .card-preview itself is fully transparent by design (float on
       hover, no dark modal) — but that means whatever's on the page
       directly behind the frame's edges (at whatever the current
       scroll position happens to be — often the live character stage
       up top) shows through right up against the border, which reads
       as a stray sliver of "wrong" content rather than a clean card.
       This large var(--bg)-colored glow fades the frame smoothly into
       the page's own background color for ~70px around it, so nothing
       else can show up touching the border, without the preview
       turning into an actual solid backdrop. */
    box-shadow: 0 0 45px 35px var(--bg), 0 20px 50px rgba(0, 0, 0, 0.6);
  }
  .card-preview-close { display: none; }
  .card:hover .card-preview {
    opacity: 1;
    transform: scale(1);
  }
}

/* Touch (no real hover): tapping the thumbnail toggles a
   .preview-open class on the card (see js/play.js) instead of
   relying on :hover, and shows a dark full-screen backdrop + close
   button since there's no "move the mouse away" equivalent on a
   phone. */
@media (hover: none), (pointer: coarse) {
  .card-preview {
    background: rgba(10, 10, 14, 0.88);
  }
  .card-preview-frame {
    width: min(500px, 90vw);
    height: min(500px, 90vw);
  }
  .card.preview-open .card-preview {
    opacity: 1;
    pointer-events: auto;
  }
  .card-preview-close {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 2px solid var(--accent);
    background: #000;
    color: var(--accent);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
  }
}

/* ---------- Top 3 ---------- */

.top-three-heading {
  margin-bottom: 14px;
}

.top-three {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 20px;
  margin-bottom: 30px;
}

/* Plain 1st/2nd/3rd left to right, same order they come back from
   the API (ranked by likes) — no podium stagger. */
.top-three .card {
  position: relative;
  width: 150px;
  border-width: 2px;
}

.top-card.gold {
  border-color: #ffd700;
  box-shadow: 0 0 0 1px #ffd700, 0 0 18px rgba(255, 215, 0, 0.35);
}

.top-card.silver {
  border-color: #c8ced6;
  box-shadow: 0 0 0 1px #c8ced6, 0 0 14px rgba(200, 206, 214, 0.3);
}

.top-card.bronze {
  border-color: #cd7f32;
  box-shadow: 0 0 0 1px #cd7f32, 0 0 14px rgba(205, 127, 50, 0.3);
}

.rank-badge {
  position: absolute;
  top: -14px;
  left: -10px;
  font-size: 1.7rem;
  line-height: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}

.like-btn {
  margin-top: 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 0.9rem;
  color: var(--text);
  cursor: pointer;
}

.like-btn:hover { border-color: var(--accent); }
.like-btn.liked { border-color: var(--accent2); }
.like-btn:disabled { opacity: 0.6; cursor: default; }

/* ---------- Utility ---------- */

.center { text-align: center; }
.muted { color: var(--text-dim); }
.small { font-size: 0.85rem; }

/* Visually hidden but still readable by screen readers and search
   engines — use this instead of deleting a heading outright when you
   just want to reclaim the visual space it was taking up. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
