/* ===== /css/mobile-first-2026-04-30.css ===== */
/* ============================================================================
 * BLOODCAFÉ — Mobile-First Responsive Override (2026-04-30)
 * ----------------------------------------------------------------------------
 * Implements the squad-side responsive spec. Loaded LAST in <head> so it
 * overrides any older desktop-only or partial mobile styles.
 *
 * Hard rules enforced here:
 *   - No horizontal scroll
 *   - 16px+ inputs (prevents iOS auto-zoom)
 *   - 44px tap targets
 *   - Tables stack on phones
 *   - All layout containers are flexible width
 *
 * Breakpoints (mobile-first, additive):
 *   base       → phones (320px+)
 *   480px+     → large phones / small tablets
 *   768px+     → tablets
 *   1024px+    → laptops
 *   1280px+    → desktop
 * ============================================================================ */

/* ── BOX MODEL + SCROLL CONTAINMENT ──────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
}

/* ── MEDIA: never overflow viewport ──────────────────────────────────────── */
img, video, canvas, svg, picture, iframe {
  max-width: 100%;
  height: auto;
}

/* ── FLUID CONTAINERS ────────────────────────────────────────────────────── */
.container, .card, .panel, .section,
.wrapper, .content, .main, .page,
main, article, section, header, footer, nav {
  max-width: 100%;
  box-sizing: border-box;
}

/* ── FLUID TYPOGRAPHY (per spec) ─────────────────────────────────────────── */
body {
  font-size: clamp(14px, 1.8vw, 18px);
  line-height: 1.5;
}
h1 { font-size: clamp(24px, 6vw, 42px); line-height: 1.2; }
h2 { font-size: clamp(20px, 4vw, 32px); line-height: 1.25; }
h3 { font-size: clamp(18px, 3vw, 26px); line-height: 1.3; }

/* ── TOUCH-FRIENDLY INPUTS (44px tap targets, no iOS zoom) ──────────────── */
/* !important is required here — older styles.css/mobile.css set 15px on
   `input[type="text"]` etc which beats the bare `input` selector by specificity.
   This is the one place !important is the correct hammer. */
button, input, select, textarea,
input[type="text"], input[type="search"], input[type="email"],
input[type="tel"], input[type="url"], input[type="password"],
input[type="number"], input[type="date"], input[type="datetime-local"],
input[type="time"], input[type="month"] {
  font-size: 16px !important;          /* Prevents iOS Safari auto-zoom on focus */
  min-height: 44px;
  font-family: inherit;
}
input[type="checkbox"], input[type="radio"] {
  min-height: auto !important;
  min-width: 20px;
  min-height: 20px;
}
button, [role="button"], a.btn, a.button {
  min-height: 44px;
  padding: 10px 14px;
  cursor: pointer;
  touch-action: manipulation;
}

/* ── PHONE-SPECIFIC LAYOUT (≤ 767px) ────────────────────────────────────── */
@media (max-width: 767px) {
  body {
    padding: 0;
    margin: 0;
  }

  /* Stack flex/grid containers on phones unless they explicitly opt out */
  .row, .flex-row, .columns, .col-row, .grid:not(.grid-keep-mobile) {
    flex-direction: column !important;
    grid-template-columns: 1fr !important;
    gap: 12px;
  }

  /* Form fields: full width */
  input, select, textarea, button:not(.icon-btn):not(.tag) {
    width: 100%;
  }

  /* ── Wide TABLES become stacked CARDS ──
     Pattern: each <tr> becomes a card; each <td> becomes a labeled row.
     If the markup includes `data-label="X"` on <td>, that label shows.
     If not, the raw cell content stacks vertically — still readable. */
  table.responsive,
  table[data-mobile="cards"],
  .table-mobile-cards table,
  table:not(.no-stack) {
    display: block;
    width: 100%;
    border-collapse: collapse;
    overflow-x: auto;
  }
  table:not(.no-stack) thead {
    /* Hide header on mobile — labels come from data-label or row context */
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
  table:not(.no-stack) tbody,
  table:not(.no-stack) tr {
    display: block;
    width: 100%;
  }
  table:not(.no-stack) tr {
    margin-bottom: 12px;
    padding: 12px;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 8px;
    background: rgba(255,255,255,0.02);
  }
  table:not(.no-stack) td {
    display: block;
    padding: 6px 0;
    border: none;
    text-align: left;
    word-wrap: break-word;
    overflow-wrap: anywhere;
  }
  /* If data-label is present, show it as an inline label */
  table:not(.no-stack) td[data-label]::before {
    content: attr(data-label) ": ";
    font-weight: 700;
    color: rgba(0, 217, 255, 0.85);
    margin-right: 6px;
    text-transform: uppercase;
    font-size: 0.78em;
    letter-spacing: 0.04em;
  }

  /* Fix common desktop layout patterns: hard-cap card widths */
  .card, .panel, .section, .tile {
    width: 100% !important;
    max-width: 100% !important;
    margin-left: 0;
    margin-right: 0;
  }

  /* Headings shouldn't have huge desktop margins */
  h1, h2, h3 { margin-top: 0.6em; margin-bottom: 0.4em; }

  /* Long text shouldn't blow out the row */
  pre, code, .code-block {
    overflow-x: auto;
    max-width: 100%;
    white-space: pre-wrap;
    word-wrap: break-word;
  }

  /* Sticky top nav (where present): make sure tap targets are big */
  nav, .navbar, .top-nav, header.site-header {
    width: 100%;
    max-width: 100%;
  }
  nav a, .navbar a, .top-nav a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: 8px 12px;
  }

  /* Hover-only effects shouldn't gate anything on touch devices */
  .hover-only, .desktop-only {
    display: none !important;
  }
  .mobile-only {
    display: block !important;
  }
}

/* ── LARGE PHONES / SMALL TABLETS (≥ 480px) ─────────────────────────────── */
@media (min-width: 480px) {
  /* No structural change — just slight padding bump */
  .container, .page, main { padding-left: 12px; padding-right: 12px; }
}

/* ── TABLETS (≥ 768px) ──────────────────────────────────────────────────── */
@media (min-width: 768px) {
  /* Tables can be tables again */
  table:not(.no-stack) { display: table; }
  table:not(.no-stack) thead { position: static; width: auto; height: auto; }
  table:not(.no-stack) tbody, table:not(.no-stack) tr { display: table-row-group; }
  table:not(.no-stack) tr { display: table-row; margin: 0; padding: 0; border: none; background: transparent; }
  table:not(.no-stack) td { display: table-cell; padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.06); }
  table:not(.no-stack) td[data-label]::before { content: none; }

  /* Multi-column layouts come back */
  .row, .flex-row, .columns, .col-row {
    flex-direction: row !important;
  }
  .desktop-only { display: revert !important; }
  .mobile-only { display: none !important; }
}

