/* ============================================================
   Rivals Team Builder — design tokens
   Palette drawn from the game's select screen: deep space navy,
   gold call-to-action, and one color per role.

   Two-colour team-up language, used identically in the hero grid,
   the team-up panel and the team bar:
     gold   — this hero GAINS an enhanced effect
     violet — this hero ANCHORS one for a teammate
   Solid gold (no pulse) means the team-up is already active.
   ============================================================ */
:root {
  --bg: #0b0e1a;
  --bg-raise: #121732;
  --bg-panel: #151b3a;
  --line: #2a3158;
  --ink: #edf0fa;
  --ink-dim: #8a92b2;
  --gold: #f2c744;
  --gold-soft: rgba(242, 199, 68, 0.16);
  --anchor: #b585ff;
  --kofi: #ff5e5b;
  --contact: #b585ff;
  --vanguard: #ffa53e;
  --duelist: #ff4d6d;
  --strategist: #3fd2ff;
  --flex: #b585ff;
  --skew: -8deg;
  --hero-icon: 88px;
  --topbar-h: 150px;
  --peek-h: 54px; /* height of the mobile bottom-sheet peek bar */
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --font-display: "Rajdhani", system-ui, sans-serif;
  --font-body: "Barlow", system-ui, sans-serif;
  /* Scrollbar: track melts into the page bg, thumb reads as part of the
     navy frame and warms to gold on hover. */
  --scroll-track: var(--bg);
  --scroll-thumb: #303a6b;
  --scroll-thumb-hover: #4a5699;
}

* { box-sizing: border-box; }

/* ---------- Themed scrollbars (window + any overflow pane) ----------
   Firefox + modern Chromium honour scrollbar-color (it inherits, so this
   one declaration covers the page and the team-up panel). Safari and older
   Chromium fall back to the ::-webkit-scrollbar rules below. */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll-thumb) var(--scroll-track);
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--scroll-track); }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border-radius: 8px;
  border: 2px solid var(--scroll-track);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: var(--scroll-track); }

body {
  margin: 0;
  background:
    radial-gradient(1200px 500px at 75% -10%, rgba(63, 210, 255, 0.07), transparent 60%),
    radial-gradient(900px 420px at 10% 0%, rgba(255, 77, 109, 0.06), transparent 55%),
    var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.45;
}

h1, h2, h3 { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; }

button { font: inherit; cursor: pointer; }

/* ---------- Anchored top bar (masthead + team bar together) ---------- */
.topbar {
  position: sticky; top: 0; z-index: 30;
  background: linear-gradient(180deg, rgba(11, 14, 26, 0.97), rgba(11, 14, 26, 0.93));
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(8px);
}

.masthead { border-bottom: 1px solid rgba(42, 49, 88, 0.6); }
.masthead-inner {
  max-width: 1280px; margin: 0 auto; padding: 12px 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}
.wordmark { margin: 0; font-size: 24px; font-weight: 700; }
.wordmark span { color: var(--gold); }
.masthead-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.season-tag {
  font-family: var(--font-display); font-weight: 600; font-size: 13px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--gold);
  border: 1px solid rgba(242, 199, 68, 0.45); padding: 3px 10px;
  clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
}
.ghost-btn {
  background: transparent; color: var(--ink); border: 1px solid var(--line);
  padding: 6px 14px; font-family: var(--font-display); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; font-size: 12px;
  clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
  transition: border-color 120ms ease, color 120ms ease;
}
.ghost-btn:hover, .ghost-btn:focus-visible { border-color: var(--gold); color: var(--gold); }
.ghost-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.ghost-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.ghost-btn:disabled:hover { border-color: var(--line); color: var(--ink); }
/* Click feedback */
.ghost-btn.flash-ok {
  border-color: var(--gold); color: var(--gold); background: var(--gold-soft);
  animation: btn-pop 420ms var(--ease);
}
.ghost-btn.flash-err {
  border-color: var(--duelist); color: var(--duelist); background: rgba(255, 77, 109, 0.12);
  animation: btn-shake 360ms ease;
}
@keyframes btn-pop { 0% { transform: scale(1); } 45% { transform: scale(1.07); } 100% { transform: scale(1); } }
@keyframes btn-shake {
  0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); }
}

/* ---------- Team bar ---------- */
.team-bar-inner {
  max-width: 1280px; margin: 0 auto; padding: 10px 20px 6px;
  display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
}
/* A transparent wrapper on desktop (display:contents keeps the slots a direct
   flex child of .team-bar-inner); the sticky pinned strip on mobile. */
.team-strip { display: contents; }
.team-slots { display: flex; gap: 8px; flex-wrap: wrap; }