/* ── LAPTOPS (≥ 1024px) ─────────────────────────────────────────────────── */
@media (min-width: 1024px) {
  .container, .wrapper, main { max-width: 1200px; margin-left: auto; margin-right: auto; }
}

/* ── DESKTOP (≥ 1280px) ─────────────────────────────────────────────────── */
@media (min-width: 1280px) {
  .container, .wrapper, main { max-width: 1400px; }
}

/* ── ACCESSIBILITY: respect reduced-motion + dark/contrast prefs ────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── LAST-RESORT VIEWPORT GUARD (catches anything that escapes box-sizing) */
@media (max-width: 767px) {
  body * {
    max-width: 100vw;
  }

  /* Kill any `min-width: NNNpx` on layout containers — most common cause of
     horizontal scroll on phones (e.g. rank-calculator min-width: 900px,
     sphere-guide min-width: 500px). */
  table, .stage-table, .sphere-grid, .sphere-card,
  .container, .wrap, .wrapper, .content, .main, .page, .card, .panel, .section,
  [class*="grid"], [class*="card"], [class*="panel"], [class*="container"],
  [class*="wrap"], [class*="row"], [class*="col"],
  section, article, main, header, footer, nav,
  div, ul, ol, dl, form, fieldset {
    min-width: 0 !important;
  }

  /* Cap any inline pixel width >= 360px so it can't overflow phone screens */
  [style*="width:360px"], [style*="width: 360px"],
  [style*="width:380px"], [style*="width: 380px"],
  [style*="width:400px"], [style*="width: 400px"],
  [style*="width:420px"], [style*="width: 420px"],
  [style*="width:440px"], [style*="width: 440px"],
  [style*="width:480px"], [style*="width: 480px"],
  [style*="width:500px"], [style*="width: 500px"],
  [style*="width:520px"], [style*="width: 520px"],
  [style*="width:540px"], [style*="width: 540px"],
  [style*="width:560px"], [style*="width: 560px"],
  [style*="width:580px"], [style*="width: 580px"],
  [style*="width:600px"], [style*="width: 600px"],
  [style*="width:640px"], [style*="width: 640px"],
  [style*="width:680px"], [style*="width: 680px"],
  [style*="width:700px"], [style*="width: 700px"],
  [style*="width:720px"], [style*="width: 720px"],
  [style*="width:760px"], [style*="width: 760px"],
  [style*="width:800px"], [style*="width: 800px"],
  [style*="width:900px"], [style*="width: 900px"],
  [style*="width:960px"], [style*="width: 960px"],
  [style*="width:1000px"], [style*="width: 1000px"],
  [style*="min-width:"], [style*="min-width: "] {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
  }
}

/* ===== /css/reaper.css ===== */
/* ============================================================
   BLOOD CAFÉ — "REAPER" theme  (sniper / grim-reaper / Punisher)
   Built 2026-06-01. Hard art direction: black + bone + blood-red,
   gun-grit grain, sniper scope reticle, skull watermark, stencil type.
   Layered + reversible (remove the <link> to revert).
   ============================================================ */
/* Self-hosted 2026-09-03. Replaces a render-blocking @import to fonts.googleapis.com
   that sat on ~66 pages: it serialised HTML -> reaper.css -> googleapis -> gstatic
   before a single heading could paint, and leaked a third-party request on every
   view. Both faces are SIL Open Font License. font-display:swap keeps text visible
   while they load. Oswald ships as one variable file covering weights 500-700. */