/* Team slots match the select-grid icons, so the team reads as the same object */
.slot {
  width: var(--hero-icon);
  background: transparent; border: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  color: inherit;
}
.slot-icon {
  width: var(--hero-icon); height: var(--hero-icon); border-radius: 8px;
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--line);
  background: #10142c;
  transition: border-color 140ms ease, transform 140ms var(--ease), box-shadow 140ms ease;
}
.slot-icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.slot.empty .slot-icon { border-style: dashed; background: rgba(18, 23, 50, 0.5); }
.slot.empty .slot-num { font-family: var(--font-display); font-weight: 700; font-size: 24px; color: var(--line); }
.slot.filled .slot-icon { border-color: var(--role-color, var(--line)); }
.slot.filled:hover .slot-icon, .slot.filled:focus-visible .slot-icon {
  border-color: var(--duelist); transform: translateY(-2px);
}
.slot.filled:focus-visible { outline: none; }
.slot.filled:focus-visible .slot-icon { outline: 2px solid var(--gold); outline-offset: 2px; }
.slot-remove {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(11, 14, 26, 0.72); color: var(--duelist);
  font-size: 28px; font-weight: 700;
  opacity: 0; transition: opacity 120ms ease;
}
.slot.filled:hover .slot-remove, .slot.filled:focus-visible .slot-remove { opacity: 1; }
.slot-name {
  font-family: var(--font-display); font-weight: 600; font-size: 11px; line-height: 1.1;
  text-transform: uppercase; letter-spacing: 0.03em; text-align: center;
  color: var(--ink-dim);
  max-width: var(--hero-icon); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.slot.filled .slot-name { color: var(--ink); }

/* Slot join / leave */
/* backwards, not both: a forwards fill would pin the final keyframe's
   opacity:1 over the .symp-focus dim rules for as long as the class stays. */
.slot.just-added { animation: slot-in 340ms var(--ease) backwards; }
.slot.just-added .slot-icon { animation: slot-flash 620ms ease-out backwards; }
@keyframes slot-in {
  0% { opacity: 0; transform: translateY(-14px) scale(0.72); }
  60% { transform: translateY(0) scale(1.08); }
  100% { opacity: 1; transform: none; }
}
@keyframes slot-flash {
  0% { box-shadow: 0 0 0 0 rgba(242, 199, 68, 0.9), 0 0 22px rgba(242, 199, 68, 0.8); }
  100% { box-shadow: 0 0 0 10px rgba(242, 199, 68, 0), 0 0 0 rgba(242, 199, 68, 0); }
}
/* Slots leave as a detached ghost clone (see ghostSlot), so there is no
   .leaving state here — the real slot is gone the instant you click. */

/* Reorder: filled slots are draggable. touch-action:none lets a touch drag
   reorder without the page trying to scroll out from under it. */
.slot.filled { cursor: grab; touch-action: none; }
body.dragging-slot { cursor: grabbing; user-select: none; }
body.dragging-slot .slot.filled { cursor: grabbing; }
/* The slot being dragged reads as an empty gap; its ghost clone shows the motion. */
.slot.drag-src { opacity: 0.28; }
.slot.drag-src .slot-icon { border-style: dashed; }
.slot.drag-src .slot-remove { opacity: 0; }
.slot-ghost { filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.55)); will-change: transform; }
.slot-ghost .slot-icon { border-color: var(--gold); }
.slot-ghost .slot-remove { opacity: 0; }

/* Sympathy: hovering a hero in the grid lights the teammates it relates to */
.slot.symp-gain .slot-icon { border-color: var(--gold); animation: pulse-gold 1.5s ease-in-out infinite; }
.slot.symp-anchor .slot-icon { border-color: var(--anchor); animation: pulse-anchor 1.5s ease-in-out infinite; }

.role-tally { display: flex; gap: 14px; font-family: var(--font-display); font-weight: 600; font-size: 14px; letter-spacing: 0.05em; }
.tally { display: flex; align-items: center; gap: 6px; }
.tally-dot { width: 10px; height: 10px; transform: skewX(var(--skew)); display: inline-block; }
.tally.vanguard .tally-dot { background: var(--vanguard); }
.tally.duelist .tally-dot { background: var(--duelist); }
.tally.strategist .tally-dot { background: var(--strategist); }
.tally.flex .tally-dot { background: var(--flex); }

/* Footer row: Browse link on the left, Ko-fi + Contact on the right, baseline-aligned */
.team-bar-footer {
  max-width: 1280px; margin: 0 auto; padding: 0 20px 8px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 22px; flex-wrap: wrap;
}
.topbar-links {
  margin: 0; font-size: 13px;
}
.topbar-links a {
  color: var(--gold); font-weight: 600; text-decoration: none;
}
.topbar-links a:hover { text-decoration: underline; }

/* ---------- Ko-fi + Contact ---------- */
.team-bar-actions {
  display: inline-flex; align-items: center; gap: 10px;
}
.kofi {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; text-decoration: none; color: var(--ink);
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
  letter-spacing: 0.06em; text-transform: uppercase;
  border: 1px solid var(--kofi); background: rgba(255, 94, 91, 0.08);
  clip-path: polygon(9px 0, 100% 0, calc(100% - 9px) 100%, 0 100%);
  transition: background 160ms ease, transform 160ms var(--ease), box-shadow 160ms ease;
}
.kofi strong { color: var(--kofi); font-weight: 700; }
/* Ko-fi's own white-on-dark mark (189x120), sized to its native 1.575 aspect
   so it renders unletterboxed. Don't recolour it — it's their trademark. */
.kofi-mark {
  width: 22px; height: 14px; flex: none; display: block;
  transition: transform 280ms var(--ease);
}
.kofi-emote {
  width: 26px; height: 26px; object-fit: contain; display: block; flex: none;
  transition: transform 280ms var(--ease);
}
.kofi:hover, .kofi:focus-visible {
  background: rgba(255, 94, 91, 0.18);
  box-shadow: 0 0 16px rgba(255, 94, 91, 0.3);
  transform: translateY(-1px);
}
.kofi:hover .kofi-emote, .kofi:focus-visible .kofi-emote { transform: rotate(-8deg) scale(1.22); }
.kofi:hover .kofi-mark, .kofi:focus-visible .kofi-mark { transform: rotate(-6deg) scale(1.12); }
.kofi:focus-visible { outline: 2px solid var(--kofi); outline-offset: 3px; }

/* Contact — same skewed chip as Ko-fi, tinted the Galacta purple. Mirrors the
   parallelogram the other way so the pair reads as a matched set. */
.contact {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; text-decoration: none; color: var(--ink);
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
  letter-spacing: 0.06em; text-transform: uppercase;
  border: 1px solid var(--contact); background: rgba(181, 133, 255, 0.08);
  clip-path: polygon(0 0, 100% 0, calc(100% - 9px) 100%, 9px 100%);
  transition: background 160ms ease, transform 160ms var(--ease), box-shadow 160ms ease;
}
.contact strong { color: var(--contact); font-weight: 700; }
.contact-emote {
  width: 26px; height: 26px; object-fit: contain; display: block; flex: none;
  transition: transform 280ms var(--ease);
}
.contact:hover, .contact:focus-visible {
  background: rgba(181, 133, 255, 0.18);
  box-shadow: 0 0 16px rgba(181, 133, 255, 0.3);
  transform: translateY(-1px);
}
.contact:hover .contact-emote, .contact:focus-visible .contact-emote { transform: rotate(8deg) scale(1.22); }
.contact:focus-visible { outline: 2px solid var(--contact); outline-offset: 3px; }