@font-face{font-family:'Oswald';font-style:normal;font-weight:200 700;font-display:swap;
  src:url('/fonts/oswald-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:200 700;font-display:swap;
  src:url('/fonts/oswald-latin.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Black Ops One';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/blackopsone-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}
@font-face{font-family:'Black Ops One';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/blackopsone-latin.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}

:root{
  --bg:#060607; --bg2:#0b0b0d; --panel:#0d0d10; --panel2:#121216;
  --bone:#e9e6dd; --ash:#8d877c;
  /* --ash-dim was #5b564e: 2.78:1 on the panel background, well under the 4.5:1
     WCAG AA minimum. Lightened along the same hue to the smallest value that
     clears AA (4.52:1) so the tone is preserved. 2026-09-03. */
  --ash-dim:#898376;
  --blood:#b00710; --blood-hi:#ff2530; --blood-glow:rgba(190,12,20,.55);
  --steel:#23262c;
  --neon-blue:#ff2530; --neon-cyan:#ff2530;     /* hijack old cyan -> blood */
  --neon-purple:#ff2530; --cyber-purple:#b00710; --neon-magenta:#ff2530; /* kill purple */
  /* Headings are painted with this gradient through -webkit-background-clip:text.
     The old ramp ended on #b00710 (2.78:1) and #7a050c (1.79:1) against the near-black
     page, so the hero wordmark and every gradient section title were effectively
     unreadable — the single worst visual defect on the site. This ramp keeps the
     bone-and-blood art direction the theme's own header describes while holding every
     stop at 4.5:1 or better. 2026-09-03. */
  --gradient-primary:linear-gradient(135deg,#ffe9e6 0%,#ff5a4f 52%,#e0313c 100%)!important;
  --gradient-secondary:linear-gradient(135deg,#ff2530,#b00710)!important;
  /* --text-muted was #7d776c: 3.89-4.26:1, failing AA on every panel. Raised to
     the minimum same-hue value that clears 4.5:1. This single token was the
     source of most measured contrast failures site-wide. 2026-09-03. */
  --text-primary:#e9e6dd; --text-secondary:#b9b3a8; --text-muted:#898376;
  --border:rgba(233,230,221,.10); --border-bright:rgba(255,37,48,.45);
}

/* ---------- base + grit atmosphere ---------- */
body{ background:var(--bg)!important; color:var(--bone)!important; position:relative; }
body::before{ /* film grain + blood vignette + faint scope behind hero */
  content:"";position:fixed;inset:0;pointer-events:none;z-index:-1;opacity:.9;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E"),
    radial-gradient(60% 42% at 50% 16%, rgba(176,7,16,.16), transparent 70%),
    radial-gradient(130% 120% at 50% 50%, transparent 58%, rgba(0,0,0,.72) 100%);
}
body:has(.reaper-hero)::after{ /* sniper scope reticle — LANDING ONLY (not every page) */
  content:"";position:fixed;top:-160px;left:50%;transform:translateX(-50%);
  width:760px;height:760px;pointer-events:none;z-index:-1;opacity:.14;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400' fill='none' stroke='%23ff2530' stroke-width='1.2'%3E%3Ccircle cx='200' cy='200' r='150'/%3E%3Ccircle cx='200' cy='200' r='96'/%3E%3Ccircle cx='200' cy='200' r='3' fill='%23ff2530'/%3E%3Cline x1='200' y1='20' x2='200' y2='150'/%3E%3Cline x1='200' y1='250' x2='200' y2='380'/%3E%3Cline x1='20' y1='200' x2='150' y2='200'/%3E%3Cline x1='250' y1='200' x2='380' y2='200'/%3E%3Cg stroke-width='2'%3E%3Cline x1='200' y1='112' x2='200' y2='128'/%3E%3Cline x1='200' y1='272' x2='200' y2='288'/%3E%3Cline x1='112' y1='200' x2='128' y2='200'/%3E%3Cline x1='272' y1='200' x2='288' y2='200'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat;
}
/* atmosphere lives at z-index:-1 (behind content) so we DON'T force position on
   body children — forcing it broke fixed/sticky headers & blocked clicks. */

/* ---------- typography: military stencil ---------- */
h1,h2,.section-title,.hero-title,.logo-text{
  font-family:'Black Ops One','Oswald',var(--font,sans-serif)!important;
  letter-spacing:.04em;color:var(--bone)!important;text-transform:uppercase;
}
/* No text-shadow on gradient-clipped headings. styles.css fills every h1 through
   -webkit-background-clip:text with a transparent fill, and an opaque shadow drawn
   from the same glyph shape swamps the gradient — the heading renders as a dark
   smudge with coloured edges. Verified by toggling the shadow off in the browser:
   the gradient reappears immediately. The hero keeps its halo because it is
   explicitly re-filled with a solid colour further down. 2026-09-03. */
h1,.hero-title{ text-shadow:none; }
h3,h4,.stat-label,.qa-text{ font-family:'Oswald',sans-serif!important; letter-spacing:.12em; }

/* ---------- panels: gun-metal slab, bone hairline, blood top-cut ---------- */
.panel,.stat-card,.quick-action-btn,.bs-cell,.sp-bi{
  position:relative;border-radius:2px!important;
  background:linear-gradient(180deg, rgba(18,18,22,.96), rgba(10,10,13,.98))!important;
  border:1px solid var(--border)!important;
  box-shadow:0 10px 30px rgba(0,0,0,.7), inset 0 1px 0 rgba(233,230,221,.04);
}
.panel::before,.stat-card::before{ /* blood top edge */
  content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:linear-gradient(90deg,transparent,var(--blood-hi),transparent);opacity:.8;
}
.panel:hover,.stat-card:hover,.quick-action-btn:hover{
  border-color:var(--border-bright)!important;
  box-shadow:0 0 0 1px rgba(255,37,48,.25),0 14px 38px rgba(0,0,0,.75),0 0 30px var(--blood-glow)!important;
}

/* ---------- section titles: stenciled with crosshair tick ---------- */
.section-title{display:flex;align-items:center;gap:.65rem;font-size:1.05rem;}
.section-title::before{
  content:"✛";color:var(--blood-hi);font-size:1.1em;
  text-shadow:0 0 12px var(--blood-glow);transform:translateY(-1px);
}
.section-title::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--border-bright),transparent);}

/* ---------- stat readouts ---------- */
.stat-label{color:var(--ash)!important;text-transform:uppercase;font-size:.64rem;}
.stat-value{color:var(--bone)!important;text-shadow:0 0 18px var(--blood-glow);font-variant-numeric:tabular-nums;}
.stat-icon{filter:drop-shadow(0 0 8px var(--blood-glow));}

/* ---------- buttons: blood, sharp, lethal ---------- */
.btn,.btn-primary,button.btn,.quick-action-btn{
  text-transform:uppercase;letter-spacing:.1em;font-weight:700;border-radius:2px;
}
.btn-primary,.btn.btn-primary{
  background:linear-gradient(135deg,var(--blood),#7a050c)!important;color:var(--bone)!important;
  border:1px solid rgba(255,37,48,.5)!important;box-shadow:0 0 20px var(--blood-glow);
}
.btn-primary:hover{box-shadow:0 0 34px var(--blood-hi),0 0 14px var(--blood-glow);transform:translateY(-1px);}
.quick-action-btn{overflow:hidden;}
.quick-action-btn::after{
  content:"";position:absolute;inset:0;transform:translateX(-120%);
  background:linear-gradient(100deg,transparent,rgba(255,37,48,.18) 50%,transparent);transition:transform .5s ease;
}
.quick-action-btn:hover::after{transform:translateX(120%);}
.qa-icon{filter:drop-shadow(0 0 7px var(--blood-glow));}

/* ---------- inputs: terminal ---------- */
.input,input[type=text],input[type=password]{
  background:#08080a!important;border:1px solid var(--border)!important;color:var(--bone)!important;
}
.input:focus,input:focus{border-color:var(--blood-hi)!important;box-shadow:0 0 0 1px var(--blood-hi),0 0 18px var(--blood-glow)!important;outline:none!important;}

/* ---------- chips/badges: dog-tag stencil ---------- */
.legend-chip,.tag-badge,.pill,.sp-role,.sp-rarity{
  text-transform:uppercase;letter-spacing:.06em;border-radius:2px;
  border:1px solid var(--border)!important;background:rgba(255,37,48,.06)!important;color:var(--bone)!important;
}

/* Pull leftover cyan accents toward blood.
   Match the COLOUR declaration only. The old form was [style*="#00d9ff"], which fired
   on any inline style that merely mentioned cyan — including the 7 places where cyan is
   the BACKGROUND. Those got blood-red text on a cyan tint: 3.9:1, under AA, and unrelated
   to anything the brand pass was trying to achieve. */
[style*="color:#00d9ff"],[style*="color: #00d9ff"],
[style*="color:#00ffff"],[style*="color: #00ffff"]{color:var(--blood-hi)!important;}
a{color:var(--blood-hi);}
::selection{background:var(--blood);color:#fff;}

/* HERO v5 — dedicated splash element (.reaper-hero) injected ABOVE the configurator,
   fully self-contained so it doesn't fight the existing #heroSection collapse/CSS.
   display:grid here is SAFE — it only centers this element's own h1 (no page content). */
.reaper-hero{
  position:relative; isolation:isolate; overflow:hidden;
  min-height:clamp(360px,54vh,560px); margin:0 0 1.4rem; border:1px solid var(--border); border-radius:3px;
  display:grid; place-items:end center; text-align:center; padding:2rem 1.5rem 3.2rem;
  background:
    linear-gradient(180deg, rgba(6,6,7,.30) 0%, rgba(6,6,7,0) 26%, rgba(6,6,7,.08) 50%, rgba(6,6,7,.92) 100%),
    url('/img/gen-hero2.jpg') center 40% / cover no-repeat;
}
.reaper-hero::after{ /* scanline + scope vignette over the painting */
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:repeating-linear-gradient(0deg,transparent 0 2px,rgba(0,0,0,.10) 2px 3px),
    radial-gradient(120% 120% at 50% 45%, transparent 58%, rgba(6,6,7,.55) 100%);
}
.reaper-hero > .rh-inner{ position:relative; z-index:2; }
.reaper-hero h1{
  font-family:'Black Ops One','Oswald',sans-serif; font-weight:400; margin:0; color:var(--bone);
  font-size:clamp(3rem,9vw,7rem); line-height:.86; letter-spacing:.03em; text-transform:uppercase;
  text-shadow:0 3px 0 #000, 0 0 38px rgba(0,0,0,.92), 0 0 54px var(--blood-glow);
}
.reaper-hero .rh-tag{
  margin-top:.7rem; font-family:'Oswald',sans-serif; font-weight:700; color:var(--blood-hi);
  letter-spacing:.42em; text-indent:.42em; text-transform:uppercase;
  font-size:clamp(.72rem,1.7vw,1.05rem); text-shadow:0 0 18px var(--blood-glow), 0 1px 4px #000;
}
.reaper-hero .rh-sub{ margin-top:.5rem; color:var(--ash); letter-spacing:.06em; font-size:.9rem; text-shadow:0 1px 6px #000; }
@media (prefers-reduced-motion:no-preference){
  .reaper-hero h1{ animation:rhIn .7s ease both; }
  @keyframes rhIn{ from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:none} }
}

/* Icon attribution (CC BY 3.0): reaper-emblem.svg + favicons are derived from
   skull & crosshair icons by Lorc & Delapouite, https://game-icons.net
   Kept in source + /humans.txt per site-owner request — no visible footer credit. */

/* ---- discreet unofficial-fan-site disclaimer (replaces the old icon credit) ---- */
.reaper-disclaimer{
  text-align:center; padding:16px 14px 26px; margin-top:2.2rem;
  font-family:'Oswald',sans-serif; font-size:.6rem; line-height:1.7;
  letter-spacing:.14em; text-transform:uppercase; color:var(--ash-dim);
  border-top:1px solid var(--border); position:relative; z-index:1;
  /* No opacity here. --ash-dim (#898376) is 5.22:1 on the page ground, but the old
     opacity:.7 composited it down to 3.09:1 — under AA, on all 53 pages that carry
     this line. The text stays discreet through its size, casing and letter-spacing,
     which cost nothing in contrast. */
}

/* ---- top nav = HUD command bar + in-page tabs tactical ---- */
.nav{ background:linear-gradient(180deg,rgba(11,11,14,.97),rgba(6,6,7,.985))!important;
  border-bottom:1px solid rgba(255,37,48,.28)!important; box-shadow:0 2px 22px rgba(0,0,0,.6)!important; backdrop-filter:blur(6px); }
.brand,.bloodcafe-logo{ letter-spacing:.08em!important; filter:drop-shadow(0 0 10px rgba(190,12,20,.4)); }
.tab{ text-transform:uppercase!important; letter-spacing:.1em!important; font-family:'Oswald',sans-serif!important; font-weight:600!important; position:relative; transition:color .15s ease; }
.tab:hover{ color:var(--blood-hi)!important; }
.tab.active{ color:var(--blood-hi)!important; }
.tab.active::after{ content:""; position:absolute; left:10%; right:10%; bottom:-2px; height:2px; background:var(--blood-hi); box-shadow:0 0 10px var(--blood-glow); }

/* ============================================================
   POLISH PASS (CSS-only)
   ============================================================ */
/* blood scrollbar (site-wide) */
html{ scrollbar-width:thin; scrollbar-color:#8a0a10 #0b0b0d; }
::-webkit-scrollbar{ width:11px; height:11px; }
::-webkit-scrollbar-track{ background:#0a0a0d; }
::-webkit-scrollbar-thumb{ background:linear-gradient(#b00710,#6a040a); border:2px solid #0a0a0d; }
::-webkit-scrollbar-thumb:hover{ background:var(--blood-hi); }
/* accessible blood focus ring */
a:focus-visible,button:focus-visible,input:focus-visible,.tab:focus-visible,.character-item:focus-visible{
  outline:2px solid var(--blood-hi)!important; outline-offset:2px!important; }
/* stat-card HUD corner tick */
.stat-card::after{ content:""; position:absolute; bottom:6px; right:6px; width:9px; height:9px;
  border-right:1px solid var(--blood-hi); border-bottom:1px solid var(--blood-hi); opacity:.45; }
/* quick-action hover lift */
.quick-action-btn{ transition:transform .15s ease, border-color .15s ease; }
.quick-action-btn:hover{ transform:translateY(-2px); }
/* character buttons = dog-tags */
.character-item{ border:1px solid var(--border)!important; border-radius:2px!important; transition:all .15s ease; position:relative; }
.character-item:hover{ border-color:var(--border-bright)!important; transform:translateY(-1px); }
.character-item.active,.character-item.selected{ border-color:var(--blood-hi)!important;
  box-shadow:0 0 18px var(--blood-glow), inset 0 0 0 1px rgba(255,37,48,.3)!important; }
.character-name{ text-transform:uppercase!important; letter-spacing:.08em!important; font-family:'Oswald',sans-serif!important; }
/* hero scope-sweep line */
.reaper-hero::before{ content:""; position:absolute; left:0; right:0; top:0; height:2px; z-index:2; pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--blood-hi),transparent); opacity:.55; animation:rhScan 6s linear infinite; }
@keyframes rhScan{ 0%{transform:translateY(0)} 100%{transform:translateY(540px)} }
@media (prefers-reduced-motion:reduce){ .reaper-hero::before{animation:none;opacity:0} }

/* ============================================================
   SQUAD LOGIN (squad-login.html) — cinematic + animated
   ============================================================ */
.login-bg{
  position:fixed!important; inset:0!important; z-index:0!important;
  background:url('/img/gen-gate.jpg') center 30%/cover no-repeat!important;
  animation:loginZoom 26s ease-in-out infinite alternate;
}
@keyframes loginZoom{ from{transform:scale(1.06)} to{transform:scale(1.2) translateY(-2%)} }
.login-bg::after{ content:""; position:absolute; inset:0; pointer-events:none;
  background:
    repeating-linear-gradient(0deg,transparent 0 2px,rgba(0,0,0,.16) 2px 3px),
    linear-gradient(180deg, rgba(6,6,7,.7), rgba(6,6,7,.86)),
    radial-gradient(120% 100% at 50% 40%, transparent 45%, rgba(6,6,7,.85) 100%);
  animation:loginFlicker 7s steps(60) infinite; }
@keyframes loginFlicker{0%,96%{opacity:1}97%{opacity:.9}98%{opacity:1}}
.login-card{
  position:relative!important; z-index:2!important; overflow:hidden;
  background:linear-gradient(180deg,rgba(13,13,16,.96),rgba(8,8,10,.985))!important;
  border:1px solid rgba(255,37,48,.4)!important; border-radius:3px!important;
  box-shadow:0 0 0 1px rgba(255,37,48,.12),0 40px 100px rgba(0,0,0,.85),0 0 60px rgba(190,12,20,.3)!important;
  animation:cardIn .7s cubic-bezier(.2,.7,.2,1) both, cardPulse 4.5s ease-in-out 1s infinite;
}
@keyframes cardIn{from{opacity:0;transform:translateY(20px) scale(.98)}to{opacity:1;transform:none}}
@keyframes cardPulse{0%,100%{box-shadow:0 0 0 1px rgba(255,37,48,.12),0 40px 100px rgba(0,0,0,.85),0 0 50px rgba(190,12,20,.25)}50%{box-shadow:0 0 0 1px rgba(255,37,48,.25),0 40px 100px rgba(0,0,0,.85),0 0 80px rgba(190,12,20,.45)}}
.login-card::before{ /* scope-sweep line down the card */
  content:""; position:absolute; left:0; right:0; top:0; height:2px; z-index:3;
  background:linear-gradient(90deg,transparent,#ff2530,transparent);
  animation:loginSweep 4.5s linear infinite; }
@keyframes loginSweep{0%{transform:translateY(0);opacity:0}8%{opacity:.7}92%{opacity:.7}100%{transform:translateY(440px);opacity:0}}
.login-logo{ filter:drop-shadow(0 0 10px rgba(255,37,48,.55)); animation:emblemPulse 3.4s ease-in-out infinite; }
@keyframes emblemPulse{0%,100%{filter:drop-shadow(0 0 8px rgba(255,37,48,.45))}50%{filter:drop-shadow(0 0 18px rgba(255,37,48,.8))}}
.login-title{ font-family:'Black Ops One',Oswald,sans-serif!important; letter-spacing:.06em!important; text-transform:uppercase!important; color:var(--bone)!important; text-shadow:0 2px 0 #000,0 0 26px rgba(190,12,20,.7)!important; }
.login-subtitle{ color:#ff2530!important; letter-spacing:.3em!important; text-transform:uppercase!important; font-size:.7rem!important; font-weight:700!important; }
.login-card input{ background:#08080a!important; border:1px solid rgba(255,255,255,.1)!important; color:var(--bone)!important; letter-spacing:.08em!important; border-radius:2px!important; }
.login-card input:focus{ border-color:#ff2530!important; box-shadow:0 0 0 1px #ff2530,0 0 18px rgba(190,12,20,.4)!important; outline:none!important; }
.submit-btn{ position:relative; overflow:hidden; background:linear-gradient(135deg,#b00710,#7a050c)!important; color:var(--bone)!important; border:1px solid rgba(255,37,48,.55)!important; border-radius:2px!important; text-transform:uppercase!important; letter-spacing:.2em!important; font-weight:700!important; box-shadow:0 0 22px rgba(190,12,20,.4)!important; animation:btnPulse 3s ease-in-out infinite; }
@keyframes btnPulse{0%,100%{box-shadow:0 0 18px rgba(190,12,20,.35)}50%{box-shadow:0 0 32px rgba(255,37,48,.6)}}
.submit-btn::after{ content:""; position:absolute; inset:0; transform:translateX(-130%); background:linear-gradient(100deg,transparent,rgba(255,255,255,.18) 50%,transparent); animation:btnShine 3.6s ease-in-out infinite; }
@keyframes btnShine{0%,70%{transform:translateX(-130%)}100%{transform:translateX(130%)}}
@media (prefers-reduced-motion:reduce){ .login-bg,.login-card,.login-card::before,.login-logo,.submit-btn,.submit-btn::after{animation:none!important} }

/* ============================================================
   MOBILE polish + animation  (+ height-adaptive hero sweep)
   ============================================================ */
/* make the hero scope-sweep adapt to any hero height (mobile included) */
.reaper-hero::before{ animation:rhScan2 6s linear infinite!important; }
@keyframes rhScan2{ 0%{top:0;opacity:0} 6%{opacity:.55} 94%{opacity:.55} 100%{top:99%;opacity:0} }

@keyframes popIn{ from{opacity:0;transform:translateY(10px) scale(.96)} to{opacity:1;transform:none} }
@keyframes fadeUp{ from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:none} }

@media (max-width:768px){
  /* hero: punchier + cleaner on portrait */
  .reaper-hero{ min-height:46vh!important; padding:1.4rem 1rem 2.2rem!important; background-position:center 36%!important; }
  .reaper-hero h1{ font-size:clamp(2.7rem,13vw,4.2rem)!important; line-height:.88!important; }
  .reaper-hero .rh-tag{ letter-spacing:.26em!important; text-indent:.26em!important; font-size:.6rem!important; }
  .reaper-hero .rh-sub{ font-size:.78rem!important; }
  .reaper-hero .rh-inner{ animation:fadeUp .7s ease both; }
  /* character buttons: cleaner grid + staggered pop-in + tap feedback */
  .grid-characters{ gap:8px!important; }
  .character-item{ animation:popIn .5s ease both; }
  .character-item:nth-child(1){animation-delay:.03s}.character-item:nth-child(2){animation-delay:.06s}
  .character-item:nth-child(3){animation-delay:.09s}.character-item:nth-child(4){animation-delay:.12s}
  .character-item:nth-child(5){animation-delay:.15s}.character-item:nth-child(6){animation-delay:.18s}
  .character-item:nth-child(7){animation-delay:.21s}.character-item:nth-child(8){animation-delay:.24s}
  .character-item:nth-child(9){animation-delay:.27s}
  .character-item:active,.quick-action-btn:active,.btn:active{ transform:scale(.96)!important; }
  .stat-card{ animation:fadeUp .55s ease both; }
  .section-title{ font-size:.95rem!important; }
}
@media (prefers-reduced-motion:reduce){ .reaper-hero .rh-inner,.character-item,.stat-card{ animation:none!important } }

/* ============================================================
   IN-PAGE POLISH — sphere cards, tooltips, spinners
   ============================================================ */
/* sphere cards = tactical slabs */
.sphere-card{ border:1px solid var(--border)!important; border-radius:2px!important;
  background:linear-gradient(180deg,rgba(18,18,22,.92),rgba(10,10,13,.96))!important;
  transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease; }
.sphere-card:hover{ border-color:var(--border-bright)!important; transform:translateY(-3px);
  box-shadow:0 14px 34px rgba(0,0,0,.6),0 0 26px var(--blood-glow)!important; }
.sp-mode-warn,.sp-low-warn{ color:var(--blood-hi)!important; }
/* tooltips = reaper terminal (override colors only, keep positioning) */
[data-tip]::after{ background:#0d0d10!important; border:1px solid rgba(255,37,48,.4)!important; color:var(--bone)!important;
  font-family:'Oswald',sans-serif!important; letter-spacing:.03em!important; border-radius:2px!important;
  box-shadow:0 8px 26px rgba(0,0,0,.75),0 0 18px var(--blood-glow)!important; }
[data-tip]::before{ border-top-color:rgba(255,37,48,.5)!important; }
/* loading spinners -> blood */
.dev-news-spinner,.sb-load,[class*="spinner"]{ border-color:rgba(255,37,48,.22)!important; border-top-color:var(--blood-hi)!important; }

/* ============================================================
   Filled controls — contrast fix, 2026-09-03.
   The reaper theme remaps --neon-purple to the bright #ff2530, so every control
   that fills itself with that token and prints white text landed at 3.78:1,
   under the 4.5:1 AA minimum. The theme's deeper --blood (#b00710) carries the
   same identity at 7.28:1, so filled controls use it and keep white text.
   ============================================================ */
.char-tab.active,
.tab.active,
.btn-primary,
button.primary,
.filter-btn.active,
.pill.active{
  background:var(--blood)!important;
  border-color:var(--blood)!important;
  color:#fff!important;
}

/* ============================================================
   Gradient-clipped headings — legibility fix, 2026-09-03.

   styles.css paints h1 / .hero-title / .brand .h1 with
   `-webkit-background-clip:text` + `-webkit-text-fill-color:transparent`.
   The reaper theme then sets its own solid `color:var(--bone)` on the hero, but
   the transparent FILL wins over `color`, so the glyphs rendered as holes and all
   you saw was the heavy black text-shadow behind them. That is why the "BLOOD CAFÉ"
   wordmark and the section titles read as near-black smudges on a near-black page.

   Filled type in bone over the art is what the theme was designed for, and it
   measures 15:1 instead of "unreadable". Reset the clip explicitly.
   ============================================================ */
.reaper-hero h1,
.reaper-hero .hero-title{
  background:none!important;
  -webkit-background-clip:border-box!important;
  background-clip:border-box!important;
  -webkit-text-fill-color:var(--bone)!important;
  color:var(--bone)!important;
  /* keep a dark halo for separation from the art, drop the opaque black slab */
  text-shadow:0 2px 0 rgba(0,0,0,.85), 0 0 26px rgba(0,0,0,.8), 0 0 60px var(--blood-glow)!important;
}

/* Section titles elsewhere: keep the gradient, but on the light blood ramp so the
   text is legible, and never let a stray dark shadow fill the glyph interior. */
h1, h2.hero-title, .brand .h1, .section-title{
  -webkit-text-fill-color:transparent;
  background-image:var(--gradient-primary);
  -webkit-background-clip:text;
  background-clip:text;
}
h2.hero-title, .section-title{ text-shadow:none!important; }

/* ===== /css/tokens.css ===== */
/* ============================================================================
   BLOOD CAFÉ — DESIGN TOKENS
   Added 2026-09-03. Loaded LAST so it is the single authority on colour, spacing,
   radius and elevation.

   WHY THIS FILE EXISTS
   The site had two competing visual languages: an original neon purple/cyan system
   in styles.css and a later black/bone/blood "reaper" theme layered on top, with
   reaper hijacking some of the old variables (--neon-cyan -> red) but not others.
   Worse, --muted, --purple, --brand and --cyan were referenced 111 times across the
   site while being defined in only a handful of page-local <style> blocks — three
   pages used var(--muted) without ever defining it, so that text silently fell back
   to an inherited colour.

   The direction is "tactical command centre", not "neon casino": a near-black ground,
   quiet elevated surfaces, hairline borders, ONE hot accent (blood red) used sparingly
   for emphasis and state, and a cool steel-blue as the single secondary. Depth comes
   from layered surfaces and hairlines, not from glow.
   ============================================================================ */

:root {
  /* ---- ground & surfaces: four deliberate elevations, not ad-hoc greys ---- */
  --bc-bg-0:#060607;                 /* page ground */
  --bc-bg-1:#0b0c0e;                 /* section band */
  --bc-surface-1:#101115;            /* card */
  --bc-surface-2:#16181d;            /* raised card / hover */
  --bc-surface-3:#1d2026;            /* input, well, table header */

  /* ---- borders: hairlines do the structural work ---- */
  --bc-border:rgba(233,230,221,.10);
  --bc-border-strong:rgba(233,230,221,.18);
  --bc-border-accent:rgba(255,60,68,.45);

  /* ---- text ---- */
  --bc-text:#e9e6dd;                 /* bone */
  --bc-text-2:#b9b3a8;
  --bc-text-3:#898376;               /* muted — WCAG AA on every surface above */
  --bc-text-inverse:#0b0c0e;

  /* ---- accents. ONE hot colour, one cool. Everything else is semantic. ---- */
  --bc-accent:#ff3c44;               /* blood — emphasis, active state, focus */
  --bc-accent-deep:#b00710;          /* filled controls (7.3:1 with white) */
  --bc-accent-dim:rgba(255,60,68,.14);
  --bc-steel:#7f93b5;                /* the single cool secondary */
  --bc-steel-dim:rgba(127,147,181,.14);

  /* ---- semantic ---- */
  --bc-ok:#3ddc84; --bc-warn:#f5a524; --bc-bad:#ff4d4f; --bc-info:#5cc8ff;

  /* ---- radius scale ---- */
  --bc-r-xs:3px; --bc-r-sm:6px; --bc-r-md:10px; --bc-r-lg:14px; --bc-r-pill:999px;

  /* ---- spacing scale (4px base) ---- */
  --bc-s-1:4px; --bc-s-2:8px; --bc-s-3:12px; --bc-s-4:16px;
  --bc-s-5:24px; --bc-s-6:32px; --bc-s-7:48px; --bc-s-8:64px;

  /* ---- elevation. Shadows are for depth, never decoration. ---- */
  --bc-e-1:0 1px 2px rgba(0,0,0,.5);
  --bc-e-2:0 4px 14px rgba(0,0,0,.45);
  --bc-e-3:0 12px 34px rgba(0,0,0,.55);
  --bc-focus:0 0 0 3px rgba(255,60,68,.35);

  /* ---- type scale ---- */
  --bc-fs-xs:11px; --bc-fs-sm:13px; --bc-fs-base:15px; --bc-fs-lg:18px;
  --bc-fs-xl:22px; --bc-fs-2xl:28px; --bc-fs-3xl:38px;
  --bc-lh-tight:1.2; --bc-lh:1.6;
  --bc-measure:72ch;                 /* readable line length for prose */

  --bc-motion:180ms cubic-bezier(.4,0,.2,1);

  /* ---- LEGACY ALIASES ------------------------------------------------------
     Every old variable now resolves through the system above, so the two visual
     languages become one without rewriting several thousand existing rules. The
     four that were never defined anywhere are defined here for the first time. */
  --muted:var(--bc-text-3);
  --purple:var(--bc-steel);
  --cyan:var(--bc-steel);
  --brand:var(--bc-accent);
  --text-primary:var(--bc-text);
  --text-secondary:var(--bc-text-2);
  --text-muted:var(--bc-text-3);
  --border:var(--bc-border);
  --border-bright:var(--bc-border-accent);
  --bg-primary:var(--bc-bg-0);
  --bg-secondary:var(--bc-bg-1);
  --bg-card:var(--bc-surface-1);
  --bg-card-hover:var(--bc-surface-2);
  --bg-input:var(--bc-surface-3);
  --success:var(--bc-ok);
  --warning:var(--bc-warn);
  --danger:var(--bc-bad);
  --info:var(--bc-info);
  --shadow-sm:var(--bc-e-1);
  --shadow-md:var(--bc-e-2);
  --shadow-lg:var(--bc-e-3);
}

/* ============================================================================
   COMPONENTS
   Deliberately low-specificity and additive. The goal is depth and hierarchy from
   surface, hairline and weight — not from glow. Every effect below earns its place
   by making structure easier to read; anything that was only there to "look gamer"
   has been left out.
   ============================================================================ */

/* ---- surfaces ------------------------------------------------------------ */
.panel, .card, .seo-callout, .ss-section, .sc-total-display, .guide-section {
  border-radius: var(--bc-r-md);
}
.card, .panel {
  background: linear-gradient(180deg, var(--bc-surface-1), var(--bc-bg-1));
  border: 1px solid var(--bc-border);
  box-shadow: var(--bc-e-1);
  transition: border-color var(--bc-motion), transform var(--bc-motion), box-shadow var(--bc-motion);
}
/* Lift only where a card is actually interactive — a static panel that moves on
   hover is noise, and it was previously applied to both. */
a.card:hover, .card[onclick]:hover, .card[role="button"]:hover, .sphere-card:hover {
  border-color: var(--bc-border-accent);
  box-shadow: var(--bc-e-2);
  transform: translateY(-2px);
}

/* ---- tables: the site is data-heavy, so these matter most ---------------- */
table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
th {
  text-align: left; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  font-size: var(--bc-fs-xs); color: var(--bc-text-2);
  background: var(--bc-surface-3);
  border-bottom: 1px solid var(--bc-border-strong);
  padding: 10px 12px; position: sticky; top: 0; z-index: 2;
}
td { padding: 10px 12px; border-bottom: 1px solid var(--bc-border); vertical-align: top; }
tbody tr { transition: background var(--bc-motion); }
tbody tr:hover { background: rgba(255,255,255,.03); }
tbody tr:last-child td { border-bottom: 0; }
/* Numbers read far faster right-aligned and tabular. */
td:has(> strong:only-child), .num, td.num, th.num { text-align: right; }

/* Wide tables scroll inside their own container, never the page. */
.table-scroll, .max-values-table-wrap, .ss-table-wrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);
  background: var(--bc-surface-1);
}
.table-scroll > table, .table-scroll table { margin: 0; }
/* A hairline hint that there is more to the right, which disappears at the end. */
.table-scroll { background-image: linear-gradient(to left, rgba(255,60,68,.10), transparent 34px); background-repeat: no-repeat; background-position: right; background-size: 34px 100%; }

/* ---- buttons ------------------------------------------------------------- */
.btn, button.primary, .btn-primary {
  border-radius: var(--bc-r-sm);
  transition: background var(--bc-motion), border-color var(--bc-motion), transform var(--bc-motion);
}
.btn:hover { border-color: var(--bc-border-accent); }
.btn:active, button:active { transform: translateY(1px); }

/* ---- badges & pills ------------------------------------------------------ */
.badge, .pill, .chip, .mk, .tag {
  border-radius: var(--bc-r-pill);
  font-size: var(--bc-fs-xs); letter-spacing: .03em;
}

/* ---- callouts: one shape, colour carries the meaning --------------------- */
.seo-note, .tip-box, .mistake-box, .ss-callout, .sc-cond-note {
  border-left: 3px solid var(--bc-accent);
  border-radius: 0 var(--bc-r-md) var(--bc-r-md) 0;
  padding: var(--bc-s-3) var(--bc-s-4);
  line-height: var(--bc-lh);
}
.tip-box   { border-left-color: var(--bc-info); }
.mistake-box { border-left-color: var(--bc-bad); }

/* ---- stat emphasis: the number is the message ---------------------------- */
.stat-value, .sc-total-value, .bs-value {
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  line-height: var(--bc-lh-tight);
}
.stat-label, .bs-label, .sc-total-label {
  font-size: var(--bc-fs-xs); letter-spacing: .12em; text-transform: uppercase;
  color: var(--bc-text-3);
}

/* ---- prose: cap the measure so long-form guides stay readable ------------ */
.seo-page > p, .guide-section > p, .ss-section > p { max-width: var(--bc-measure); }

/* ---- interaction states -------------------------------------------------- */
a { transition: color var(--bc-motion); }
:focus-visible { outline: 2px solid var(--bc-accent) !important; outline-offset: 2px; box-shadow: var(--bc-focus); }

/* ---- honour reduced motion everywhere ------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
  .card:hover, a.card:hover, .sphere-card:hover { transform:none; }
}

/* Empty states should read as a deliberate answer, not a gap in the page. */
/* No opacity fade: it composited --bc-text-3 down to 3.67:1 inside .empty-note.
   The dashed border and wording already read as an empty state. */
.sect.is-empty { }
.empty-note {
  border: 1px dashed var(--bc-border-strong);
  border-radius: var(--bc-r-md);
  padding: var(--bc-s-4);
  color: var(--bc-text-3);
  background: var(--bc-surface-1);
}

/* ============================================================================
   TARGET SIZE — WCAG 2.2 AA (2.5.8 Target Size Minimum, 24x24 CSS px)
   Applied to discrete controls only. Links inline in a sentence are explicitly
   exempted by the success criterion and are left alone, because padding them out
   would wreck the line rhythm of long-form guides for no accessibility gain.
   ============================================================================ */
button,
[role="button"],
input[type="button"], input[type="submit"], input[type="checkbox"], input[type="radio"],
select,
.btn, .chip, .pill, .tab, .char-tab, .rarity-badge, .filter-btn,
.mobile-nav-item, .mobile-menu-item, .sc-add-btn, .sc-remove-btn {
  min-height: 24px;
  min-width: 24px;
}
/* Small chips get their hit area from padding rather than a forced box, so the
   visual size is unchanged while the target grows. */
.chip, .pill, .rarity-badge, .mk { padding-block: 5px; padding-inline: 10px; }

/* Icon-only controls need a real square. */
button:not(:has(> *)):empty, .icon-btn, .close-btn, .mobile-menu-close {
  min-width: 32px; min-height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ============================================================================
   MOBILE BOTTOM BAR — usable tap targets
   Pages that hand-roll their own bottom bar were fitting 13 items across a 360px
   phone, giving each a 23px-wide hit area: under the WCAG 2.2 minimum and genuinely
   hard to hit. Give every item a real target and let the bar scroll horizontally
   rather than shrinking items to fit.
   ============================================================================ */
.mobile-nav-inner {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.mobile-nav-inner::-webkit-scrollbar { display: none; }
/* The minimum itself is set at source in styles.css so there is one owner of it;
   this only adds the scroll affordance. */
.mobile-nav-item { scroll-snap-align: start; }
/* Fade the trailing edge so it is obvious there is more to scroll to. */
.mobile-nav::after {
  content: ""; position: absolute; right: 0; top: 0; bottom: 0; width: 26px;
  background: linear-gradient(to left, var(--bc-bg-1), transparent);
  pointer-events: none;
}

/* ============================================================================
   LAYOUT STABILITY (CLS)
   site-header.js replaces an empty <div id="site-header"> placeholder at runtime.
   The placeholder had no height, so every page reflowed the moment the header
   rendered — index.html measured a Cumulative Layout Shift of 0.32, three times the
   0.1 "good" threshold, and the biggest single contributor was the nav appearing.
   Reserving the header's real height removes the jump without changing the design.
   ============================================================================ */
#site-header:empty { display: block; min-height: 61px; }
#site-mobile-nav:empty { display: block; }
@media (max-width: 768px) {
  #site-header:empty { min-height: 97px; }
}
/* The hero image is the other contributor: give it an aspect ratio so the space is
   claimed before the image decodes. */
.reaper-hero { contain-intrinsic-size: auto 420px; }
.reaper-hero > .rh-inner { min-height: 1px; }


/* ---------------------------------------------------------------------------
   Screen-reader-only text. Defined HERE because tokens.css is appended last to
   every page bundle, and .sr-only previously existed only in mobile.css /
   responsive-pro.css — which most pages do not load. Table captions and control
   labels added for the accessibility pass rely on it being present everywhere.
   --------------------------------------------------------------------------- */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border-width:0;
}
/* A skip link is only useful if it appears when it receives focus. */
.sr-only-focusable:focus, .sr-only-focusable:focus-visible{
  position:static; width:auto; height:auto; margin:0; overflow:visible;
  clip:auto; white-space:normal;
}

/* ---------------------------------------------------------------------------
   Sortable / filterable data tables (js/table-tools.js).
   Styled here so the controls inherit the same tokens on every page that opts
   in, rather than each page inventing its own header button.
   --------------------------------------------------------------------------- */
.bc-sort-btn{
  -webkit-appearance:none; appearance:none; background:none; border:0; padding:0;
  font:inherit; color:inherit; letter-spacing:inherit; text-transform:inherit;
  display:inline-flex; align-items:center; gap:6px; width:100%;
  text-align:left; cursor:pointer;
}
.bc-sort-btn:hover .bc-sort-label{ text-decoration:underline; }
/* The arrow is a state readout, so it must not be the ONLY signal — aria-sort on
   the th carries the same information for anyone not looking at the glyph. */
.bc-sort-arrow{ opacity:.45; font-size:.85em; line-height:1; }
.bc-sort-arrow::after{ content:"↕"; }
[aria-sort="ascending"] .bc-sort-arrow{ opacity:1; }
[aria-sort="ascending"] .bc-sort-arrow::after{ content:"↑"; }
[aria-sort="descending"] .bc-sort-arrow{ opacity:1; }
[aria-sort="descending"] .bc-sort-arrow::after{ content:"↓"; }
th[aria-sort="ascending"], th[aria-sort="descending"]{ color:var(--bc-accent, #a78bfa); }

.bc-table-filter{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:14px 0 8px;
}
.bc-table-filter-label{
  font-size:11px; letter-spacing:.12em; text-transform:uppercase;
  color:var(--bc-text-3, #898376); font-weight:700;
}
.bc-table-filter-input{
  flex:1 1 200px; min-width:0; max-width:340px;
  padding:8px 12px; font:inherit; font-size:13px;
  color:var(--bc-text-1, #e9e6dd);
  background:var(--bc-surface-3, rgba(255,255,255,.04));
  border:1px solid var(--bc-border, rgba(233,230,221,.16));
  border-radius:var(--bc-r-sm, 8px);
}
.bc-table-filter-count{
  font-size:11px; letter-spacing:.08em; color:var(--bc-text-3, #898376);
  font-variant-numeric:tabular-nums;
}
@media (max-width:480px){
  .bc-table-filter-input{ max-width:none; }
}

/* A link inside running text must not be identified by colour alone (WCAG 1.4.1).
   The footer "Member Login" link measured 1.11:1 against the sentence around it —
   invisible to anyone who does not perceive that hue difference. Class-bearing links
   (buttons, tiles, nav) opt out, since they carry their own affordance. */
p > a:not([class]), li > a:not([class]), td > a:not([class]) { text-decoration: underline; text-underline-offset: 2px; }

/* ---------------------------------------------------------------------------
   Sphere input + its "copy to all matching slots" button (public/sphere-system.js).
   Neither class had a single CSS rule anywhere, so the button rendered as a raw
   user-agent control — a light-grey box below each field on a black page. It sits
   inside the field's right edge, where it belongs.
   --------------------------------------------------------------------------- */
.sphere-input-wrapper{ position:relative; display:block; }
.sphere-input-wrapper > .sphere-input-enhanced{ padding-right:40px; margin-bottom:0; display:block; }
.sphere-copy-btn{
  position:absolute; top:50%; right:6px; transform:translateY(-50%);
  -webkit-appearance:none; appearance:none;
  width:28px; height:28px; padding:0; line-height:1;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:14px; cursor:pointer;
  background:var(--bc-surface-2, rgba(255,255,255,.06));
  border:1px solid var(--bc-border, rgba(233,230,221,.16));
  border-radius:var(--bc-r-sm, 6px);
  color:var(--bc-text-2, #cfcabc);
  opacity:.65; transition:opacity .15s ease, border-color .15s ease, background .15s ease;
}
.sphere-copy-btn:hover, .sphere-copy-btn:focus-visible{
  opacity:1; background:var(--bc-surface-3, rgba(255,255,255,.10));
  border-color:var(--bc-border-accent, rgba(255,37,48,.45));
}