/* Ko-fi's donation widget, in an in-page overlay so the app never has to be
   left behind to chip in. Opened from the .kofi button above. */
.kofi-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.kofi-modal[hidden] { display: none; }
.kofi-modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(5, 7, 16, 0.78); backdrop-filter: blur(3px);
  animation: kofi-fade 160ms var(--ease);
}
.kofi-modal-panel {
  position: relative; width: min(400px, 100%); height: min(680px, 88vh);
  border: 1px solid var(--kofi); border-radius: 12px; overflow: hidden;
  background: var(--bg-panel);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6), 0 0 26px rgba(255, 94, 91, 0.28);
  animation: kofi-pop 200ms var(--ease);
}
.kofi-modal-panel iframe {
  width: 100%; height: 100%; border: 0; display: block;
  background: #fff;
}
.kofi-modal-close {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  width: 30px; height: 30px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1; color: var(--ink);
  border: 1px solid var(--line); border-radius: 50%;
  background: rgba(11, 14, 26, 0.85);
  transition: background 140ms ease, transform 140ms var(--ease), color 140ms ease;
}
.kofi-modal-close:hover, .kofi-modal-close:focus-visible {
  background: var(--kofi); color: #fff; transform: scale(1.08); border-color: var(--kofi);
}
.kofi-modal-close:focus-visible { outline: 2px solid var(--kofi); outline-offset: 2px; }
@keyframes kofi-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes kofi-pop {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .kofi-modal-backdrop, .kofi-modal-panel { animation: none; }
}

/* The hero detail popover lives in css/hero-detail.css — a shared component
   also used by team-ups.html (see js/hero-detail.js). */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- Layout ---------- */
.layout {
  max-width: 1280px; margin: 0 auto; padding: 22px 20px 40px;
  display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px;
}

.section-title { font-size: 18px; font-weight: 700; margin: 0; }
.select-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 14px;
}
/* Toggles: radio-like (one at a time) but clearable by clicking the active one */
.key { display: flex; gap: 8px; flex-wrap: wrap; margin: 0; margin-right: auto; }
.key-item {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-body); font-size: 11px; color: var(--ink-dim);
  background: transparent; border: 1px solid transparent; border-radius: 12px;
  padding: 4px 10px;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease,
    transform 140ms var(--ease);
}
.key-item:hover { color: var(--ink); border-color: var(--line); }
.key-item:active { transform: scale(0.96); }
.key-item:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.key-item[aria-pressed="true"] { color: var(--ink); }
.key-item.gains[aria-pressed="true"] { border-color: var(--gold); background: var(--gold-soft); }
.key-item.anchors[aria-pressed="true"] { border-color: var(--anchor); background: rgba(181, 133, 255, 0.14); }

.key-chip {
  width: 11px; height: 11px; border-radius: 3px; border: 2px solid;
  display: inline-block; flex: none;
  transition: background 160ms ease, transform 200ms var(--ease), box-shadow 200ms ease;
}
.key-chip.gains { border-color: var(--gold); }
.key-chip.anchors { border-color: var(--anchor); }
.key-item[aria-pressed="true"] .key-chip { transform: scale(1.2); }
.key-item.gains[aria-pressed="true"] .key-chip { background: var(--gold); box-shadow: 0 0 8px rgba(242, 199, 68, 0.7); }
.key-item.anchors[aria-pressed="true"] .key-chip { background: var(--anchor); box-shadow: 0 0 8px rgba(181, 133, 255, 0.7); }

.hero-filter {
  background: var(--bg-raise); border: 1px solid var(--line); color: var(--ink);
  padding: 7px 12px; font: inherit; width: 210px;
  clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
  transition: border-color 160ms ease, box-shadow 200ms ease, width 220ms var(--ease);
}
.hero-filter::placeholder { color: var(--ink-dim); transition: opacity 160ms ease; }
.hero-filter:focus { outline: none; border-color: var(--gold); width: 250px; box-shadow: 0 0 14px rgba(242, 199, 68, 0.22); }
.hero-filter:focus::placeholder { opacity: 0.4; }

/* ---------- Hero grid: the icon IS the button ---------- */
.role-group { margin-bottom: 22px; }
.role-group.no-match { display: none; }

.role-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: transparent; border: 0; padding: 4px 0; margin-bottom: 10px;
  font-family: var(--font-display); font-weight: 700; font-size: 15px;
  letter-spacing: 0.14em; text-transform: uppercase; text-align: left;
  transition: opacity 140ms ease;
}
.role-head:hover { opacity: 0.78; }
.role-head:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.role-head .role-mark { width: 14px; height: 14px; transform: skewX(var(--skew)); flex: none; }
.role-head .role-rule { flex: 1; height: 1px; background: var(--line); }
.role-count { display: inline-flex; align-items: center; gap: 7px; flex: none; }
.count-n {
  font-size: 11px; letter-spacing: 0.1em; color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 10px; padding: 0 7px;
}
.count-picked {
  font-size: 10px; letter-spacing: 0.08em; color: var(--gold);
  border: 1px solid rgba(242, 199, 68, 0.4); background: var(--gold-soft);
  border-radius: 10px; padding: 0 7px;
}
.role-caret { flex: none; font-size: 11px; color: var(--ink-dim); transition: transform 240ms var(--ease); }
.role-group.collapsed .role-caret { transform: rotate(-90deg); }
.role-group.vanguard .role-mark { background: var(--vanguard); }
.role-group.duelist .role-mark { background: var(--duelist); }
.role-group.strategist .role-mark { background: var(--strategist); }
.role-group.flex .role-mark { background: var(--flex); }

/* ---------- role glyphs (data/roles.json) ----------
   The datamined role art is white on transparency, so an element becomes its
   role's glyph by masking whatever background colour it already carries — one
   image per role, tinted by CSS, no per-colour variants. JS adds .role-glyph and
   sets --role-icon only when roles.json supplied a path, so a missing file leaves
   every one of these as the skewed colour block it was before.

   The URL must be absolute: a relative one inside a custom property resolves
   against this stylesheet, not the document (see applyRoleGlyph in js/app.js).
   Size is set per use site — a thin glyph needs a little more room than a block. */
/* No `transform: none` here: this selector is weaker than the ones that skew the
   blocks it replaces (.role-head .role-mark, .tally-dot), so it would lose and the
   glyph would render slanted 8 degrees — which reads as a wonky icon, not as a
   styled one. Each size rule below cancels the skew at matching specificity. */
.role-glyph {
  -webkit-mask: var(--role-icon) center / contain no-repeat;
  mask: var(--role-icon) center / contain no-repeat;
}

/* Sizes are height-first, matched to the cap height of the text each one sits
   beside, because that's the line the eye reads them against — a glyph taller than
   the capitals looks misaligned even when it's perfectly centred. The mapper trims
   these to a 1.5 ratio with the ink filling the canvas height, so `contain` renders
   the ink at exactly the box height as long as width >= height * 1.5. Widths are
   that bound; the extra is symmetric slack, not visible margin. */
.role-head .role-mark.role-glyph { width: 17px; height: 11px; transform: none; }  /* caps 10px */
.tally-dot.role-glyph { width: 15px; height: 10px; transform: none; }             /* caps 9px  */

/* On a slot, the glyph sits over the portrait, so it needs its own contrast
   rather than borrowing the card's. drop-shadow follows the masked shape (unlike
   box-shadow, which would trace the square box). */
.slot-role {
  position: absolute; left: 3px; bottom: 3px; width: 18px; height: 12px;
  background: var(--role-color, var(--ink));
  filter: drop-shadow(0 0 2px rgba(11, 14, 26, 0.95));
  pointer-events: none;
}
.slot.filled:hover .slot-role, .slot.filled:focus-visible .slot-role { opacity: 0; }
.role-group.vanguard .role-head { color: var(--vanguard); }
.role-group.duelist .role-head { color: var(--duelist); }
.role-group.strategist .role-head { color: var(--strategist); }
.role-group.flex .role-head { color: var(--flex); }

/* Collapse. Height is measured and set in JS rather than using the CSS-only
   grid 1fr->0fr trick, whose fr interpolation is uneven across browsers. */
.role-body {
  overflow: hidden;
  transition: height 280ms var(--ease), opacity 200ms ease;
}
.role-group.collapsed .role-body { opacity: 0; }

.card-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--hero-icon), 1fr));
  gap: 14px 12px;
  justify-items: center;
  padding-bottom: 2px;
}

/* The pick and its ⓘ button live side by side in a fixed-width cell so the
   info sits over the icon's corner. :has() hides the whole cell when the pick
   is filtered out, leaving no empty grid gap. */
.hero-cell { position: relative; width: var(--hero-icon); }
.hero-cell:has(.hero-pick.filtered-out) { display: none; }

.hero-pick {
  background: transparent; border: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  width: var(--hero-icon);
  color: var(--ink);
}
.hero-pick.filtered-out { display: none; }

/* ⓘ badge — kept out of the way until you hover or focus the card, so the grid
   stays clean; always shown on touch, where there's no hover to reveal it. */
.hero-info {
  position: absolute; top: 4px; right: 4px; z-index: 2;
  width: 20px; height: 20px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 700; font-size: 12px; line-height: 1;
  color: var(--ink); background: rgba(11, 14, 26, 0.82);
  border: 1px solid var(--line); border-radius: 50%;
  cursor: pointer; opacity: 0;
  transition: opacity 140ms ease, color 140ms ease, border-color 140ms ease,
    background 140ms ease, transform 140ms var(--ease);
}
.hero-cell:hover .hero-info, .hero-cell:focus-within .hero-info, .hero-info:focus-visible {
  opacity: 1;
}
.hero-info:hover, .hero-info:focus-visible {
  color: var(--gold); border-color: var(--gold); background: rgba(11, 14, 26, 0.95);
  transform: scale(1.12);
}
.hero-info:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
@media (hover: none) {
  .hero-info { opacity: 0.85; }
}
.hero-icon {
  width: var(--hero-icon); height: var(--hero-icon);
  border-radius: 8px; overflow: hidden; position: relative;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid transparent;
  background: linear-gradient(160deg, color-mix(in srgb, var(--card-role) 30%, #1a2050), #10142c);
  /* Dimmed by default so the picked and the highlighted read as lit-up. Any
     icon that means something (hover, on team, pulsing) drops the filter —
     it would desaturate the gold and violet glow along with the portrait. */
  filter: grayscale(0.45) brightness(0.82);
  transition: border-color 140ms ease, transform 140ms var(--ease), box-shadow 140ms ease,
    filter 160ms ease;
}
.hero-icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-icon .fallback {
  font-family: var(--font-display); font-weight: 700; font-size: 26px;
  color: color-mix(in srgb, var(--card-role) 80%, white);
}
.hero-name {
  font-family: var(--font-display); font-weight: 600; font-size: 12px; line-height: 1.1;
  text-transform: uppercase; letter-spacing: 0.03em; text-align: center;
  color: var(--ink-dim); transition: color 140ms ease;
}

.hero-pick:hover .hero-icon, .hero-pick:focus-visible .hero-icon {
  transform: translateY(-3px) scale(1.04); border-color: var(--card-role);
  filter: none;
}
.hero-pick:hover .hero-name, .hero-pick:focus-visible .hero-name { color: var(--ink); }
.hero-pick:focus-visible { outline: none; }
.hero-pick:focus-visible .hero-icon { outline: 2px solid var(--gold); outline-offset: 3px; }

.hero-pick.in-team .hero-icon { border-color: var(--card-role); filter: none; }
.hero-pick.in-team .hero-name { color: var(--ink); }
.hero-pick.in-team .hero-icon::after {
  content: "✓"; position: absolute; top: 2px; right: 5px;
  color: var(--gold); font-weight: 700; font-size: 14px; text-shadow: 0 1px 3px #000;
  animation: check-in 260ms var(--ease) both;
}
@keyframes check-in { 0% { opacity: 0; transform: scale(0.4) rotate(-25deg); } 100% { opacity: 1; transform: none; } }

/* Click feedback on the icon itself */
.hero-pick.bump .hero-icon { animation: bump 260ms var(--ease); }
@keyframes bump { 0% { transform: scale(1); } 40% { transform: scale(0.9); } 100% { transform: scale(1); } }

/* Filter cascade — results re-deal on each keystroke */
#hero-grid.cascade .hero-pick:not(.filtered-out) {
  animation: pop-in 190ms var(--ease) backwards;
  animation-delay: var(--stagger, 0ms);
}
@keyframes pop-in { 0% { opacity: 0; transform: scale(0.82) translateY(6px); } 100% { opacity: 1; transform: none; } }

/* ---------- The two-colour team-up language ---------- */
/* Solid gold — team-up already active */
.hero-pick.tu-active .hero-icon, .teamup-icon.tu-active {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(242, 199, 68, 0.5), 0 0 16px rgba(242, 199, 68, 0.35);
}
/* Pulsing gold — pick them and THEY gain an enhanced effect */
.hero-pick.tu-gains .hero-icon, .teamup-icon.tu-gains { animation: pulse-gold 1.5s ease-in-out infinite; }
.hero-pick.tu-gains .hero-name { color: var(--gold); }
/* Pulsing violet — pick them and they ANCHOR one for a teammate */
.hero-pick.tu-anchors .hero-icon, .teamup-icon.tu-anchors { animation: pulse-anchor 1.5s ease-in-out infinite; }
.hero-pick.tu-anchors .hero-name { color: var(--anchor); }
/* Both at once (~6%): the ring splits gold over violet, and the two glows take
   turns rather than sitting still — otherwise it reads as the only dead icon. */
.hero-pick.tu-both .hero-icon {
  border-color: var(--gold) var(--gold) var(--anchor) var(--anchor);
  animation: pulse-both 3s ease-in-out infinite;
}
.hero-pick.tu-gains .hero-icon, .hero-pick.tu-anchors .hero-icon, .hero-pick.tu-both .hero-icon { filter: none; }
.hero-pick.tu-both .hero-name {
  background: linear-gradient(90deg, var(--gold), var(--anchor), var(--gold));
  background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  animation: name-shift 3s linear infinite;
}

@keyframes pulse-gold {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242, 199, 68, 0.6), 0 0 8px rgba(242, 199, 68, 0.2); border-color: rgba(242, 199, 68, 0.5); }
  50% { box-shadow: 0 0 0 6px rgba(242, 199, 68, 0), 0 0 20px rgba(242, 199, 68, 0.5); border-color: var(--gold); }
}
@keyframes pulse-anchor {
  0%, 100% { box-shadow: 0 0 0 0 rgba(181, 133, 255, 0.6), 0 0 8px rgba(181, 133, 255, 0.2); border-color: rgba(181, 133, 255, 0.5); }
  50% { box-shadow: 0 0 0 6px rgba(181, 133, 255, 0), 0 0 20px rgba(181, 133, 255, 0.5); border-color: var(--anchor); }
}
/* Gold breathes in and out, then violet does the same — each half mirrors
   pulse-gold/pulse-anchor's own grow-then-shrink shape so the dual state
   doesn't read as a different animation, just two colours taking turns. The
   split border holds throughout so the state stays identifiable mid-cycle. */
@keyframes pulse-both {
  0%, 49%, 100% { box-shadow: 0 0 0 0 rgba(242, 199, 68, 0.6), 0 0 8px rgba(242, 199, 68, 0.2); }
  25% { box-shadow: 0 0 0 6px rgba(242, 199, 68, 0), 0 0 20px rgba(242, 199, 68, 0.5); }
  50%, 99% { box-shadow: 0 0 0 0 rgba(181, 133, 255, 0.6), 0 0 8px rgba(181, 133, 255, 0.2); }
  75% { box-shadow: 0 0 0 6px rgba(181, 133, 255, 0), 0 0 20px rgba(181, 133, 255, 0.5); }
}
@keyframes name-shift { to { background-position: -200% 0; } }

/* Hover focus — while a hovered hero's team-up connection is lit (.symp-focus on
   <body>, the hovered card marked .symp-source, its teammate slot pulsing), calm
   every other team-up pulse across the grid and side panel so the eye lands on
   the pair and nothing else competes. The hovered card keeps its own pulse. */
.symp-focus .hero-pick:is(.tu-active, .tu-gains, .tu-anchors, .tu-both):not(.symp-source) .hero-icon,
.symp-focus .teamup-icon:is(.tu-active, .tu-gains, .tu-anchors, .tu-both) {
  animation: none;
  box-shadow: none;
}
.symp-focus .hero-pick.tu-both:not(.symp-source) .hero-name { animation: none; }
.symp-focus .hero-pick:is(.tu-active, .tu-gains, .tu-anchors, .tu-both):not(.symp-source),
.symp-focus .teamup-icon:is(.tu-active, .tu-gains, .tu-anchors, .tu-both) {
  opacity: 0.4;
}
/* The selected roster fades back too — every filled slot and in-team grid card
   except the lit teammate — so only the hovered pair stays bright. */
.symp-focus .slot.filled:not(.symp-gain):not(.symp-anchor),
.symp-focus .hero-pick.in-team:not(.symp-source) {
  opacity: 0.4;
}
/* The team-up panel joins the same spotlight: the row(s) tied to the hovered
   hero glow in their group's colour (gold to gain, violet to anchor) while the
   rest of the list fades back — see highlightPanelRows() in js/app.js. */
.symp-focus .teamup-row:not(.tu-highlight) { opacity: 0.4; }
.symp-focus .teamup-row.tu-highlight .teamup-icon { opacity: 1; }
.teamup-row.tu-highlight {
  transform: translateX(2px);
  border-color: var(--gold);
  box-shadow: inset 0 0 0 1px var(--gold), 0 0 16px -4px var(--gold);
}
.teamup-group.anchors .teamup-row.tu-highlight {
  border-color: var(--anchor);
  box-shadow: inset 0 0 0 1px var(--anchor), 0 0 16px -4px var(--anchor);
}

.hero-pick:disabled { opacity: 0.3; cursor: not-allowed; }
/* Team is full — nothing here is one pick away any more, so drop the promise.
   Must outrank the pulse rules above, hence the order. */
.hero-pick:disabled .hero-icon {
  transform: none; animation: none; border-color: transparent; box-shadow: none;
  filter: grayscale(0.45) brightness(0.82);
}
.hero-pick:disabled .hero-name { color: var(--ink-dim); background: none; -webkit-text-fill-color: currentColor; }

/* ---------- Team-up panel ---------- */
.teamup-panel {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  padding: 18px 16px;
  align-self: start;
  position: sticky; top: calc(var(--topbar-h) + 12px);
  max-height: calc(100vh - var(--topbar-h) - 32px);
  overflow-y: auto;
  clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
}
.teamup-panel .section-title { margin-bottom: 12px; }

/* Bottom-sheet scaffolding. On desktop the peek bar and backdrop are hidden and
   the scroll wrapper is display:contents, so the panel lays out exactly as it
   did before this markup existed. Everything activates in the ≤960 query. */
.sheet-peek, .sheet-backdrop { display: none; }
.teamup-scroll { display: contents; }
/* Collapsible panel groups. Hiding "gains"/"anchors" also mutes that pulse in
   the hero grid — see updateGrid(). */
.teamup-group { margin-bottom: 14px; }
.group-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: transparent; border: 0; padding: 4px 0; margin-bottom: 8px;
  text-align: left; color: var(--ink-dim);
  font-family: var(--font-display); font-weight: 700; font-size: 11px;
  letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.25;
  transition: opacity 140ms ease;
}
.group-head:hover { opacity: 0.78; }
.group-head:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.group-dot { width: 10px; height: 10px; border-radius: 2px; flex: none; border: 2px solid currentColor; }
.teamup-group.active .group-head, .teamup-group.gains .group-head { color: var(--gold); }
.teamup-group.anchors .group-head { color: var(--anchor); }
.teamup-group.active .group-dot { background: currentColor; }
.group-title { flex: 1; min-width: 0; }
.group-count {
  flex: none; font-size: 10px; letter-spacing: 0.06em; color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: 10px; padding: 0 6px;
}
.group-caret { flex: none; font-size: 10px; transition: transform 240ms var(--ease); }
.teamup-group.collapsed .group-caret { transform: rotate(-90deg); }
.group-body { overflow: hidden; transition: height 280ms var(--ease), opacity 200ms ease; }
.teamup-group.collapsed .group-body { opacity: 0; }

/* CSS `order` sorts the list, so rows never move in the DOM and their
   pulse animations never restart. */
.teamup-list { display: flex; flex-direction: column; }

.teamup-row {
  border: 1px solid var(--line); background: var(--bg-raise);
  margin-bottom: 8px;
  transition: opacity 220ms ease, transform 240ms var(--ease), max-height 240ms var(--ease),
    margin 240ms var(--ease), border-color 200ms ease;
}
.teamup-row.entering { opacity: 0; transform: translateY(-8px) scale(0.97); }
.teamup-row.leaving { opacity: 0; transform: translateX(14px); overflow: hidden; margin-bottom: 0; pointer-events: none; }
/* The suggested pick's role section is collapsed on the left, so they aren't
   visible there either — mute the row rather than suggest a hero the grid
   is currently hiding. Still clickable: collapsing a role is a grid display
   choice, not a restriction on who can be added. */
.teamup-row.pick-hidden { opacity: 0.5; }
.teamup-row.pick-hidden .teamup-icon { animation: none; border-color: var(--line); box-shadow: none; }
.teamup-row.pick-hidden .add-partner { border-color: var(--line); color: var(--ink-dim); }
.teamup-row.pick-hidden .add-partner:hover, .teamup-row.pick-hidden .add-partner:focus-visible {
  background: var(--bg-raise); border-color: var(--line); color: var(--ink);
}

.teamup-head {
  width: 100%; background: transparent; border: 0; padding: 8px 10px;
  display: flex; align-items: center; gap: 10px; text-align: left; color: var(--ink);
}
.teamup-head:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.teamup-icon {
  width: 46px; height: 46px; flex: none;
  border-radius: 6px; border: 2px solid var(--line);
  background: #10142c; overflow: hidden;
}
.teamup-icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.teamup-meta { flex: 1; min-width: 0; }
.teamup-name {
  display: block;
  font-family: var(--font-display); font-weight: 700; font-size: 13px;
  text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.15;
}
.teamup-members { display: flex; gap: 4px; margin-top: 5px; }
.member {
  width: 24px; height: 24px; border-radius: 4px; overflow: hidden;
  border: 1px solid var(--line); background: #10142c; flex: none; display: block;
  transition: filter 180ms ease, border-color 180ms ease, opacity 180ms ease;
}
.member img { width: 100%; height: 100%; object-fit: cover; display: block; }
.member.has { border-color: var(--gold); }
.member.missing { filter: grayscale(1) brightness(0.5); opacity: 0.7; }
.expand-caret { color: var(--ink-dim); font-size: 11px; flex: none; transition: transform 220ms var(--ease); }
.teamup-row.open .expand-caret { transform: rotate(90deg); }

/* Add the missing hero straight from the panel */
.teamup-add:empty { display: none; }
.add-partner {
  display: block; width: calc(100% - 20px); margin: 0 10px 9px;
  background: transparent; border: 1px solid var(--line); color: var(--ink-dim);
  padding: 5px 10px;
  font-family: var(--font-display); font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.08em; text-align: center;
  clip-path: polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.add-partner.tu-gains { border-color: rgba(242, 199, 68, 0.45); color: var(--gold); }
.add-partner.tu-gains:hover, .add-partner.tu-gains:focus-visible {
  background: var(--gold-soft); border-color: var(--gold); color: var(--ink);
}
.add-partner.tu-anchors { border-color: rgba(181, 133, 255, 0.45); color: var(--anchor); }
.add-partner.tu-anchors:hover, .add-partner.tu-anchors:focus-visible {
  background: rgba(181, 133, 255, 0.14); border-color: var(--anchor); color: var(--ink);
}
.add-partner:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }
.add-partner:disabled { opacity: 0.35; cursor: not-allowed; }

/* Height measured in JS, same as the role groups */
.teamup-body-wrap {
  overflow: hidden; height: 0;
  transition: height 240ms var(--ease);
}
.teamup-body > :last-child { margin-bottom: 10px; }
.teamup-effect { font-size: 13px; margin: 8px 10px 0; }
.teamup-effect em {
  color: var(--gold); font-style: normal; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.1em; display: block; margin-bottom: 2px;
}
.teamup-effect.base em { color: var(--ink-dim); }
.effect-missing { color: var(--ink-dim); font-style: italic; }
.empty-msg { color: var(--ink-dim); font-size: 13px; font-style: italic; margin: 0; }
.empty-msg[hidden] { display: none; }

/* ---------- About / FAQ ----------
   Static prose below the builder (see the comment above <section class="about">
   in index.html). Deliberately quieter than the tool: dimmed body text, no
   panel fills, so it reads as reference material someone scrolls down to rather
   than competing with the hero grid. Matches .layout's 1280px measure, but caps
   prose at ~68ch since full-width lines are unreadable on a wide monitor. */
.about { border-top: 1px solid var(--line); }
.about-inner {
  max-width: 1280px; margin: 0 auto; padding: 34px 20px 10px;
  font-family: var(--font-body); color: var(--ink-dim); font-size: 14px; line-height: 1.65;
}
.about h2 {
  font-family: var(--font-display); font-size: 22px; font-weight: 700;
  color: var(--ink); margin: 0 0 14px; letter-spacing: 0.2px;
}
.about h3 {
  font-family: var(--font-display); font-size: 15px; font-weight: 700;
  color: var(--ink); margin: 26px 0 8px; letter-spacing: 0.3px; text-transform: uppercase;
}
.about p { margin: 0 0 12px; max-width: 68ch; }
.about-lede { color: var(--ink); font-size: 15px; }
.about strong { color: var(--ink); font-weight: 600; }

/* The preview card (img/preview.png). Intrinsically 1200x1200 so the file is
   large enough for Google's image preview; displayed far smaller. `height: auto`
   is required — the width/height attributes on the <img> are there to reserve
   the aspect ratio, and without this the element would render 1200px tall. */
.about-figure { margin: 18px 0 20px; max-width: 380px; }
.about-figure img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: 10px;
}
.about-figure figcaption {
  margin-top: 8px; font-size: 12px; color: var(--ink-dim); line-height: 1.45;
}

/* Two even columns on desktop, stacked on narrow screens. */
.about-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 40px; }
.about-cols > div > h3:first-child { margin-top: 18px; }
.about-cols ul, .about-cols ol { margin: 0; padding-left: 20px; }
.about-cols li { margin-bottom: 7px; }
.about-cols li::marker { color: var(--gold); }

/* FAQ as a <dl> so the Q/A pairing is real markup, not styling. */
.faq { margin: 0; max-width: 78ch; }
.faq dt {
  color: var(--ink); font-weight: 600; margin-top: 16px;
  /* A slim gold rule instead of a bullet: reads as a question without a glyph. */
  border-left: 2px solid var(--gold-soft); padding-left: 10px;
}
.faq dd { margin: 5px 0 0; padding-left: 12px; max-width: 68ch; }

.about-cta { margin: 26px 0 0; }
.about-cta a { color: var(--gold); font-weight: 600; text-decoration: none; }
.about-cta a:hover { text-decoration: underline; }

@media (max-width: 720px) {
  .about-cols { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .about-inner { padding-top: 26px; }
}

/* ---------- Footer ---------- */
.site-foot { border-top: 1px solid var(--line); margin-top: 8px; }
.site-foot p {
  max-width: 1280px; margin: 0 auto; padding: 14px 20px 0; color: var(--ink-dim); font-size: 12px;
}
.site-foot p:last-child { padding-bottom: 18px; padding-top: 6px; }
.site-foot .foot-links { padding-top: 18px; font-size: 14px; }
.site-foot .foot-links a { color: var(--gold); font-weight: 600; text-decoration: none; }
.site-foot .foot-links a:hover { text-decoration: underline; }

/* ---------- Motion off ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* The pulses carry meaning, so keep a static ring where motion would have been */
  .hero-pick.tu-gains .hero-icon, .teamup-icon.tu-gains { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(242, 199, 68, 0.4); }
  .hero-pick.tu-anchors .hero-icon, .teamup-icon.tu-anchors { border-color: var(--anchor); box-shadow: 0 0 0 2px rgba(181, 133, 255, 0.4); }
  .hero-pick.tu-both .hero-icon { border-color: var(--gold) var(--gold) var(--anchor) var(--anchor); }
  .slot.symp-gain .slot-icon { border-color: var(--gold); }
  .slot.symp-anchor .slot-icon { border-color: var(--anchor); }
}

/* ---------- Responsive ---------- */
/* The team-up panel newly activates as a live pulse of the peek bar. */
@keyframes peek-pulse {
  0% { background: transparent; }
  30% { background: var(--gold-soft); }
  100% { background: transparent; }
}

@media (max-width: 960px) {
  .layout { grid-template-columns: 1fr; }
  /* Keep the footer clear of the always-present peek bar. */
  body { padding-bottom: var(--peek-h); }

  /* The panel becomes a bottom sheet: only the peek bar shows until tapped,
     then it rises to at most half the screen with the list scrolling inside.
     Collapsed = translated down so just the top --peek-h stays on screen. */
  .teamup-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 50;
    margin: 0; padding: 0;
    max-height: 50vh; max-height: 50dvh;
    display: flex; flex-direction: column; overflow: visible;
    clip-path: none; border: 1px solid var(--line); border-bottom: 0;
    border-radius: 16px 16px 0 0;
    transform: translateY(calc(100% - var(--peek-h)));
    transition: transform 340ms var(--ease);
  }
  .teamup-panel.sheet-open { transform: translateY(0); }
  /* The peek bar is the sheet's header, so the in-panel title is redundant. */
  .teamup-panel .section-title { display: none; }

  .sheet-peek {
    display: flex; align-items: center; gap: 10px;
    flex: none; width: 100%; height: var(--peek-h);
    padding: 0 16px; position: relative;
    background: transparent; border: 0; color: var(--ink);
    /* The whole bar is a drag handle: claim vertical gestures so they move the
       sheet instead of scrolling the page, and don't select the label text. */
    touch-action: none; user-select: none; -webkit-user-select: none;
    cursor: grab;
  }
  .sheet-peek:active { cursor: grabbing; }
  .sheet-peek:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
  .sheet-peek.pulse { animation: peek-pulse 640ms var(--ease); }
  .sheet-grip {
    position: absolute; top: 7px; left: 50%; transform: translateX(-50%);
    width: 34px; height: 4px; border-radius: 3px; background: var(--line);
  }
  .peek-dot-icon {
    width: 10px; height: 10px; border-radius: 2px; flex: none; background: var(--line);
  }
  .sheet-peek.has-active .peek-dot-icon { background: var(--gold); }
  .peek-counts { display: flex; align-items: center; gap: 8px; font-family: var(--font-display); }
  #peek-active {
    font-weight: 700; font-size: 13px; letter-spacing: 0.05em; text-transform: uppercase;
  }
  .peek-sep { color: var(--line); }
  .peek-away { font-size: 12px; color: var(--ink-dim); letter-spacing: 0.02em; }
  .peek-caret {
    margin-left: auto; color: var(--ink-dim); font-size: 12px;
    transition: transform 300ms var(--ease);
  }
  .teamup-panel.sheet-open .peek-caret { transform: rotate(180deg); }

  .teamup-scroll {
    display: block; flex: 1; min-height: 0;
    overflow-y: auto; padding: 2px 16px 18px;
  }

  .sheet-backdrop {
    display: block; position: fixed; inset: 0; z-index: 45;
    background: rgba(4, 6, 14, 0.55);
    opacity: 0; pointer-events: none;
    transition: opacity 320ms var(--ease);
  }
  .sheet-backdrop.show { opacity: 1; pointer-events: auto; }
}
@media (max-width: 560px) {
  :root { --hero-icon: 68px; }
  .slot { width: 52px; }
  .slot-icon { width: 46px; height: 46px; }
  .slot-name { max-width: 52px; }
  .team-bar-inner { gap: 10px; }
  .hero-filter, .hero-filter:focus { width: 100%; }
  .key { order: 3; width: 100%; }
  .team-bar-actions { margin-left: 0; width: 100%; }
  .kofi, .contact { flex: 1; justify-content: center; }

  /* Comfortable ≥44px touch targets on phones: the masthead actions
     ("Copy team link" / "Copy as text" / "Clear team") and the grid's
     gains/anchors toggles were ~31px and ~26px tall. */
  .masthead-meta .ghost-btn {
    min-height: 44px; display: inline-flex; align-items: center;
  }
  .key-item { min-height: 44px; padding-left: 12px; padding-right: 12px; }

  /* The full topbar is ~60% of a phone screen, so pinning the whole thing sticky
     buries the grid. The old fix toggled a "condensed" class from a scroll
     listener, but shrinking a stuck bar at the very top of the page moved the
     scroll position out from under the reader — the toggle fired, the page
     jumped, the toggle fired again: a flicker at the threshold.

     Instead, flatten the bar into normal flow (display:contents) and let only
     the team-slots strip stay sticky. The masthead and footer scroll away like
     any other content — no JS, no scroll-jank — and the team
     row alone pins to the top as a slim strip once the rest has scrolled off. */
  .topbar, .team-bar, .team-bar-inner { display: contents; }
  .masthead {
    background: linear-gradient(180deg, rgba(11, 14, 26, 0.97), rgba(11, 14, 26, 0.93));
  }
  /* The pinned strip: Copy link on the left, the team centred, Clear on the
     right. app.js moves the two buttons in here from the masthead on mobile. */
  .team-strip {
    display: flex; align-items: center; gap: 6px;
    position: sticky; top: 0; z-index: 30;
    padding: 7px 8px;
    background: linear-gradient(180deg, rgba(11, 14, 26, 0.97), rgba(11, 14, 26, 0.93));
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--line);
  }
  .team-strip .team-slots {
    flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; justify-content: center; gap: 4px;
  }
  .team-strip .slot { width: 34px; }
  .team-strip .slot-icon { width: 33px; height: 33px; }
  .team-strip .slot-name { display: none; }
  /* Side actions sized to the strip; the short labels (set in app.js) keep them
     to a line or two. flex:none so the team keeps the centre. */
  .team-strip .ghost-btn {
    flex: 0 0 auto; width: 62px; min-height: 42px; padding: 5px 4px;
    font-size: 11px; line-height: 1.15; letter-spacing: 0.03em; white-space: normal;
  }
}
