/* ============================================================
   EllesmereUI — site design system
   Dark teal-black surfaces, mint (#0cd29d) accent, glass panels.
   Modeled on the in-game EllesmereUI options panel.
   ============================================================ */

/* ---- Fonts: Sora and Inter, served from this site ----
   The exact files and rules Google Fonts serves for
   css2?family=Sora:wght@600;700;800&family=Inter:wght@400;500;600;700&display=optional
   (Inter v20, Sora v17, fetched 2026-10-22); only each src points here, so no
   request leaves the site for a font. Licences: media/fonts/OFL-Inter.txt and
   OFL-Sora.txt. The pages preload the two latin files. A new font version gets
   new file names (they are cached for a year). tools/contract/check-assets.mjs
   holds these blocks to the saved Google CSS. */
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek-ext.woff2") format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek.woff2") format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek-ext.woff2") format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek.woff2") format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek-ext.woff2") format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek.woff2") format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek-ext.woff2") format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-greek.woff2") format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/inter-v20-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;
}
/* latin-ext */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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;
}
/* latin-ext */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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;
}
/* latin-ext */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 800;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 800;
  font-display: optional;
  src: url("../media/fonts/sora-v17-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 {
  /* Neutral night surfaces. These carry the vast majority of the page -
     keep them genuinely neutral so the accent can actually stand out. */
  --bg-0: #111111;
  --bg-1: #161617;
  --bg-2: #1a1a1c;

  --panel: #19191b;
  --panel-2: #1e1e21;
  --panel-3: #242427;

  /* Chrome borders are neutral. Only --line-accent is green, and it is
     reserved for things that are deliberately highlighted. */
  --line-soft: rgba(255, 255, 255, 0.055);   /* resting card edge */
  --line: rgba(255, 255, 255, 0.11);         /* hover / emphasis    */
  --line-strong: rgba(255, 255, 255, 0.20);  /* strongest neutral   */
  --line-accent: rgba(12, 210, 157, 0.40);   /* deliberate accent   */

  --accent: #0cd29d;
  --accent-bright: #35eebb;
  --accent-deep: #08a67b;
  --accent-glow: rgba(12, 210, 157, 0.35);
  --accent-soft: rgba(12, 210, 157, 0.10);

  --ink: #f3f4f5;
  --ink-dim: #c7cacd;
  --muted: #8c9197;
  --muted-2: #5d6167;

  --danger: #ff5c6c;
  --warn: #ffcf5c;

  --font-display: "Sora", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", "JetBrains Mono", Consolas, monospace;

  --radius: 12px;
  --radius-lg: 18px;
  --radius-sm: 8px;

  --shadow-1: 0 6px 24px rgba(0, 0, 0, 0.45);
  --shadow-glow: 0 0 0 1px var(--line), 0 10px 40px rgba(0, 0, 0, 0.5);

  --nav-h: 72px;
  --container: 1180px;
}

/* A night theme, so the browser draws its own control parts to match: a
   search field's clear button, textarea grips, a native list. Embedded
   frames keep the normal scheme, as before - a frame whose page differs
   gets an opaque canvas, so a video that has not painted yet would show
   white. */
:root { color-scheme: dark; }
iframe { color-scheme: normal; }

/* ---------- Reset / base ---------- */

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg-0);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  /* Sticky footer. Most pages build their content in JS after load, so
     for a few frames <main> is nearly empty and the footer would sit in
     the middle of the screen before being shoved down. Stretch the page
     to the viewport and let <main> absorb the slack: the footer starts
     at the bottom edge and content growing above it never moves it up. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}

img { max-width: 100%; display: block; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-bright); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: 1.15;
  margin: 0 0 0.5em;
  letter-spacing: -0.015em;
}

h1 { font-size: clamp(2.3rem, 5vw, 3.6rem); font-weight: 800; }
h2 { font-size: clamp(1.7rem, 3.4vw, 2.4rem); font-weight: 700; }
h3 { font-size: 1.25rem; font-weight: 700; }
p  { margin: 0 0 1em; }

::selection { background: rgba(12, 210, 157, 0.30); color: #fff; }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg-0); }
::-webkit-scrollbar-thumb {
  background: #2f2f32; border-radius: 8px; border: 2px solid var(--bg-0);
}
::-webkit-scrollbar-thumb:hover { background: #404046; }

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}

.section { padding: 88px 0; position: relative; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display);
  font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
}
.eyebrow::before {
  content: ""; width: 22px; height: 2px;
  background: linear-gradient(90deg, var(--accent), transparent);
  border-radius: 2px;
}

.section-head { max-width: 720px; margin-bottom: 44px; }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head.center .eyebrow { justify-content: center; }
.section-head p { color: var(--muted); font-size: 1.06rem; }

.text-muted { color: var(--muted); }
.text-accent { color: var(--accent); }
.nowrap { white-space: nowrap; }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font-family: var(--font-display);
  font-weight: 700; font-size: 0.95rem;
  padding: 13px 31px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
  white-space: nowrap;
  user-select: none;
  line-height: 1.2;
}
.btn:active { transform: translateY(1px); }
.btn svg { width: 18px; height: 18px; flex: none; }

.btn-primary {
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  color: #04120d;
  box-shadow: 0 4px 20px rgba(12, 210, 157, 0.28);
}
.btn-primary:hover {
  background: linear-gradient(180deg, #4bf7c8, var(--accent-bright));
  color: #04120d;
  box-shadow: 0 6px 28px rgba(12, 210, 157, 0.21);
  transform: translateY(-1px);
}

.btn-ghost {
  background: rgba(12, 210, 157, 0.06);
  border-color: var(--line-accent);
  color: var(--ink);
}
.btn-ghost:hover {
  background: rgba(12, 210, 157, 0.13);
  border-color: var(--accent);
  color: #fff;
}

.btn-dark {
  background: var(--panel-2);
  border-color: var(--line-soft);
  color: var(--ink-dim);
}
.btn-dark:hover { border-color: var(--line-strong); color: #fff; }

.btn-danger {
  background: rgba(255, 92, 108, 0.10);
  border-color: rgba(255, 92, 108, 0.45);
  color: #ff8b97;
}
.btn-danger:hover { background: rgba(255, 92, 108, 0.2); color: #ffb3ba; }

.btn-sm { padding: 9px 16px; font-size: 0.85rem; }
.btn-lg { padding: 16px 34px; font-size: 1.05rem; }
.btn[disabled], .btn.disabled { opacity: 0.45; pointer-events: none; }

.btn-discord {
  background: #5865f2; color: #fff;
}
.btn-discord:hover { background: #6b76ff; color: #fff; transform: translateY(-1px); }

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

/* The header is ALWAYS visible. It never hides, slides away, or waits for
   a scroll direction - it only changes how solid it looks. */
.site-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  height: var(--nav-h);
  background: rgba(17, 17, 17, 0.72);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid var(--line-soft);
  transition: background 0.3s ease, border-color 0.3s ease;
}
.site-nav.scrolled { background: rgba(17, 17, 17, 0.9); border-bottom-color: var(--line); }

.nav-inner {
  height: var(--nav-h);
  /* Matches the gap inside .nav-links / .nav-actions so the whole row
     reads as one evenly spaced set of links. */
  display: flex; align-items: center; gap: 30px;
}

.nav-logo {
  display: flex; align-items: center; gap: 11px;
  font-family: var(--font-display); font-weight: 700; font-size: 1.16rem;
  color: var(--ink); letter-spacing: -0.01em;
}
.nav-logo img { width: 34px; height: 34px; filter: drop-shadow(0 0 8px rgba(12, 210, 157, 0.28)); }
.nav-logo b { color: var(--accent); font-weight: 800; }
.nav-logo:hover { color: #fff; }

/* Logo owns the left edge; everything else rides the right. */
.nav-logo { margin-right: auto; }

.nav-links { display: flex; align-items: center; gap: 30px; }

/* Every item in the bar is the same plain text link. */
.nav-links a,
.nav-link-btn {
  position: relative;
  font-family: var(--font-body);
  font-weight: 500; font-size: 0.88rem; letter-spacing: 0.01em;
  color: var(--muted);
  padding: 5px 0;
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; cursor: pointer;
  transition: color 0.2s ease;
}

/* Colour only. Nothing moves, nothing resizes - text that shifts under
   the cursor is the thing that reads as cheap. */
.nav-links a:hover,
.nav-link-btn:hover { color: var(--ink); }

/* Current page: simply lit in the accent. */
.nav-links a.active { color: var(--accent); }

.nav-links a svg { width: 11px; height: 11px; opacity: 0.45; transition: opacity 0.22s ease; }
.nav-links a:hover svg { opacity: 0.75; }

.nav-actions { display: flex; align-items: center; gap: 30px; }

/* Signed out, #authArea renders nothing. Collapse it (and the whole
   actions group on desktop, where the burger is hidden) so the row does
   not carry a phantom gap after the last link. */
#authArea:empty { display: none; }
@media (min-width: 761px) {
  .nav-actions:has(#authArea:empty) { display: none; }
  /* Desktop signs in from the header button; the in-menu entry is
     for the phone burger only. */
  .nav-links .nav-signin-item { display: none; }
}

.nav-burger {
  display: none;
  background: none; border: 1px solid var(--line-soft); border-radius: 8px;
  width: 42px; height: 42px; cursor: pointer; color: var(--ink);
  align-items: center; justify-content: center;
}
.nav-burger svg { width: 20px; height: 20px; }

/* Keyboard focus: an accent ring, never on a mouse click. */
.nav-links a:focus-visible,
.nav-link-btn:focus-visible,
.nav-burger:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px;
}
.nav-burger:focus-visible { outline-offset: 2px; border-radius: 8px; }

/* ---------- Nav dropdown (Resources) ----------
   A button in the bar opens a small panel of links, drawn like the
   account menu with the bell's rows inside. With a mouse it also opens
   on hover; the panel's ::before bridges the gap under the button so
   the pointer can cross it. In the phone menu the same markup unfolds
   in place under its button (the 760px block below). */
.nav-drop { position: relative; display: flex; align-items: center; }
/* A button does not inherit the page's line height; without this its
   label sat a pixel off the links beside it. */
.nav-drop-btn { line-height: inherit; }

.nav-drop-chev {
  width: 11px; height: 11px; flex: none;
  opacity: 0.45;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-drop-btn:hover .nav-drop-chev,
.nav-drop-btn[aria-expanded="true"] .nav-drop-chev { opacity: 0.8; }
.nav-drop-btn[aria-expanded="true"] { color: var(--ink); }
.nav-drop-btn[aria-expanded="true"] .nav-drop-chev { transform: rotate(180deg); }
/* The section you are in lights up like any current link. */
.nav-drop-btn.active { color: var(--accent); }

.nav-drop-menu {
  position: absolute; top: calc(100% + 14px); left: 50%;
  z-index: 95;
  min-width: 190px; width: max-content;
  padding: 6px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  opacity: 0; visibility: hidden;
  transform: translate(-50%, 4px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
}
.nav-drop-menu.open {
  opacity: 1; visibility: visible; transform: translate(-50%, 0);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s;
}
.nav-drop-menu::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 16px;
}
.nav-drop-list { display: flex; flex-direction: column; gap: 2px; }

/* Plain rows, sized like the account menu's. */
.nav-links .nav-drop-menu a {
  display: block;
  padding: 10px 12px; border-radius: 8px;
  font-size: 0.92rem; font-weight: 500; letter-spacing: 0;
  color: var(--ink-dim);
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.nav-links .nav-drop-menu a:hover,
.nav-links .nav-drop-menu a:focus-visible { background: rgba(255, 255, 255, 0.05); color: var(--ink); }
.nav-links .nav-drop-menu a:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--line-accent); }
/* The page you are on, like the current link in the bar. */
.nav-links .nav-drop-menu a.active { color: var(--accent); }
/* Reduced motion: see the end of the Responsive section, after the phone
   rules it has to override. */

/* auth area */
.auth-user { position: relative; }
.auth-avatar-btn {
  display: flex; align-items: center; gap: 9px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  padding: 5px 12px 5px 5px; border-radius: 999px; cursor: pointer;
  color: var(--ink); font-family: var(--font-body); font-size: 0.88rem; font-weight: 600;
  transition: border-color 0.15s;
}
.auth-avatar-btn:hover { border-color: var(--line-strong); }
.auth-avatar-btn img { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line); }
.auth-avatar-btn .chev { width: 12px; height: 12px; opacity: 0.55; }

.auth-menu {
  position: absolute; right: 0; top: calc(100% + 10px);
  min-width: 190px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 6px;
  display: none;
  z-index: 95;
}
.auth-menu.open { display: block; }
.auth-menu a, .auth-menu button {
  display: flex; width: 100%; align-items: center; gap: 10px;
  background: none; border: none; text-align: left; cursor: pointer;
  color: var(--ink-dim); font-family: var(--font-body); font-size: 0.92rem; font-weight: 500;
  padding: 10px 12px; border-radius: 8px;
}
.auth-menu a:hover, .auth-menu button:hover { background: var(--accent-soft); color: #fff; }
.auth-menu .menu-sep { height: 1px; background: var(--line-soft); margin: 5px 8px; }
.auth-menu svg { width: 15px; height: 15px; opacity: 0.7; }
/* Moderator-only entry point - tinted so it reads as privileged. */
.auth-menu .menu-staff { color: var(--accent); font-weight: 600; }
.auth-menu .menu-staff svg { opacity: 1; }

/* ---------- Page shells ---------- */

/* flex: 1 pairs with the sticky-footer column on body - main soaks up
   any viewport height the page's content does not fill yet. */
main { padding-top: var(--nav-h); flex: 1 0 auto; }

/* Inner-page hero. Same vocabulary as the home hero: no glow, a fine
   grid that fades out, left aligned, two-tone heading. */
.page-hero {
  padding: 64px 0 38px;
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg-0);
}
.page-hero .container { position: relative; z-index: 1; }
.page-hero h1 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.7vw, 2rem);
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -0.022em;
  color: var(--muted);
  margin: 0 0 9px;
  max-width: 740px;
}
/* Same weight as the rest of the line - colour alone carries the
   emphasis, which is what keeps the two-tone heading from shouting. */
.page-hero h1 b { color: var(--ink); font-weight: 600; }
.page-hero p { color: var(--muted); font-size: 0.95rem; line-height: 1.6; max-width: 560px; margin-bottom: 0; }

/* Hero call-to-action buttons: no accent glow, and 10% smaller than the
   site-wide button. The hero already has the page's attention, so the
   button does not need to shout to be found. */
.page-hero .btn,
.hero-lite .btn {
  padding: 12px 28px;
  font-size: 0.86rem;
  box-shadow: none;
}
.page-hero .btn:hover,
.hero-lite .btn:hover { box-shadow: none; }
.page-hero .btn svg,
.hero-lite .btn svg { width: 16px; height: 16px; }

/* The fine grid, reusable on any section that wants texture. */
.fine-grid {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.026) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.026) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 100%);
}

/* ---------- Shared card surface ----------
   Single-element version of the .gcard look: the inset top highlight is
   what reads as a light source above the card, without needing the
   two-layer gradient-border markup. */
.preset-card,
.video-card,
.form-card,
.side-card {
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.preset-card:hover,
.video-card:hover {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09), 0 16px 40px rgba(0, 0, 0, 0.25);
}

/* ============================================================
   HOME HERO - compact and type-led. Deliberately NOT full screen:
   the showcase below it should be partly visible on load so the
   product is on screen immediately.
   ============================================================ */

.hero-lite {
  position: relative;
  margin-top: calc(var(--nav-h) * -1);
  padding: calc(var(--nav-h) + 154px) 0 84px;
  /* No overflow clip: the grid below deliberately spills past the hero
     so its fade can run over a taller area than the section itself. */
  background: var(--bg-0);
}

/* Headline and buttons on the left, the explanation in its own column on
   the right. Reads as a statement with a footnote rather than a stack of
   centred marketing copy. */
.hero-stage {
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  gap: 20px 56px;
  align-items: start;
  text-align: left;
  max-width: var(--container);
}

/* ---------- Confirmation panel (after a form succeeds) ----------
   Pairs with EUI.swapPanels(): the form collapses, this expands in its
   place. The mark and text arrive slightly after the panel finishes
   opening so the sequence reads as one motion, not three. */

.done-card { text-align: center; max-width: 560px; }

.done-mark {
  width: 62px; height: 62px; margin: 4px auto 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--line-accent);
  color: var(--accent);
  animation: done-pop 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.28s both;
}
.done-mark svg { width: 27px; height: 27px; }

.done-card h2 {
  font-size: 1.5rem; margin-bottom: 10px;
  animation: done-rise 0.5s ease 0.38s both;
}
.done-card p {
  font-size: 0.97rem; line-height: 1.65; margin: 0 auto 26px; max-width: 420px;
  animation: done-rise 0.5s ease 0.46s both;
}
.done-actions {
  display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
  animation: done-rise 0.5s ease 0.54s both;
}

@keyframes done-pop {
  from { opacity: 0; transform: scale(0.82); }
  to   { opacity: 1; transform: none; }
}
@keyframes done-rise {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 480px) {
  .done-actions .btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .done-mark, .done-card h2, .done-card p, .done-actions { animation: none; }
}

/* ============================================================
   HOME PAGE SYSTEM
   Three patterns carry the whole page:
     1. .h-duo    - two-tone heading, bright lead clause + muted rest
     2. .gcard    - gradient hairline border, lit from the top edge
     3. .sec      - one rhythm, separated by hairlines not by alternating
                    background colours (which read as stripes)
   ============================================================ */

.sec {
  padding: 92px 0;
  border-bottom: 1px solid var(--line-soft);
  position: relative;
}
.sec:last-of-type { border-bottom: none; }
.sec-tight { padding: 64px 0; }

/* Two-tone heading. Put the important clause in <b>. */
.h-duo {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.1vw, 2.45rem);
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -0.022em;
  color: var(--muted);
  margin: 0 0 14px;
  max-width: 780px;
}
/* Same weight as the rest of the line - colour alone carries the
   emphasis, matching .page-hero h1 b. */
.h-duo b { color: var(--ink); font-weight: 600; }
/* Phones: the 1.6rem floor wrapped every clause onto two lines, so a
   two-clause heading became four. Scale with the viewport instead so
   each clause holds its own line on a typical phone. */
@media (max-width: 640px) {
  .h-duo { font-size: clamp(1.15rem, 5.4vw, 1.6rem); line-height: 1.32; }
  /* The muted second clause is the longer one and was still wrapping.
     Where it is wrapped in a <span> it steps down a notch, so the
     bright lead keeps its size and the subtitle fits its own line. */
  .h-duo span { font-size: 0.82em; }
}
.sec-head { margin-bottom: 40px; }
.sec-head.center { text-align: center; margin-left: auto; margin-right: auto; }
.sec-head.center .h-duo { margin-left: auto; margin-right: auto; }
.sec-head p {
  color: var(--muted); font-size: 1rem; line-height: 1.65;
  max-width: 620px; margin: 0;
}
.sec-head.center p { margin-left: auto; margin-right: auto; }

/* Gradient hairline card. The 1px outer gradient reads as a light
   source above the card, which is most of the "expensive" look. */
.gcard {
  position: relative;
  border-radius: 14px;
  padding: 1px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.11), rgba(255, 255, 255, 0.028));
  transition: background 0.28s ease, transform 0.28s ease, box-shadow 0.3s ease;
  display: block;
  color: var(--ink);
}
.gcard:hover {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.05));
  transform: translateY(-3px);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);
  color: var(--ink);
}
.gcard-in {
  border-radius: 13px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  height: 100%;
  padding: 26px 26px 28px;
  display: flex; flex-direction: column;
  overflow: hidden;
}

/* Bento grid: 12 columns, cards claim different widths so the page
   does not read as a uniform tile wall. */
.bento { display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px; }
.bento > * { grid-column: span 12; }
@media (min-width: 680px) {
  .bento > *      { grid-column: span 6; }
  .bento .b-wide  { grid-column: span 12; }
}
@media (min-width: 1040px) {
  .bento .b-4    { grid-column: span 4; }
  .bento .b-6    { grid-column: span 6; }
  .bento .b-8    { grid-column: span 8; }
  .bento .b-wide { grid-column: span 12; }
}

/* ---- module rows: one long card, three stacked beside it ----

   The three keep the old small-card width (the 2:1 split is the same
   8/4 ratio the grid used), and stack rather than sitting in their own
   columns. Nested flex rather than grid row-spanning: the stack has to
   match the long card's height exactly, and letting flex divide it is
   more reliable than guessing a row count. */

.bento-row { grid-column: span 12; }

@media (min-width: 900px) {
  .bento-row {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 14px;
    align-items: stretch;
  }
  /* Long card on the right instead. Column order, not direction, so the
     DOM order still reads long-then-stack for a screen reader. */
  .bento-row.flip { grid-template-columns: 1fr 2fr; }
  .bento-row.flip > .bento-stack { order: 1; }
  .bento-row.flip > .gcard        { order: 2; }

  .bento-stack { display: flex; flex-direction: column; gap: 14px; }
  /* Equal thirds of whatever the long card comes out at. */
  .bento-stack > .gcard { flex: 1 1 0; min-height: 0; }
}

@media (max-width: 899px) {
  .bento-row   { display: flex; flex-direction: column; gap: 14px; }
  .bento-stack { display: flex; flex-direction: column; gap: 14px; }
  /* Once the bento is a single column, every card costs a full screen
     of scrolling - the secondary module cards step aside on phones. */
  .gcard.mobile-hide { display: none; }
}

.gcard h3 {
  font-size: 1.06rem; margin: 0 0 8px;
  display: flex; align-items: center; gap: 10px;
}
.gcard p { color: var(--muted); font-size: 0.9rem; line-height: 1.6; margin: 0; }
.gcard .g-icon {
  width: 34px; height: 34px; flex: none;
  border-radius: 9px;
  display: grid; place-items: center;
  background: var(--accent-soft);
  border: 1px solid var(--line-accent);
  color: var(--accent);
}
.gcard .g-icon svg { width: 17px; height: 17px; }

/* A card that leads with a picture. */
.gcard-media {
  margin: -26px -26px 20px;
  aspect-ratio: 16 / 8;
  overflow: hidden;
  background: var(--bg-2);
  border-bottom: 1px solid var(--line-soft);
}
.gcard-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
.gcard:hover .gcard-media img { transform: scale(1.04); }

/* Small stat row used under headings. */
.fact-row {
  display: flex; flex-wrap: wrap; gap: 34px;
  margin-top: 34px;
}
.fact b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.7rem; font-weight: 800; color: var(--ink); line-height: 1;
  letter-spacing: -0.02em;
}
.fact span { font-size: 0.82rem; color: var(--muted-2); }

/* ---------- Showcase: tabbed autoplay demos ---------- */

.showcase-section { padding: 0 0 92px; border-bottom: 1px solid var(--line-soft); }

.show-tabs {
  display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 9px;
  margin-bottom: 26px;
}
.show-tab {
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  color: var(--muted);
  border-radius: 999px;
  padding: 9px 30px;
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
  cursor: pointer; white-space: nowrap;
  transition: color 0.22s ease, border-color 0.22s ease, background 0.22s ease, transform 0.22s ease;
}
.show-tab:hover { color: var(--ink-dim); border-color: var(--line); transform: translateY(-1px); }
/* Active tab is neutral, not accent: a lifted white fill with a white
   outline. Keeps the green reserved for actions rather than state. */
.show-tab.on {
  background: rgba(255, 255, 255, 0.10);
  border-color: #fff;
  color: #fff;
}

/* The media is the point. No side column competing with it - just a
   large panel sat on a gradient hairline, exactly like the reference. */
.show-stage {
  border-radius: 16px;
  padding: 1px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.03));
}

.show-media {
  position: relative;
  border-radius: 15px;
  overflow: hidden;
  background: var(--bg-2);
}
.show-slot { aspect-ratio: 16 / 9; }
.show-slot video,
.show-slot img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  animation: show-in 0.5s ease both;
}
/* A tab whose clip does not exist yet shows its still instead. */

@keyframes show-in {
  from { opacity: 0; transform: scale(1.012); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 900px) {
  /* Scaled to match the desktop bump rather than adding a flat 50px -
     vertical space is scarcer on a phone. */
  .hero-lite { padding: calc(var(--nav-h) + 94px) 0 52px; }
  /* Stack: headline, explanation, then buttons - so the copy still reads
     before the call to action on a phone. */
  .hero-stage { grid-template-columns: 1fr; gap: 18px; }
  .hero-line { padding-top: 0; max-width: 620px; order: 2; }
  .hero-copy { display: contents; }
  .hero-head { order: 1; }
  .hero-act { order: 3; margin-top: 8px; }
  .sec { padding: 64px 0; }
  .fact-row { gap: 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .show-slot video, .show-slot img, .show-copy h3, .show-copy p { animation: none; }
}

/* The only texture in the hero now that the glow is gone: a faint grid,
   evenly present rather than pooled, so it reads as paper rather than as
   a light source. */
/* Runs 300px past the bottom of the hero and fades from 40% down, so the
   dissolve happens over a much taller area than the section itself and
   never reads as a hard edge where the hero ends. */
.hero-grid {
  position: absolute; inset: 0 0 -300px 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.0195) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.0195) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%);
}

.hero-copy { min-width: 0; }

/* Sized so "Your entire interface." holds one line in the left column.
   Going much above 2.85rem wraps it at the container's max width. */
.hero-head {
  font-size: clamp(1.9rem, 3.9vw, 2.85rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.03em;
  margin: 0;
}
.hero-head .grad {
  background: linear-gradient(100deg, var(--accent-bright) 0%, var(--accent) 52%, #0a9e77 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* Nudged down so its first line sits optically with the headline rather
   than flush to its cap height. */
.hero-line {
  margin: 0;
  padding-top: 12px;
  max-width: 460px;
  font-size: clamp(0.96rem, 1.35vw, 1.05rem);
  color: var(--muted);
  line-height: 1.7;
}

.hero-act {
  margin-top: 32px;
  display: flex; align-items: center;
  gap: 12px; flex-wrap: wrap;
}

/* Staggered entrance - each element carries its own --d delay. */
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

.hero-lite .rise {
  animation: hero-rise 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: var(--d, 0ms);
}

@media (max-width: 640px) {
  .hero-act { gap: 10px; }
  .hero-act .btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-lite .rise { animation: none; }
}

/* ---------- Background flourishes ---------- */

.bg-grid {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.028) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, black 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, black 30%, transparent 78%);
}


/* .hero-ctas survives the old hero: the download CTA band still uses it. */
.hero-ctas { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 34px; }

/* ---------- Feature grid ---------- */






/* ---------- Split feature section ---------- */

.split {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 60px; align-items: center;
}
.split.reverse .split-media { order: 2; }

.split-media {
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  overflow: hidden;
  box-shadow: var(--shadow-glow);
}
.split-media img { width: 100%; }

.split-copy .eyebrow { margin-bottom: 10px; }
.split-copy h2 { margin-bottom: 16px; }
.split-copy > p { color: var(--muted); font-size: 1.03rem; }

.check-list { list-style: none; padding: 0; margin: 22px 0 0; display: grid; gap: 13px; }
.check-list li { display: flex; gap: 13px; align-items: flex-start; color: var(--ink-dim); font-size: 0.98rem; }
.check-list li b { color: var(--ink); }
.check-list .chk {
  flex: none; width: 22px; height: 22px; margin-top: 2px;
  border-radius: 6px; background: var(--accent-soft); border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center; color: var(--accent);
}
.check-list .chk svg { width: 12px; height: 12px; }

/* ---------- Gallery ---------- */

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.gallery-item {
  position: relative; cursor: zoom-in;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--panel);
  transition: transform 0.2s ease, border-color 0.2s ease;
  aspect-ratio: 16 / 9;
}
.gallery-item img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.35s ease; }
.gallery-item:hover { transform: translateY(-3px); border-color: var(--line-strong); }
.gallery-item:hover img { transform: scale(1.04); }
.gallery-item .zoom {
  position: absolute; right: 12px; bottom: 12px;
  width: 34px; height: 34px; border-radius: 8px;
  background: rgba(17, 17, 17, 0.72); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent); opacity: 0; transition: opacity 0.2s ease;
}
.gallery-item:hover .zoom { opacity: 1; }
.gallery-item .zoom svg { width: 16px; height: 16px; }

/* Lightbox */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(9, 9, 9, 0.92);
  backdrop-filter: blur(6px);
  display: none;
  align-items: center; justify-content: center;
  padding: 4vh 4vw;
}
.lightbox.open { display: flex; }
.lightbox img {
  max-width: 100%; max-height: 90vh;
  border-radius: 10px; border: 1px solid var(--line);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.8);
}
.lightbox-btn {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px; border-radius: 50%;
  background: rgba(30, 30, 33, 0.85); border: 1px solid var(--line);
  color: var(--ink); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s, border-color 0.15s;
}
.lightbox-btn:hover { background: var(--panel-3); border-color: var(--accent); }
.lightbox-btn svg { width: 20px; height: 20px; }
.lightbox-prev { left: 22px; }
.lightbox-next { right: 22px; }
.lightbox-close { top: 22px; right: 22px; transform: none; }

/* ---------- Video cards ---------- */

.videos-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.videos-grid.guides { grid-template-columns: repeat(3, 1fr); }

.video-card {
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.2s ease, border-color 0.2s ease;
}
.video-card:hover { transform: translateY(-3px); border-color: var(--line); }

.video-thumb {
  position: relative; cursor: pointer; aspect-ratio: 16 / 9;
  background: #000; overflow: hidden;
}
.video-thumb img { width: 100%; height: 100%; object-fit: cover; opacity: 0.85; transition: opacity 0.2s, transform 0.3s; }
.video-thumb:hover img { opacity: 1; transform: scale(1.03); }
.video-thumb iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 62px; height: 62px; border-radius: 50%;
  background: rgba(12, 210, 157, 0.92);
  display: flex; align-items: center; justify-content: center;
  color: #04120d;
  box-shadow: 0 0 0 8px rgba(12, 210, 157, 0.1), 0 8px 30px rgba(0, 0, 0, 0.25);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
  pointer-events: none;
}
.video-thumb:hover .video-play { transform: translate(-50%, -50%) scale(1.08); box-shadow: 0 0 0 12px rgba(12, 210, 157, 0.08), 0 8px 30px rgba(0, 0, 0, 0.25); }
.video-play svg { width: 24px; height: 24px; margin-left: 3px; }

.video-meta { padding: 16px 18px 18px; }
.video-tag {
  display: inline-block; font-family: var(--font-display);
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 6px;
}
.video-meta h3 { font-size: 1.02rem; margin: 0; }

/* ---------- Preset cards ---------- */

.preset-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.preset-card {
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.25s ease;
  color: var(--ink);
}
.preset-card:hover {
  transform: translateY(-4px);
  border-color: var(--line);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);
  color: var(--ink);
}

.preset-cover {
  aspect-ratio: 16 / 9; position: relative; overflow: hidden;
  background: linear-gradient(140deg, var(--panel-3), var(--panel));
}
.preset-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.35s ease; }
.preset-card:hover .preset-cover img { transform: scale(1.05); }
.preset-cover .cover-fallback {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
}
.preset-cover .cover-fallback img {
  width: 74px; height: 74px; object-fit: contain;
  opacity: 0.3; filter: drop-shadow(0 0 20px rgba(12, 210, 157, 0.2));
}

.preset-body { padding: 16px 18px 14px; flex: 1; display: flex; flex-direction: column; gap: 10px; }
.preset-body h3 {
  font-size: 1.03rem; margin: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.preset-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* On gallery cards the chip row is ONE line: chips still wrap in
   layout, but the row's height is pinned to a single chip and clips
   the rest - so exactly as many tags show as fit, and the fourth that
   would wrap just is not seen. The preset page keeps every tag. */
.preset-card .preset-chips {
  max-height: 26px;      /* exactly one chip row - see .chip line-height */
  overflow: hidden;
  align-content: flex-start;
}
.chip {
  line-height: 18px;     /* pinned so the one-row clip above is exact:
                            18 + 3px*2 padding + 1px*2 border = 26px */
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.74rem; font-weight: 600;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line-soft);
  color: var(--muted);
  white-space: nowrap;
}
/* Role chips and tag chips are one neutral style. */
.chip.role-chip { color: var(--ink-dim); }
.chip.tag-chip { color: var(--ink-dim); }
.chip.demo-chip { color: var(--warn); border-color: rgba(255, 207, 92, 0.4); }

.preset-foot {
  margin-top: auto;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.83rem; color: var(--muted);
}
.preset-author { display: flex; align-items: center; gap: 8px; min-width: 0; }
.preset-author img { width: 22px; height: 22px; border-radius: 50%; flex: none; }
.preset-author span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.preset-foot .counts { margin-left: auto; display: flex; gap: 12px; align-items: center; flex: none; }
.preset-foot .counts span { display: inline-flex; align-items: center; gap: 5px; }
.preset-foot .counts svg { width: 14px; height: 14px; }
.preset-foot .liked { color: var(--accent); }

/* ---------- Filter bar ---------- */

.filter-bar {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-bottom: 30px;
  align-items: center;
}

.input, .select, textarea.input {
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-family: var(--font-body); font-size: 0.95rem;
  padding: 11px 14px;
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.input::placeholder { color: var(--muted-2); }
.input:focus, .select:focus, textarea.input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(12, 210, 157, 0.14);
}
/* Validation: red border once you have LEFT a bad field. Focus wins
   while you are back inside fixing it, so the accent ring shows and
   the red returns only if you leave it still wrong. */
.input.invalid:not(:focus), textarea.input.invalid:not(:focus) {
  border-color: #e5484d;
  box-shadow: 0 0 0 3px rgba(229, 72, 77, 0.14);
}
.select {
  appearance: none; -webkit-appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2393aaa1' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}
.select option { background: var(--panel-2); color: var(--ink); }

.search-wrap { position: relative; flex: 1; min-width: 220px; }
.search-wrap .input { width: 100%; padding-left: 42px; }
.search-wrap svg {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--muted-2); pointer-events: none;
}

/* ---------- Preset detail ---------- */

.detail-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px;
  gap: 36px; align-items: start;
}

.detail-main { min-width: 0; }

.detail-cover {
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
  margin-bottom: 14px;
  background: #000;
}
.detail-cover img { width: 100%; max-height: 560px; object-fit: contain; background: #11111120; }

.detail-thumbs { display: flex; gap: 10px; margin-bottom: 26px; flex-wrap: wrap; }
.detail-thumbs button {
  position: relative;
  width: 108px; aspect-ratio: 16/9; border-radius: 8px; overflow: hidden;
  border: 2px solid transparent; background: var(--panel); cursor: pointer; padding: 0;
  opacity: 0.65; transition: opacity 0.15s, border-color 0.15s;
}
.detail-thumbs button img { width: 100%; height: 100%; object-fit: cover; }
.detail-thumbs button.active, .detail-thumbs button:hover { opacity: 1; border-color: var(--accent); }
/* The video slide's tile: a mini play badge so it reads as a video
   while it is still just one thumbnail among the screenshots. */
.detail-thumbs .thumb-play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(12, 210, 157, 0.92); color: #04120d;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
.detail-thumbs .thumb-play svg { width: 12px; height: 12px; margin-left: 2px; }

/* ---------- Discord help CTA (EUI Support page) ----------
   One loud card at the top: the fast lane for people who need a human.
   The hint under it turns everyone else toward the walkthroughs. */
.help-cta {
  display: flex; align-items: center; gap: 18px;
  padding: 24px 26px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  color: var(--ink);
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.help-cta:hover { border-color: #5865f2; color: var(--ink); transform: translateY(-2px); }
.help-cta .hc-icon {
  width: 48px; height: 48px; flex: none;
  display: grid; place-items: center;
  border-radius: 12px;
  background: rgba(88, 101, 242, 0.14);
  border: 1px solid rgba(88, 101, 242, 0.35);
  color: #8891f2;
}
.help-cta .hc-icon svg { width: 24px; height: 24px; }
.help-cta .hc-copy { flex: 1; min-width: 0; display: grid; gap: 3px; }
.help-cta .hc-copy b { font-size: 1.05rem; }
.help-cta .hc-copy span { font-size: 0.88rem; color: var(--muted); }
.help-cta .btn { flex: none; }
.help-hint {
  margin: 14px 2px 0;
  font-size: 0.85rem; color: var(--muted);
}
@media (max-width: 640px) {
  .help-cta { flex-wrap: wrap; }
  .help-cta .hc-copy { flex: 1 1 calc(100% - 66px); }
  .help-cta .btn { margin-left: 66px; }
}

/* ---------- Merch banner (support page) ----------
   Deliberately quieter than the three cards above it: merch is the
   footnote, not one of the main asks. */
.merch-banner {
  display: flex; align-items: center; gap: 16px;
  margin-top: 26px; padding: 18px 22px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  color: var(--ink);
  transition: border-color 0.2s ease;
}
.merch-banner:hover { border-color: var(--line-strong); color: var(--ink); }
.merch-banner .mb-icon {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--accent-soft);
  border: 1px solid var(--line-accent);
  color: var(--accent);
}
.merch-banner .mb-icon svg { width: 19px; height: 19px; }
.merch-banner .mb-copy { flex: 1; min-width: 0; display: grid; gap: 2px; }
.merch-banner .mb-copy b { font-size: 0.95rem; }
.merch-banner .mb-copy span { font-size: 0.85rem; color: var(--muted); }
.merch-banner .mb-cta {
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  font-weight: 600; font-size: 0.88rem; color: var(--accent);
}
.merch-banner .mb-cta svg { width: 14px; height: 14px; }
@media (max-width: 640px) {
  .merch-banner { flex-wrap: wrap; }
  .merch-banner .mb-copy { flex: 1 1 calc(100% - 56px); }
  .merch-banner .mb-cta { margin-left: 56px; }
}

/* ---------- EUI Legends (support page) ----------
   The in-game EUI Legends page on the web: a seasonal podium, the
   numbered all-time donor wall and the staff groups. Colour stays
   neutral except where it means something - the podium metals
   (gold / silver / bronze) and the season chip. --season is an
   "r, g, b" triplet set by js/legends.js; --m is the metal triplet of
   one podium spot. */
/* The donate section above already ends on its own 88px, so this one
   opens tighter than a standard .sec (92px, 64px at 900px and below). */
.legends { border-top: 1px solid var(--line-soft); padding-top: 68px; --season: 12, 210, 157; }
@media (max-width: 900px) {
  .legends { padding-top: 48px; }
}
.lg-top { position: relative; }
.lg-top > :not(.lg-fx) { position: relative; z-index: 1; }
.legends .sec-head { margin-bottom: 34px; }

.lg-podium-head {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 12px; margin-bottom: 22px;
}
.lg-podium-label {
  font-family: var(--font-display);
  font-size: 0.76rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-dim);
}
.lg-season {
  font-family: var(--font-display);
  font-size: 0.78rem; font-weight: 600;
  color: rgb(var(--season));
  padding: 4px 12px; border-radius: 999px;
  border: 1px solid rgba(var(--season), 0.38);
  background: rgba(var(--season), 0.08);
}

/* Podium: DOM order is 1-2-3 (what a screen reader should hear); the
   grid shows 2-1-3 with every bottom edge on one line, so #1 reads
   raised purely by being taller - the in-game layout. */
.lg-podium {
  list-style: none; margin: 0 auto; padding: 0;
  max-width: 780px;
  /* Zero minimums: a long name ellipsizes instead of widening its spot. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.16fr) minmax(0, 1fr);
  gap: 14px; align-items: end;
}
.lg-spot.r1 { order: 2; --m: 255, 204, 71; }
.lg-spot.r2 { order: 1; --m: 189, 199, 214; }
.lg-spot.r3 { order: 3; --m: 212, 138, 71; }
.lg-spot {
  position: relative;
  border-radius: 14px;
  padding: 1px;
  background: linear-gradient(180deg, rgba(var(--m), 0.5), rgba(255, 255, 255, 0.035) 72%);
}
.lg-spot.reveal { transition-delay: calc(var(--i, 0) * 90ms); }
.lg-spot-in {
  position: relative; overflow: hidden;
  border-radius: 13px;
  min-height: 150px;
  padding: 22px 16px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(var(--m), 0.09), transparent 62%),
    linear-gradient(180deg, var(--panel-2), var(--panel));
}
.lg-spot.r1 .lg-spot-in { min-height: 184px; padding-top: 18px; }
.lg-spot-in::before {
  content: ""; position: absolute; top: 0; left: 16%; right: 16%; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(var(--m), 0.95), transparent);
}
.lg-crown { width: 22px; height: 22px; margin-bottom: 6px; color: rgb(var(--m)); opacity: 0.9; }
.lg-medal {
  width: 38px; height: 38px; margin-bottom: 14px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--font-display); font-size: 0.92rem; font-weight: 700;
  color: rgb(var(--m));
  border: 1.5px solid rgba(var(--m), 0.62);
  background: radial-gradient(circle at 35% 30%, rgba(var(--m), 0.26), rgba(var(--m), 0.05) 70%);
}
.lg-spot.r1 .lg-medal { width: 46px; height: 46px; font-size: 1.05rem; }
.lg-name {
  max-width: 100%;
  font-family: var(--font-display); font-size: 1.12rem; font-weight: 600;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lg-spot.r1 .lg-name { font-size: 1.36rem; }
.lg-place {
  margin-top: 5px;
  font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(var(--m), 0.78);
}
/* A rank nobody holds yet keeps its card, quieter. */
.lg-spot.open { background: linear-gradient(180deg, rgba(var(--m), 0.2), rgba(255, 255, 255, 0.03) 72%); }
.lg-spot.open .lg-medal,
.lg-spot.open .lg-crown { opacity: 0.45; }
.lg-spot.open .lg-name { color: var(--muted-2); font-weight: 500; }
.lg-spot.open .lg-place { color: var(--muted-2); }

/* Walls: donors left, staff right, bottoms aligned. */
.lg-walls { display: grid; gap: 14px; margin-top: 56px; }
@media (min-width: 960px) {
  .lg-walls { grid-template-columns: 5fr 7fr; }
}
/* Information cards, not links: no hover lift. */
.gcard.lg-card:hover {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.11), rgba(255, 255, 255, 0.028));
  transform: none; box-shadow: none;
}
.lg-card .gcard-in { padding: 24px 24px 26px; }
/* Arriving from the patch notes' thank-you line: the staff card is
   outlined for a moment, like an in-game settings deep link. */
.lg-card.lg-focus { animation: lg-focus 2.4s ease-out; }
@keyframes lg-focus {
  0%, 55% { box-shadow: 0 0 0 2px var(--accent), 0 0 28px var(--accent-glow); }
  100% { box-shadow: 0 0 0 2px rgba(12, 210, 157, 0), 0 0 28px rgba(12, 210, 157, 0); }
}
.lg-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-bottom: 16px; margin-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.lg-card-head h3 { margin: 0; }
/* Small grey qualifier after a card title, e.g. "($100 or more)". */
.lg-sub {
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 500;
  letter-spacing: 0; color: var(--muted);
}
.lg-count {
  flex: none;
  font-family: var(--font-display); font-size: 0.76rem; font-weight: 600;
  color: var(--muted);
  padding: 3px 11px; border-radius: 999px;
  border: 1px solid var(--line-soft);
  font-variant-numeric: tabular-nums;
}

/* Numbered donors, reading across each row (1 2 / 3 4): a plain list,
   rows ruled by hairlines (never boxed - boxes read as buttons).
   js/legends.js marks the final row .lastrow so no rule dangles under it. */
.lg-donors {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0 28px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.lg-d {
  display: flex; align-items: center; gap: 12px;
  min-width: 0;
  padding: 9px 2px;
  border-bottom: 1px solid var(--line-soft);
}
.lg-d.lastrow { border-bottom-color: transparent; }

/* Side by side, the donors card never outgrows the staff card: the list
   sits in a scroll box whose own height counts for nothing (flex-basis 0,
   size containment), so the row takes the staff card's height and a
   longer list scrolls inside it. Stacked (narrower), it shows in full. */
.lg-scroll { min-height: 0; }
@media (min-width: 960px) {
  .lg-card-donors .lg-scroll {
    flex: 1 1 0;
    contain: size;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 8px;
  }
  /* A soft fade at the bottom edge while more names sit below. */
  .lg-card-donors .lg-scroll.lg-more {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
  }
  .lg-card-donors .lg-scroll::-webkit-scrollbar { width: 6px; }
  .lg-card-donors .lg-scroll::-webkit-scrollbar-track { background: transparent; }
  .lg-card-donors .lg-scroll::-webkit-scrollbar-thumb { background: #3a3a3f; border: 0; border-radius: 6px; }
}
.lg-n {
  flex: none; min-width: 1.7em; text-align: right;
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 600;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
.lg-dn {
  min-width: 0;
  font-size: 0.92rem; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lg-d.m1 { --m: 255, 204, 71; }
.lg-d.m2 { --m: 189, 199, 214; }
.lg-d.m3 { --m: 212, 138, 71; }
/* Ranks 1-3: number and name both in the podium metal. */
.lg-d.m1 .lg-n, .lg-d.m2 .lg-n, .lg-d.m3 .lg-n { color: rgb(var(--m)); font-weight: 700; }
.lg-d.m1 .lg-dn, .lg-d.m2 .lg-dn, .lg-d.m3 .lg-dn { color: rgb(var(--m)); font-weight: 600; }
@media (max-width: 420px) {
  .lg-donors { grid-template-columns: minmax(0, 1fr); }
  /* One column: only the very last name drops its rule. */
  .lg-d.lastrow { border-bottom-color: var(--line-soft); }
  .lg-d:last-child { border-bottom-color: transparent; }
}

/* Staff groups: a label ruled off to the right, then the names as a
   plain bulleted list in as many columns as fit (a neutral bullet:
   fifty-odd accent dots would turn the accent into texture). */
.lg-group + .lg-group { margin-top: 22px; }
.lg-group-h {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 12px;
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-dim);
}
.lg-group-h em { font-style: normal; color: var(--muted-2); letter-spacing: 0.04em; }
/* The rule keeps a floor so it still shows when a long label wraps. */
.lg-group-h::after { content: ""; flex: 1 0 24px; height: 1px; background: var(--line-soft); }
.lg-names {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 2px 20px;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.lg-names li {
  display: flex; align-items: center; gap: 10px;
  min-width: 0;
  padding: 4px 0;
  font-size: 0.9rem; color: var(--ink-dim);
}
.lg-names li::before {
  content: ""; flex: none; width: 4px; height: 4px; border-radius: 50%;
  background: var(--muted-2);
}
.lg-names li span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Seasonal particles in the side margins of the head. Each .lg-leaf is
   a full-height column that slides down by its own height, carrying its
   particle from the top edge to the bottom edge on the compositor; the
   <i> inside sways, spins and fades. --x is a fraction of the real
   gutter beside the 780px content column (heading and podium), measured
   from the outer edge, so the lanes can never reach the cards; hidden
   once that gutter is too narrow to hold them. */
.lg-fx { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.lg-leaf {
  position: absolute; top: 0; bottom: 0; width: 0;
  left: calc((100% - 780px) / 2 * var(--x));
  animation: lg-drop var(--d) linear var(--w) infinite;
}
.lg-leaf.rt { left: auto; right: calc((100% - 780px) / 2 * var(--x)); }
.lg-leaf i {
  position: absolute; left: 0; bottom: 0;
  width: var(--s); height: var(--s);
  margin-left: calc(var(--s) / -2);
  background: rgb(var(--c));
  border-radius: 0 80% 0 80%;
  opacity: 0;
  animation: lg-tumble var(--d) ease-in-out var(--w) infinite;
}
.legends[data-season="spring"] .lg-leaf i { border-radius: 80% 0 80% 0; }
.legends[data-season="winter"] .lg-leaf i {
  border-radius: 50%;
  box-shadow: 0 0 6px rgba(var(--c), 0.6);
}
.legends[data-season="summer"] .lg-leaf i {
  border-radius: 50%;
  box-shadow: 0 0 8px 1px rgba(var(--c), 0.75);
}
/* Summer sparks rise instead of fall. */
.legends[data-season="summer"] .lg-leaf,
.legends[data-season="summer"] .lg-leaf i { animation-direction: reverse; }
.lg-paused .lg-leaf,
.lg-paused .lg-leaf i { animation-play-state: paused; }
@keyframes lg-drop {
  from { transform: translateY(-100%); }
  to   { transform: translateY(0); }
}
@keyframes lg-tumble {
  0%   { opacity: 0; transform: translateX(0) rotate(0deg); }
  14%  { opacity: var(--a); }
  50%  { transform: translateX(var(--sway)) rotate(calc(var(--r) * 0.5)); }
  82%  { opacity: var(--a); }
  100% { opacity: 0; transform: translateX(calc(var(--sway) * 0.3)) rotate(var(--r)); }
}
/* Below ~1040px the gutter drops under ~90px: too tight for a lane plus
   its sway. */
@media (max-width: 1039px) {
  .lg-fx { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .lg-fx { display: none; }
  .lg-spot.reveal { transition-delay: 0s; }
}

.lg-who { display: flex; flex-direction: column; align-items: center; min-width: 0; max-width: 100%; }

@media (max-width: 620px) {
  /* One column, in rank order, each spot a compact row: medal, then
     the name over its place. */
  .lg-podium { grid-template-columns: minmax(0, 1fr); max-width: 420px; gap: 10px; }
  .lg-spot.r1, .lg-spot.r2, .lg-spot.r3 { order: 0; }
  .lg-spot-in, .lg-spot.r1 .lg-spot-in {
    min-height: 0; padding: 12px 16px;
    flex-direction: row; justify-content: flex-start; gap: 14px;
    text-align: left;
  }
  .lg-medal, .lg-spot.r1 .lg-medal { margin-bottom: 0; flex: none; }
  .lg-crown { display: none; }
  .lg-who { align-items: flex-start; flex: 1; }
  .lg-spot.r1 .lg-name { font-size: 1.2rem; }
  .lg-walls { margin-top: 40px; }
  .lg-card .gcard-in { padding: 20px 18px 22px; }
}

/* The scale subtitle - sits between "About this preset" and the
   description, so it pulls up toward the title it belongs to. */
.p-scale { margin: -6px 0 14px; font-size: 0.82rem; color: var(--muted); }
.p-scale b { color: var(--ink-dim); font-weight: 700; }

.side-card {
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 18px;
}
.side-card h3 { font-size: 0.95rem; margin-bottom: 14px; color: var(--ink-dim); }

/* ---------- Resolution switcher (import string card) ----------
   Present only when a preset ships strings for more than one
   resolution; drives which string the box shows and copy copies. */
.res-picker { display: flex; gap: 6px; margin-bottom: 10px; }
.res-picker button {
  flex: 1; padding: 7px 4px; border-radius: 8px;
  font-family: var(--font-body); font-size: 0.78rem; font-weight: 600;
  background: transparent; border: 1px solid var(--line); color: var(--muted);
  cursor: pointer; transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.res-picker button:hover { color: var(--ink); border-color: var(--line-strong); }
.res-picker button.on {
  color: var(--accent); border-color: var(--accent);
  background: rgba(12, 210, 157, 0.08);
}

/* ---------- Description links (preset page) ----------
   The author's titled links, rendered as ordinary text links stacked
   under the description - they read as part of the prose. */
.desc-links { display: grid; gap: 4px; margin-top: 14px; }
.desc-links a { width: fit-content; font-weight: 600; }
.desc-links a:hover { text-decoration: underline; }

.export-box {
  position: relative;
  background: #141416;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 14px;
  font-family: var(--font-mono); font-size: 0.78rem; color: var(--muted);
  max-height: 130px; overflow: hidden;
  word-break: break-all;
  line-height: 1.5;
}
.export-box::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 60px;
  background: linear-gradient(transparent, #141416);
}

/* Heart + downloads side by side in the sidebar's top card. */
.stat-card { display: flex; align-items: center; justify-content: center; gap: 12px; }
.dl-stat {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: 999px;
  border: 1px solid var(--line-soft);
  color: var(--ink-dim);
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
}
.dl-stat svg { width: 18px; height: 18px; }

.like-btn {
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  color: var(--ink-dim);
  border-radius: 999px; padding: 10px 20px;
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.like-btn svg { width: 18px; height: 18px; transition: fill 0.15s, color 0.15s, transform 0.15s; }
.like-btn:hover { border-color: var(--line-strong); color: #fff; }
/* Liked: the heart fills and the count takes the accent - the pill
   itself stays put, so the state reads in the icon, not the button. */
.like-btn.liked svg { fill: var(--accent); color: var(--accent); }
.like-btn.liked span { color: var(--accent); }
.like-btn:active svg { transform: scale(0.85); }

/* ---------- Comments ---------- */

.comment-form { display: flex; gap: 14px; margin-bottom: 28px; }
.comment-form img { width: 40px; height: 40px; border-radius: 50%; flex: none; }
.comment-form .cf-main { flex: 1; }
.comment-form textarea { width: 100%; min-height: 84px; resize: vertical; }
.comment-form .cf-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 10px; }

.comment {
  display: flex; gap: 14px;
  padding: 18px 0;
  border-bottom: 1px solid var(--line-soft);
}
.comment:last-child { border-bottom: none; }
/* The comment a notification links to: a soft accent tint that fades. */
.comment.is-arrived { border-radius: 8px; animation: comment-arrive 2.4s ease-out; }
@keyframes comment-arrive {
  0%, 55% { background: var(--accent-soft); box-shadow: 0 0 0 10px var(--accent-soft); }
  100%    { background: transparent; box-shadow: 0 0 0 10px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .comment.is-arrived { animation: none; background: var(--accent-soft); box-shadow: 0 0 0 10px var(--accent-soft); }
}
.comment > img { width: 38px; height: 38px; border-radius: 50%; flex: none; }
.comment-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.comment-head b { font-size: 0.94rem; }
.comment-head time { font-size: 0.78rem; color: var(--muted-2); }
.comment-head .mod-badge {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--line-accent);
  padding: 1px 7px; border-radius: 999px;
}
.comment-body { color: var(--ink-dim); font-size: 0.95rem; margin-top: 4px; white-space: pre-wrap; word-break: break-word; }
.comment-actions { margin-top: 7px; display: flex; gap: 14px; }
.comment-actions button {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted-2); font-size: 0.78rem; font-weight: 600; font-family: var(--font-body);
}
.comment-actions button:hover { color: var(--danger); }

/* ---------- Forms (submit page) ---------- */

.form-card {
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  padding: 34px;
  max-width: 780px;
}

.form-row { margin-bottom: 22px; }
.form-row label.frm-label {
  display: block; font-family: var(--font-display);
  font-weight: 600; font-size: 0.9rem; margin-bottom: 8px;
}
.form-row .hint { font-size: 0.8rem; color: var(--muted-2); margin-top: 6px; }
.form-row .req { color: var(--accent); }
/* Sub-label inside a grouped row - the per-resolution import boxes. */
.form-row .frm-sub {
  display: block; font-size: 0.78rem; font-weight: 600;
  color: var(--ink-dim); margin-bottom: 6px;
}

/* Links editor: Title | URL | remove, one row per link, "+" adds one. */
.link-rows { display: grid; gap: 8px; margin-bottom: 10px; }
.link-rows:empty { margin-bottom: 0; }
.link-row-edit { display: grid; grid-template-columns: 1fr 1.6fr auto; gap: 8px; align-items: center; }
.link-row-edit .lk-remove {
  width: 34px; height: 34px; border-radius: 8px;
  background: transparent; border: 1px solid var(--line-soft);
  color: var(--muted); cursor: pointer; font-size: 0.8rem;
  transition: border-color 0.15s, color 0.15s;
}
.link-row-edit .lk-remove:hover { border-color: #e5484d; color: #e5484d; }
@media (max-width: 560px) {
  .link-row-edit { grid-template-columns: 1fr auto; }
  .link-row-edit .lk-url { grid-column: 1 / -1; }
}

/* Custom-link row: the domain is a fixed prefix, only the slug types. */
.slug-row { display: flex; align-items: center; gap: 8px; }
.slug-row .slug-prefix {
  flex: none; font-family: var(--font-mono); font-size: 0.85rem;
  color: var(--muted);
}
.slug-row .input { flex: 1; max-width: 280px; }
/* Phones: the prefix (ellesmereui.com/preset/) takes its own line and the
   name gets the full width, or a long name would not fit. */
@media (max-width: 560px) {
  .slug-row { flex-wrap: wrap; gap: 6px 8px; }
  .slug-row .input { flex-basis: 100%; max-width: none; }
}

/* Section labels inside a custom-select menu (from <optgroup>). */
.cs-menu .cs-group {
  padding: 9px 14px 3px;
  font-family: var(--font-display);
  font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted-2);
  pointer-events: none;
}
.cs-menu .cs-group + [data-v] { margin-top: 0; }
/* A divider between groups (an <optgroup> with no label): the Bug
   Tracker's sections and the Feature Hub's modules. The menu is a
   scrolling flex column, so the empty line must not shrink away. */
.cs-menu .cs-sep {
  flex: none;
  height: 1px; margin: 5px 10px;
  background: var(--line);
  pointer-events: none;
}
.form-row .input, .form-row .select, .form-row textarea { width: 100%; }
.form-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

/* Grouped: Game Type / Roles / Content / Theme sections, each its own chip row. */
.tag-picker { display: grid; gap: 14px; }
.tag-picker .tp-cap {
  font-family: var(--font-display);
  font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 7px;
}
.tag-picker .tp-row { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-picker button {
  background: var(--panel-2); border: 1px solid var(--line-soft);
  color: var(--muted); border-radius: 999px; padding: 7px 16px;
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 600; cursor: pointer;
  transition: all 0.15s ease;
}
.tag-picker button:hover { border-color: var(--line-strong); color: var(--ink); }
.tag-picker button.on {
  background: rgba(255, 255, 255, 0.10); border-color: #fff; color: #fff;
}

.img-drop {
  border: 2px dashed var(--line-soft);
  border-radius: var(--radius);
  padding: 30px; text-align: center; cursor: pointer;
  color: var(--muted); font-size: 0.92rem;
  transition: border-color 0.15s, background 0.15s;
}
.img-drop:hover, .img-drop.drag { border-color: var(--accent); background: var(--accent-soft); }
.img-drop svg { width: 30px; height: 30px; color: var(--accent); margin: 0 auto 10px; display: block; }

.img-previews { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.img-preview {
  position: relative; width: 150px; aspect-ratio: 16/9;
  border-radius: 8px; overflow: hidden; border: 1px solid var(--line);
}
.img-preview img { width: 100%; height: 100%; object-fit: cover; }
.img-preview button {
  position: absolute; top: 5px; right: 5px;
  width: 24px; height: 24px; border-radius: 6px;
  background: rgba(17, 17, 17, 0.85); border: 1px solid var(--line-soft);
  color: var(--danger); cursor: pointer; font-size: 13px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}

/* ---------- News ---------- */

.news-filters { display: flex; gap: 9px; flex-wrap: wrap; margin-bottom: 30px; }

/* Each entry is a card on a gradient hairline, matching the module cards
   on the home page. The old treatment was a borderless two-column row
   separated by rules, which read as a changelog dump rather than a page. */
.news-list { display: flex; flex-direction: column; gap: 14px; }

.news-item {
  border-radius: 14px;
  padding: 1px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.11), rgba(255, 255, 255, 0.028));
  transition: background 0.28s ease, transform 0.28s ease, box-shadow 0.3s ease;
}
.news-item:hover {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.20), rgba(255, 255, 255, 0.05));
  transform: translateY(-2px);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.21);
}
.news-inner {
  border-radius: 13px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  padding: 24px 26px 26px;
}

/* Date and category sit on one line above the headline instead of in a
   fixed 150px gutter, which was wasting a sixth of the width on phones. */
.news-date {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  color: var(--muted-2); font-size: 0.82rem;
  margin-bottom: 10px;
}
.news-date .cat {
  font-family: var(--font-body); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: rgba(255, 255, 255, 0.05);
  color: var(--ink-dim);
}
.news-content h3 { font-size: 1.18rem; margin-bottom: 8px; }
.news-content .news-body { color: var(--muted); font-size: 0.97rem; }
.news-content .news-body ul { margin: 10px 0 0; padding-left: 20px; }
.news-content .news-body li { margin-bottom: 5px; }
.news-content .news-body b { color: var(--ink-dim); }
.news-video { margin-top: 16px; max-width: 560px; }

/* ---------- CTA band ---------- */

.cta-band {
  position: relative;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background:
    radial-gradient(600px 260px at 50% -80px, rgba(12, 210, 157, 0.08), transparent 65%),
    linear-gradient(180deg, var(--panel-2), var(--panel));
  padding: 66px 40px;
  text-align: center;
  overflow: hidden;
}
.cta-band h2 { margin-bottom: 12px; }
.cta-band p { color: var(--muted); max-width: 560px; margin: 0 auto 30px; }
.cta-band .hero-ctas { justify-content: center; margin-bottom: 0; }

/* ---------- Support strip ---------- */


/* ---------- Notices / banners ---------- */

.notice {
  display: flex; gap: 13px; align-items: flex-start;
  background: rgba(255, 207, 92, 0.06);
  border: 1px solid rgba(255, 207, 92, 0.3);
  border-radius: var(--radius);
  padding: 15px 18px;
  color: var(--ink-dim);
  font-size: 0.92rem;
  margin-bottom: 28px;
}
.notice svg { width: 19px; height: 19px; flex: none; color: var(--warn); margin-top: 2px; }
.notice.accent {
  background: var(--accent-soft);
  border-color: var(--line-accent);
}
.notice.accent svg { color: var(--accent); }
.notice b { color: var(--ink); }
.notice p { margin: 0; }

.empty-state {
  text-align: center; padding: 70px 20px; color: var(--muted);
}
.empty-state img { width: 64px; margin: 0 auto 18px; opacity: 0.4; }
.empty-state h3 { color: var(--ink-dim); }

.load-more-wrap { text-align: center; margin-top: 36px; }

/* ---------- Toast ---------- */

.toast-host {
  position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%);
  z-index: 300; display: flex; flex-direction: column; gap: 10px; align-items: center;
  pointer-events: none;
}
.toast {
  background: var(--panel-3);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  border-radius: 10px;
  padding: 12px 22px;
  font-size: 0.92rem; font-weight: 500;
  box-shadow: var(--shadow-1);
  animation: toast-in 0.25s ease;
  max-width: min(92vw, 480px);
  text-align: center;
}
.toast.err { border-color: rgba(255, 92, 108, 0.55); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

/* "Opening in the Discord app" (app.js, Discord links on a desktop): the
   toast's place and look, the site's Discord tile, and the web address one
   click away while the app may not be installed. Firefox's launch frame
   (.dc-frame) never shows. */
.dc-open {
  position: fixed; left: 50%; bottom: 26px; z-index: 301;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  width: max-content; max-width: min(92vw, 480px);
  padding: 10px 8px 10px 12px;
  background: var(--panel-3);
  border: 1px solid var(--line-strong); border-radius: 12px;
  box-shadow: var(--shadow-1);
  animation: dc-open-in 0.25s ease;
}
.dc-open[hidden] { display: none; }
@keyframes dc-open-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
.dc-open-ic {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border-radius: 9px;
  background: rgba(88, 101, 242, 0.14);
  border: 1px solid rgba(88, 101, 242, 0.35);
  color: #8891f2;
}
.dc-open-ic svg { width: 16px; height: 16px; }
.dc-open-txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.dc-open-t { font-size: 0.9rem; font-weight: 600; line-height: 1.35; color: var(--ink); }
.dc-open-s { font-size: 0.8rem; line-height: 1.4; color: var(--muted); }
.dc-open-s a { color: var(--ink-dim); text-decoration: underline; text-underline-offset: 2px; transition: color 0.15s ease; }
.dc-open-s a:hover { color: var(--ink); }
.dc-open-s a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.dc-open-x {
  width: 30px; height: 30px; flex: none; margin-left: 4px;
  display: grid; place-items: center;
  background: none; border: 0; border-radius: 8px; cursor: pointer;
  color: var(--muted);
  transition: color 0.15s ease, background 0.15s ease;
}
.dc-open-x svg { width: 15px; height: 15px; }
.dc-open-x:hover { color: var(--ink); background: rgba(255, 255, 255, 0.05); }
.dc-open-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dc-frame { display: none; }
@media (prefers-reduced-motion: reduce) {
  .dc-open { animation: none; }
  .dc-open-s a, .dc-open-x { transition: none; }
}

/* ---------- Modal ---------- */

.modal-overlay {
  position: fixed; inset: 0; z-index: 250;
  background: rgba(9, 9, 9, 0.8);
  backdrop-filter: blur(4px);
  display: none; align-items: center; justify-content: center;
  padding: 24px;
}
.modal-overlay.open { display: flex; }
.modal {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  width: 100%; max-width: 440px;
  padding: 28px;
  animation: toast-in 0.2s ease;
}
.modal h3 { margin-bottom: 10px; }
.modal p { color: var(--muted); font-size: 0.95rem; }
.modal textarea { width: 100%; min-height: 80px; margin-top: 6px; resize: vertical; }
.modal-actions { display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px; }

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

.site-footer {
  border-top: 1px solid var(--line-soft);
  background: var(--bg-1);
  padding: 64px 0 34px;
  margin-top: 90px;
}
.footer-grid {
  display: grid; grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 40px; margin-bottom: 44px;
}
.footer-brand .nav-logo { margin-bottom: 14px; }
.footer-brand p { color: var(--muted); font-size: 0.9rem; max-width: 300px; }

.footer-col h4 {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 16px;
}
.footer-col a {
  display: block; color: var(--muted); font-size: 0.92rem;
  padding: 5px 0; transition: color 0.15s;
}
.footer-col a:hover { color: var(--accent); }

.footer-bottom {
  border-top: 1px solid var(--line-soft);
  padding-top: 26px;
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: center; justify-content: space-between;
  color: var(--muted-2); font-size: 0.8rem;
}

/* Team entry point. Deliberately quiet - it is signposting for staff, not
   a feature to advertise. The page behind it is protected by RLS either
   way, so the link being public gives nothing away. */
.foot-staff {
  color: var(--muted-2);
  margin-left: 6px;
  border-bottom: 1px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.foot-staff:hover { color: var(--ink-dim); border-bottom-color: var(--line); }
.footer-social { display: flex; gap: 10px; }
.footer-social a {
  width: 36px; height: 36px; border-radius: 9px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); transition: all 0.15s;
}
.footer-social a:hover { color: var(--accent); border-color: var(--line-strong); transform: translateY(-2px); }
.footer-social svg { width: 17px; height: 17px; }

/* ---------- Skeletons ---------- */

.skeleton {
  display: block;
  border-radius: var(--radius);
  background: linear-gradient(100deg, var(--panel) 40%, var(--panel-3) 50%, var(--panel) 60%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
  border: 1px solid var(--line-soft);
}
@keyframes shimmer { to { background-position: -200% 0; } }
.skeleton-card { height: 320px; }
/* No shimmer for visitors who asked for less motion: a still block still
   says "something is coming here". */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; background: var(--panel); }
}

/* ---------- Checkbox ----------
   One checkbox for every team surface and dialog (the Admin page, the
   Feedback Hub's dialogs). The real input stays in the page
   for keyboards and screen readers, out of sight; the box is drawn on the
   tokens, and a checked box wears the accent only as its outline and
   tick. Markup: <label class="check"><input type="checkbox"><span
   class="check-box">tick svg</span><span class="check-text">Label</span>
   </label> (a visually hidden label instead of .check-text where the row
   around it already says what it is). */
.check {
  position: relative;
  display: inline-flex; align-items: center; gap: 9px;
  flex: none; cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
.check input {
  position: absolute; left: 0; top: 50%;
  width: 1px; height: 1px; margin: 0;
  opacity: 0; pointer-events: none;
}
/* A 40 px target around the 18 px box without making the row taller. It
   reaches 10 px to the right, so whatever follows keeps 11 px clear. */
.check::after { content: ""; position: absolute; inset: -11px -10px -11px -12px; }
.check-box {
  width: 18px; height: 18px; flex: none;
  display: grid; place-items: center;
  border-radius: 5px;
  background: #212124;
  border: 1px solid var(--line);
  color: transparent;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.check-box svg { width: 12px; height: 12px; }
.check:hover .check-box { border-color: var(--line-strong); }
.check input:checked + .check-box { background: #16302a; border-color: var(--accent); color: var(--accent); }
.check input:indeterminate + .check-box { background: #16302a; border-color: var(--line-accent); }
.check input:indeterminate + .check-box svg { display: none; }
.check input:indeterminate + .check-box::after {
  content: ""; width: 8px; height: 2px; border-radius: 2px; background: var(--accent);
}
.check input:focus-visible + .check-box { box-shadow: 0 0 0 3px rgba(12, 210, 157, 0.22); }
.check input:disabled + .check-box { opacity: 0.35; }
.check.is-disabled { cursor: not-allowed; }
.check.is-disabled:hover .check-box { border-color: var(--line); }
.check.is-radio .check-box { border-radius: 50%; }
.check.is-radio .check-box svg { display: none; }
.check.is-radio input:checked + .check-box::after {
  content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
}
.check-text { font-size: 0.84rem; font-weight: 600; color: var(--muted); transition: color 0.18s ease; }
.check:hover .check-text { color: var(--ink-dim); }
/* A sentence for a label (a dialog's option): it wraps beside the box,
   which stays on its first line. */
.check.is-wrap { display: flex; align-items: flex-start; flex: 0 1 auto; min-width: 0; gap: 11px; }
.check.is-wrap::after { inset: -6px -4px -6px -8px; }
.check.is-wrap .check-box { margin-top: 2px; }
.check.is-wrap .check-text { min-width: 0; font-size: 0.88rem; font-weight: 500; line-height: 1.5; color: var(--ink-dim); }
.check.is-wrap:hover .check-text { color: var(--ink); }

/* ---------- Selection bar ----------
   Appears above a list with the first pick and goes with the last (the
   team's list tools). The host spans nothing but the bar, yet sticks while
   the list scrolls under it: a sticky element only travels inside its
   parent, so the host is what sticks. Markup: <div class="bulk-host">
   <div class="bulk-bar" role="region"><span class="bulk-count"><b>3</b>
   selected</span><div class="bulk-acts">buttons</div><button
   class="bulk-clear">Clear</button></div></div>. */
.bulk-host {
  position: sticky;
  top: calc(var(--nav-h) + 12px);
  z-index: 30;
}
.bulk-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  margin-bottom: 14px;
  padding: 10px 10px 10px 18px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.4);
  animation: bulk-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes bulk-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.bulk-count { font-size: 0.86rem; color: var(--muted); white-space: nowrap; }
.bulk-count b { font-family: var(--font-display); font-size: 1rem; font-weight: 700; color: var(--ink); margin-right: 2px; }
.bulk-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.bulk-clear {
  margin-left: auto;
  min-height: 38px; padding: 9px 12px; border-radius: var(--radius-sm);
  background: none; border: none; cursor: pointer;
  font-family: var(--font-body); font-size: 0.84rem; font-weight: 600;
  color: var(--muted);
  transition: color 0.18s ease, background 0.18s ease;
}
.bulk-clear:hover { color: var(--ink); background: rgba(255, 255, 255, 0.05); }
.bulk-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 720px) {
  .bulk-bar { padding: 12px; gap: 10px; }
  .bulk-count { flex: 1; }
  .bulk-acts { order: 3; width: 100%; }
  .bulk-acts .btn { flex: 1 1 auto; }
}
@media (prefers-reduced-motion: reduce) {
  .bulk-bar { animation: none; }
  .check-box, .check-text, .bulk-clear { transition: none; }
}

/* ---------- Reveal on scroll ---------- */

.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.55s ease, transform 0.55s ease; }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
}

@media (max-width: 920px) {
  .split { grid-template-columns: 1fr; gap: 34px; }
  .split.reverse .split-media { order: 0; }
  .detail-grid { grid-template-columns: 1fr; }
  .videos-grid, .videos-grid.guides { grid-template-columns: 1fr 1fr; }
  .preset-grid { grid-template-columns: 1fr 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 760px) {
  /* Solid, not see-through: the header's own blur makes it the backdrop
     root, so a blur here could never reach the page behind and the page
     text showed through. Scrolls inside itself on short screens. */
  .nav-links {
    position: fixed; top: var(--nav-h); left: 0; right: 0;
    background: var(--bg-0);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-1);
    flex-direction: column; align-items: stretch;
    padding: 12px 20px 18px; gap: 2px;
    max-height: calc(100vh - var(--nav-h));
    max-height: calc(100dvh - var(--nav-h));
    overflow-y: auto; overscroll-behavior: contain;
    display: none;
  }
  .nav-links.open { display: flex; }
  .nav-links a { padding: 14px 14px; font-size: 1rem; border-radius: 8px; }
  /* Stacked rows get a fill so the whole row responds, not just the text. */
  .nav-links a:hover, .nav-links a.active { background: rgba(255, 255, 255, 0.05); }
  .nav-links a.active { color: var(--accent); }
  .nav-links a:focus-visible, .nav-link-btn:focus-visible { outline-offset: -2px; border-radius: 8px; }

  /* Resources unfolds in place: its button is a row like the others,
     and its links hang under it as one indented group. */
  .nav-drop { display: block; }
  .nav-link-btn.nav-drop-btn {
    width: 100%; justify-content: space-between;
    padding: 14px; font-size: 1rem; border-radius: 8px;
  }
  /* Only where hovering is real: on a touch screen :hover sticks after a
     tap, and the row would stay filled after folding the group away. */
  @media (hover: hover) {
    .nav-drop-btn:hover { background: rgba(255, 255, 255, 0.05); }
  }
  .nav-drop-chev { width: 14px; height: 14px; }
  .nav-drop-menu,
  .nav-drop-menu.open {
    position: static; width: auto; padding: 0;
    background: none; border: 0; border-radius: 0; box-shadow: none;
    opacity: 1; transform: none;
  }
  .nav-drop-menu {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows 0.24s ease, visibility 0s linear 0.24s;
  }
  .nav-drop-menu.open {
    grid-template-rows: 1fr;
    transition: grid-template-rows 0.24s ease, visibility 0s;
  }
  .nav-drop-menu::before { display: none; }
  .nav-drop-list {
    min-height: 0; overflow: hidden; gap: 0;
    margin-left: 14px; padding-left: 8px;
    border-left: 1px solid var(--line-soft);
  }
  .nav-drop-list > a:first-child { margin-top: 2px; }
  .nav-drop-list > a:last-child { margin-bottom: 6px; }
  /* One step quieter than the rows above them: the group reads as part
     of Resources, not as three more top-level entries. */
  .nav-links .nav-drop-menu a { padding: 9px 10px; font-size: 0.95rem; font-weight: 500; color: var(--muted); }
  .nav-links .nav-drop-menu a.active { color: var(--accent); }

  .nav-burger { display: flex; }
  /* On phones sign-in lives INSIDE the burger menu (the .nav-signin-item
     entry app.js appends), so the header button hides here along with
     the rest. */
  .nav-actions .btn { display: none; }
  .nav-links .nav-signin-item {
    display: flex; align-items: center; gap: 9px;
    margin-top: 6px; padding-top: 16px;
    border-top: 1px solid var(--line-soft); border-radius: 0;
    color: var(--accent); font-weight: 600;
  }

  .section { padding: 60px 0; }
  .gallery-grid { grid-template-columns: 1fr 1fr; }
  .videos-grid, .videos-grid.guides { grid-template-columns: 1fr; }
  .preset-grid { grid-template-columns: 1fr; }
  .form-card { padding: 22px; }
  .form-2col { grid-template-columns: 1fr; }
  .cta-band { padding: 46px 22px; }
}

/* The Resources dropdown appears and unfolds without motion. */
@media (prefers-reduced-motion: reduce) {
  .nav-drop-chev, .nav-links .nav-drop-menu a,
  .nav-drop-menu, .nav-drop-menu.open { transition: none; }
}
@media (prefers-reduced-motion: reduce) and (min-width: 761px) {
  .nav-drop-menu { transform: translate(-50%, 0); }
}

@media (max-width: 480px) {
  .gallery-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .hero-ctas .btn { width: 100%; }
}


/* ============================================================
   ADMIN PAGE - TEAM, AUDIT AND PRESET MODERATION (admin.html)
   Moderation queue. Same design language as the public site:
   dark panels, accent hairlines, quiet motion.
   ============================================================ */

/* ---------- Access gates ---------- */

.staff-gate { max-width: 620px; margin: 0 auto; }
.staff-gate .notice { margin-bottom: 0; }

/* ---------- Stat tiles ---------- */

.staff-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 26px;
}

.stat-tile {
  position: relative;
  display: flex; align-items: center; gap: 15px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 18px 20px;
  overflow: hidden;
  transition: border-color 0.22s ease, transform 0.22s ease;
}
.stat-tile:hover { border-color: var(--line); transform: translateY(-2px); }

.stat-tile .st-icon {
  width: 42px; height: 42px; flex: none;
  display: grid; place-items: center;
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line-soft);
  color: var(--muted);
  transition: color 0.22s ease, border-color 0.22s ease, background 0.22s ease;
}
.stat-tile .st-icon svg { width: 19px; height: 19px; }

.stat-tile .st-num {
  font-family: var(--font-display);
  font-size: 1.6rem; font-weight: 800; line-height: 1;
  color: var(--ink);
  letter-spacing: -0.02em;
}
.stat-tile .st-label {
  font-size: 0.78rem; color: var(--muted-2);
  margin-top: 5px; letter-spacing: 0.02em;
}

/* A tile with work waiting lights up. */
.stat-tile.live { border-color: var(--line-accent); }
.stat-tile.live .st-icon {
  background: var(--accent-soft);
  border-color: var(--line-accent);
  color: var(--accent);
}
.stat-tile.live .st-num { color: var(--accent); }
.stat-tile.live::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(120px 60px at 12% 0%, rgba(12, 210, 157, 0.10), transparent 70%);
  pointer-events: none;
}

/* ---------- Segmented tab control ---------- */

.seg-wrap { display: flex; justify-content: center; margin-bottom: 26px; }

.seg {
  position: relative;
  display: inline-flex;
  padding: 5px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.seg::-webkit-scrollbar { display: none; }

/* Sliding indicator - position comes from JS as --seg-x / --seg-w. */
.seg::before {
  content: "";
  position: absolute;
  top: 5px; bottom: 5px;
  left: var(--seg-x, 5px);
  width: var(--seg-w, 0);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid #fff;
  box-shadow: none;
  transition: left 0.36s cubic-bezier(0.22, 1, 0.36, 1), width 0.36s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.seg.no-anim::before { transition: none; }

.seg button {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 8px;
  background: none; border: none;
  padding: 9px 20px;
  border-radius: 999px;
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
  color: var(--muted); cursor: pointer; white-space: nowrap;
  transition: color 0.22s ease;
}
.seg button:hover { color: var(--ink-dim); }
.seg button.on { color: #fff; }

.seg .seg-count {
  font-size: 0.72rem; font-weight: 700;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 999px; padding: 1px 7px;
  color: var(--muted-2);
  transition: background 0.22s ease, color 0.22s ease;
}
.seg button.on .seg-count { background: rgba(255, 255, 255, 0.14); color: #fff; }

/* ---------- Report card ---------- */

.report-list { display: flex; flex-direction: column; gap: 14px; }

.report-card {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 18px 20px 16px 24px;
  overflow: hidden;
  transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.25s ease;
  animation: card-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
.report-card:hover {
  border-color: var(--line);
  transform: translateY(-2px);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.2);
}

/* Type stripe down the left edge. */
.report-card::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
  opacity: 0.75;
}
.report-card.t-comment::before { background: #5cb8ff; }
.report-card.is-hidden::before { background: var(--muted-2); }
.report-card.is-hidden { background: var(--bg-1); }

@keyframes card-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* Leaving: collapse + slide out. JS pins max-height first. */
.report-card.leaving {
  animation: none;
  overflow: hidden;
  opacity: 0;
  transform: translateX(24px);
  max-height: 0 !important;
  margin-bottom: -14px;
  padding-top: 0; padding-bottom: 0;
  border-color: transparent;
  transition: opacity 0.3s ease, transform 0.3s ease, max-height 0.38s ease 0.06s,
              padding 0.38s ease 0.06s, margin 0.38s ease 0.06s, border-color 0.3s ease;
}

/* Head row */
.report-head {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-bottom: 13px;
}
.rep-type {
  font-size: 0.64rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--line-accent);
  color: var(--accent); background: var(--accent-soft);
}
.rep-type.t-comment {
  color: #5cb8ff; background: rgba(92, 184, 255, 0.10); border-color: rgba(92, 184, 255, 0.35);
}
.rep-flag {
  font-size: 0.68rem; font-weight: 700;
  padding: 3px 9px; border-radius: 999px;
  background: rgba(255, 207, 92, 0.10);
  border: 1px solid rgba(255, 207, 92, 0.32);
  color: var(--warn);
}
.rep-flag.hid {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--line-soft);
  color: var(--muted-2);
}
.report-head time { margin-left: auto; font-size: 0.76rem; color: var(--muted-2); }

/* Target: thumbnail + title/body */
.report-target { display: flex; gap: 14px; align-items: flex-start; }

.rep-thumb {
  width: 84px; height: 56px; flex: none;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  display: grid; place-items: center;
}
.rep-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rep-thumb .ph { width: 26px; height: auto; object-fit: contain; opacity: 0.25; }

.rep-title {
  font-family: var(--font-display);
  font-size: 1.02rem; font-weight: 700; color: var(--ink);
  line-height: 1.35;
  word-break: break-word;
}
.rep-gone { color: var(--muted-2); font-style: italic; font-size: 0.92rem; }

.rep-quote {
  display: block;
  color: var(--ink-dim); font-size: 0.93rem; line-height: 1.55;
  white-space: pre-wrap; word-break: break-word;
  max-height: 5.2em; overflow: hidden;
}

.report-meta {
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  margin-top: 10px;
  font-size: 0.79rem; color: var(--muted-2);
}
.rep-person {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ink-dim); font-weight: 600;
}
.rep-person img { width: 19px; height: 19px; border-radius: 50%; }
.rep-person .mod-badge {
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--line-accent);
  padding: 0 6px; border-radius: 999px;
}

/* Reason quote block */
.report-reason {
  position: relative;
  margin-top: 14px;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-left: 2px solid var(--line-accent);
  border-radius: var(--radius-sm);
  padding: 11px 14px;
  color: var(--ink-dim);
  font-size: 0.88rem; line-height: 1.55;
  white-space: pre-wrap; word-break: break-word;
}
.report-reason.empty { color: var(--muted-2); font-style: italic; }

/* Actions */
.report-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 15px; padding-top: 14px;
  border-top: 1px solid var(--line-soft);
}
.report-actions .ra-right { margin-left: auto; display: flex; gap: 10px; flex-wrap: wrap; }
.report-actions .ra-left  { display: flex; gap: 10px; flex-wrap: wrap; }

/* Quiet neutral button for secondary staff actions. */
.btn-quiet {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--line-soft);
  color: var(--ink-dim);
}
.btn-quiet:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--line);
  color: var(--ink);
}

/* ---------- Team management (owners only) ---------- */

.team-tools { margin-bottom: 18px; }
.team-tools .search-wrap { width: 100%; }

.team-section-label {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted-2);
  margin: 24px 0 12px;
}
.team-section-label::after {
  content: ""; flex: 1; height: 1px; background: var(--line-soft);
}
.team-section-label:first-child { margin-top: 0; }

.user-row {
  display: flex; align-items: center; gap: 14px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 13px 18px;
  margin-bottom: 9px;
  transition: border-color 0.2s ease, transform 0.2s ease;
  animation: card-in 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 35ms);
}
.user-row:hover { border-color: var(--line); transform: translateY(-1px); }
.user-row.is-mod { border-color: var(--line); background: linear-gradient(180deg, var(--panel-2), var(--panel)); }

.user-row > img { width: 36px; height: 36px; border-radius: 50%; flex: none; }
.user-row .ur-main { flex: 1; min-width: 0; }
.user-row .ur-name {
  font-weight: 600; color: var(--ink); font-size: 0.94rem;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.user-row .ur-sub { font-size: 0.76rem; color: var(--muted-2); margin-top: 3px; }
.user-row .ur-you {
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--warn); border: 1px solid rgba(255, 207, 92, 0.32);
  padding: 0 6px; border-radius: 999px;
}

/* Toggle switch */
.switch { position: relative; width: 46px; height: 26px; flex: none; }
.switch input {
  position: absolute; inset: 0; opacity: 0; margin: 0;
  width: 100%; height: 100%; cursor: pointer; z-index: 2;
}
.switch .track {
  position: absolute; inset: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line-soft);
  transition: background 0.25s ease, border-color 0.25s ease;
}
.switch .knob {
  position: absolute; top: 4px; left: 4px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--muted-2);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), background 0.25s ease;
  pointer-events: none;
}
.switch input:checked ~ .track { background: var(--accent-soft); border-color: var(--accent); }
.switch input:checked ~ .knob { transform: translateX(20px); background: var(--accent); }
.switch input:focus-visible ~ .track { box-shadow: 0 0 0 3px rgba(12, 210, 157, 0.22); }
.switch input:disabled { cursor: not-allowed; }
.switch input:disabled ~ .track { opacity: 0.4; }
.switch input:disabled ~ .knob { opacity: 0.4; }

/* Where to actually go, on the "not on the team" gate. Replaced the old
   owner-bootstrap block, which printed the visitor's Discord id. */
.staff-elsewhere {
  margin-top: 14px;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 17px 19px;
}
.staff-elsewhere p {
  margin: 0 0 14px;
  font-size: 0.88rem; line-height: 1.6; color: var(--muted);
}

/* ---------- Triage: toolbar ---------- */

.staff-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: 20px;
}
.staff-toolbar .search-wrap { flex: 1; min-width: 200px; }
.staff-toolbar .select, .staff-toolbar .input { padding: 9px 14px; font-size: 0.86rem; }
/* The shorthand above resets the insets that leave room for the dropdown
   arrow and the search icon, so both have to be restated at this
   specificity or the glyph sits on top of the text. */
.staff-toolbar .select { padding-right: 34px; }
.staff-toolbar .search-wrap .input { padding-left: 40px; }

/* Small pill filters (status chips) */
.chip-filters { display: flex; flex-wrap: wrap; gap: 7px; }
.chip-filters button {
  background: var(--panel-2); border: 1px solid var(--line-soft);
  color: var(--muted); border-radius: 999px; padding: 6px 14px;
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 600; cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.chip-filters button:hover { color: var(--ink-dim); border-color: var(--line); }
.chip-filters button.on {
  background: rgba(255, 255, 255, 0.10); border-color: #fff; color: #fff;
}

/* ---------- Triage: severity ---------- */

.sev {
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.sev-critical { color: #ff5c6c; background: rgba(255, 92, 108, 0.10); }
.sev-quick_fix { color: #0cd29f; background: rgba(12, 210, 159, 0.10); }
.sev-high     { color: #ff9f45; background: rgba(255, 159, 69, 0.10); }
.sev-medium   { color: #6cc6ff; background: rgba(108, 198, 255, 0.10); }
.sev-low      { color: var(--muted-2); background: rgba(255, 255, 255, 0.04); }

/* A manual override is worth seeing at a glance. */
.sev-manual {
  display: inline-flex; align-items: center; gap: 5px;
}
.sev-manual::after {
  content: "";
  width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; opacity: 0.85;
}

.dup-count {
  font-size: 0.68rem; font-weight: 700;
  padding: 3px 9px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line-soft);
  color: var(--ink-dim);
}

.mod-tag {
  font-size: 0.68rem; font-weight: 600;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(92, 184, 255, 0.09);
  border: 1px solid rgba(92, 184, 255, 0.28);
  color: #5cb8ff;
}
.mod-tag.none {
  background: rgba(255, 255, 255, 0.03);
  border-color: var(--line-soft); color: var(--muted-2);
  border-style: dashed;
}

/* Left stripe reflects severity so a wall of bugs is scannable. */
.report-card.sv-critical::before { background: #ff5c6c; }
.report-card.sv-quick_fix::before { background: #0cd29f; }
.report-card.sv-high::before     { background: #ff9f45; }
.report-card.sv-medium::before   { background: #6cc6ff; }
.report-card.sv-low::before      { background: var(--muted-2); opacity: 0.5; }
.report-card.pinned { border-color: var(--line); }

.ticket-title {
  font-family: var(--font-display);
  font-size: 1.02rem; font-weight: 700; color: var(--ink);
  line-height: 1.35; word-break: break-word;
  margin-bottom: 6px;
}
.ticket-body {
  color: var(--muted); font-size: 0.88rem; line-height: 1.55;
  max-height: 3.2em; overflow: hidden;
  margin-top: 8px;
}
.ticket-note {
  margin-top: 12px;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-left: 2px solid var(--line-accent);
  border-radius: var(--radius-sm);
  padding: 9px 13px;
  color: var(--ink-dim); font-size: 0.84rem;
}

/* Inline selects inside a ticket's action row */
.ticket-controls {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
}
.ticket-controls .select {
  padding: 7px 30px 7px 12px; font-size: 0.8rem;
  background-color: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink-dim);
  font-family: var(--font-body);
}
.ticket-controls .select:hover { border-color: var(--line); color: var(--ink); }

/* ---------- Triage: duplicate suggestions ---------- */

.dup-suggest {
  margin-top: 12px;
  border: 1px dashed var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  background: rgba(255, 255, 255, 0.012);
}
.dup-suggest-head {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 9px;
}
.dup-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 0.85rem; color: var(--ink-dim);
}
.dup-row:first-of-type { border-top: none; }
.dup-row .dup-sim {
  font-size: 0.7rem; font-weight: 700; color: var(--muted-2);
  background: rgba(255, 255, 255, 0.05);
  border-radius: 999px; padding: 2px 8px; flex: none;
}
.dup-row .dup-name { flex: 1; min-width: 0; word-break: break-word; }

/* ---------- Triage: status pills ---------- */

.status-pill {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line-soft);
  color: var(--muted);
  white-space: nowrap;
}
.status-pill.done     { color: var(--accent); background: var(--accent-soft); border-color: var(--line-accent); }
.status-pill.progress { color: #5cb8ff; background: rgba(92, 184, 255, 0.10); border-color: rgba(92, 184, 255, 0.3); }
.status-pill.dead     { color: var(--muted-2); border-style: dashed; }

/* ---------- Skeletons ---------- */

.report-skeleton {
  height: 168px;
  border-radius: var(--radius);
  background: linear-gradient(100deg, var(--panel) 40%, var(--panel-3) 50%, var(--panel) 60%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
  border: 1px solid var(--line-soft);
}

/* ---------- Designed empty state ---------- */

.staff-empty {
  text-align: center;
  padding: 66px 24px;
  border: 1px dashed var(--line-soft);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.012);
  animation: card-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.staff-empty .se-ring {
  width: 62px; height: 62px; margin: 0 auto 18px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--line-accent);
  color: var(--accent);
}
.staff-empty .se-ring svg { width: 26px; height: 26px; }
.staff-empty h3 { font-size: 1.05rem; color: var(--ink-dim); margin-bottom: 6px; }
.staff-empty p { font-size: 0.9rem; color: var(--muted-2); margin: 0; }

/* ---------- Responsive ---------- */

@media (max-width: 720px) {
  .staff-stats { grid-template-columns: 1fr; gap: 10px; }
  .stat-tile { padding: 15px 17px; }
  .seg-wrap { justify-content: flex-start; }
  .report-card { padding: 16px 16px 14px 20px; }
  .rep-thumb { width: 64px; height: 44px; }
  .report-actions .ra-right { margin-left: 0; width: 100%; }
  .report-actions .ra-left { width: 100%; }
  .report-actions .btn { flex: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .report-card, .staff-empty { animation: none; }
  .report-card.leaving { transition: opacity 0.2s ease; }
  .seg::before { transition: none; }
}


/* ============================================================
   FLOATING DISCORD BUTTON
   Lifted verbatim from wingsisup.com so both sites carry the same
   affordance in the same place. The brand green there is already
   #0cd29d, so it matches this site's accent with no adjustment.
   ============================================================ */

.fab-wrap {
  position: fixed;
  right: 20px; bottom: 20px;
  padding-top: 15px;
  margin-bottom: 0;
  z-index: 997;
  box-shadow: rgba(0, 0, 0, 0.4) 0 16px 15px -10px, rgba(0, 0, 0, 0.5) 0 8px 5px -10px;
  border-radius: 100%;
}
.fab-wrap .fab {
  display: inline-block;
  color: #fff;
  position: relative;
  overflow: hidden;
  z-index: 1;
  cursor: pointer;
  vertical-align: middle;
  width: 56px; height: 56px;
  padding: 0;
  line-height: 54px;
  font-size: 15px;
  border-radius: 100%;
  background-color: #0cd29d;
  transition: background-color 0.3s;
}
.fab-wrap .fab img { width: 56px; height: 56px; display: block; }


/* ============================================================
   GUIDES GALLERY
   Each card is a fake player: a high-resolution still with a play
   button, swapped for the real YouTube embed only on click. Styling
   the still like a player is what keeps the page sharp - YouTube's
   own thumbnails top out near 720p and arrive re-compressed.
   ============================================================ */

/* Alternating full-width rows: still on one side, copy on the other,
   flipping each time. The flip is driven by :nth-child so the markup is
   identical for every row and the order can never drift from the
   layout. Hairlines between rows instead of boxes - these are editorial
   spreads, not cards. */

.guide-rows { display: flex; flex-direction: column; }

.guide-row {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: center;
  gap: 52px;
  padding: 58px 0;
  border-top: 1px solid var(--line-soft);
}
.guide-row:first-child { border-top: none; padding-top: 8px; }

/* Every second row swaps sides. Explicit column placement rather than
   direction tricks, so the reading order in the DOM stays media-then-
   copy for screen readers however it is painted. */
.guide-row:nth-child(even) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
}
.guide-row:nth-child(even) .guide-media { grid-column: 2; grid-row: 1; }
.guide-row:nth-child(even) .guide-copy  { grid-column: 1; grid-row: 1; }

.guide-media { position: relative; min-width: 0; }
/* The still fills a true 16:9 box, so a 1920x1080 export lands
   pixel-for-pixel on a wide display with no letterboxing. */
/* No drop shadow. A wide soft black shadow on a near-black page has
   almost no contrast to work with, so the falloff quantises into
   visible bands - it read as dithering rather than depth. The border
   alone separates the still from the page. */
.guide-media .video-thumb {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-soft);
  transition: border-color 0.3s ease;
}
.guide-media .video-thumb img { opacity: 1; }
/* Hover effects belong to the still itself. Driving them from the whole
   row meant moving the cursor anywhere near the text zoomed a picture
   on the other side of the screen. */
.guide-media .video-thumb:hover { border-color: var(--line); }
/* Zoom comes from the base .video-thumb:hover rule - scoped to the
   still, not the row. */

.guide-copy { min-width: 0; }
/* Neutral, not accent: the episode name below owns the accent now, and
   two green things stacked would fight each other. */
.guide-num {
  display: block;
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: 12px;
}
/* Series on top, episode below in accent - the same split the artwork
   uses, so the spread and its still read as one piece. */
.guide-copy h3 {
  display: flex; flex-direction: column; gap: 3px;
  font-family: var(--font-display);
  letter-spacing: -0.02em; line-height: 1.15;
  margin: 0 0 14px;
}
.guide-copy h3 .gt-series {
  font-size: clamp(0.95rem, 1.4vw, 1.15rem); font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.guide-copy h3 .gt-name {
  font-size: clamp(1.5rem, 2.8vw, 2.15rem); font-weight: 700;
  color: var(--accent);
}
.guide-copy p {
  color: var(--muted); font-size: 1rem; line-height: 1.7;
  margin: 0 0 20px; max-width: 42ch;
}
/* A real button so it is focusable and keyboard-operable, styled as
   text - it plays the video, it is not a second call to action. */
.guide-cue {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 0; border: none; background: none; cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.84rem; font-weight: 600; color: var(--muted-2);
  transition: color 0.2s ease;
}
.guide-cue svg { width: 11px; height: 11px; }
.guide-cue:hover { color: var(--accent); }
.guide-cue:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

@media (max-width: 820px) {
  /* Stacked: the still leads, copy underneath, same order every time.
     Alternating sides means nothing in one column. */
  .guide-row,
  .guide-row:nth-child(even) { grid-template-columns: minmax(0, 1fr); gap: 22px; padding: 40px 0; }
  .guide-row:nth-child(even) .guide-media { grid-column: 1; grid-row: 1; }
  .guide-row:nth-child(even) .guide-copy  { grid-column: 1; grid-row: 2; }
  .guide-copy p { max-width: none; }
}


/* ============================================================
   DOWNLOAD PAGE
   Three routes, equal weight, with the two that self-update marked so
   the choice is obvious without hiding the manual option.
   ============================================================ */

.dl-grid { margin-bottom: 76px; }

.dl-card .gcard-in { position: relative; padding-top: 30px; }
.dl-card .dl-icon { width: 42px; height: 42px; margin-bottom: 16px; }
.dl-card .dl-icon svg { width: 20px; height: 20px; }
.dl-card h3 { font-size: 1.12rem; margin-bottom: 9px; }
.dl-card p { flex: 1; margin-bottom: 18px; }

/* Neutral by default; the accent is earned by the recommended routes. */
.dl-card .dl-icon {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--line-soft);
  color: var(--muted);
}
.dl-card.featured .dl-icon {
  background: var(--accent-soft);
  border-color: var(--line-accent);
  color: var(--accent);
}

.dl-badge {
  position: absolute; top: 0; right: 0;
  font-size: 0.62rem; font-weight: 800;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 4px 12px;
  border-bottom-left-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--muted-2);
}
.dl-card.featured .dl-badge { background: var(--accent-soft); color: var(--accent); }

.dl-cta {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.9rem;
  color: var(--ink-dim);
  margin-top: auto;
}
.dl-cta svg { width: 15px; height: 15px; transition: transform 0.25s ease; }
.dl-card:hover .dl-cta { color: var(--ink); }
.dl-card:hover .dl-cta svg { transform: translateX(4px); }
/* The unavailable card is a statement, not a button. */
.dl-card:not(.featured):not([href]) .dl-cta { color: var(--muted-2); }
.dl-card:not([href]) { cursor: default; }
.dl-card:not([href]):hover { transform: none; box-shadow: none; }

/* ---- What happens next ---- */

.dl-steps { max-width: 720px; }
.dl-list {
  list-style: none; counter-reset: dl; padding: 0; margin: 32px 0 0;
  display: grid; gap: 20px;
}
.dl-list li {
  counter-increment: dl;
  position: relative; padding-left: 46px;
  color: var(--muted); font-size: 0.97rem; line-height: 1.65;
}
.dl-list li::before {
  content: counter(dl);
  position: absolute; left: 0; top: -2px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel-2);
  font-family: var(--font-display); font-weight: 700; font-size: 0.82rem;
  color: var(--ink-dim);
}
.dl-list li b { color: var(--ink); font-weight: 600; }
.dl-list code {
  font-family: var(--font-mono); font-size: 0.85em;
  background: var(--bg-2); border: 1px solid var(--line-soft);
  border-radius: 5px; padding: 1px 6px; color: var(--ink-dim);
  word-break: break-all;
}
.dl-after { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 34px; }

@media (max-width: 560px) {
  .dl-after .btn { width: 100%; }
}


/* ============================================================
   PAGE NOT FOUND
   404.html: the inner-page hero, then the usual places to go. The
   cards' icons stay neutral and take the accent on hover.
   ============================================================ */

.nf-hero .btn { margin-top: 26px; }

.nf-card .g-icon {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--line-soft);
  color: var(--muted);
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}
.nf-card:hover .g-icon {
  background: var(--accent-soft);
  border-color: var(--line-accent);
  color: var(--accent);
}


/* ============================================================
   LEGAL PAGES (privacy)
   Narrow measure and generous leading - this is prose to be read,
   not scanned, so it gets none of the card treatment.
   ============================================================ */

.legal { max-width: 720px; }
.legal-lede {
  font-size: 1.05rem; line-height: 1.75; color: var(--ink-dim);
  padding-bottom: 26px; margin-bottom: 8px;
  border-bottom: 1px solid var(--line-soft);
}
.legal h2 {
  font-size: 1.24rem; font-weight: 600;
  margin: 42px 0 12px;
  letter-spacing: -0.015em;
}
.legal h3 {
  font-size: 1rem; font-weight: 600; color: var(--ink-dim);
  margin: 26px 0 8px;
}
.legal p {
  color: var(--muted); font-size: 0.97rem; line-height: 1.75;
  margin: 0 0 14px;
}
.legal p b, .legal li b { color: var(--ink-dim); font-weight: 600; }
.legal a { border-bottom: 1px solid var(--line-accent); }
.legal a:hover { border-bottom-color: var(--accent); }

.legal-list { list-style: none; padding: 0; margin: 0 0 14px; }
.legal-list li {
  position: relative; padding-left: 22px;
  color: var(--muted); font-size: 0.97rem; line-height: 1.75;
  margin-bottom: 9px;
}
.legal-list li::before {
  content: ""; position: absolute; left: 4px; top: 0.72em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--line-accent);
}


/* ============================================================
   TRIAGE v2 - clickable stat tiles, status icons and the roles
   in the team list.
   ============================================================ */

/* ---------- stat tiles that filter ---------- */

button.stat-tile {
  font: inherit; text-align: left; cursor: pointer; width: 100%;
  color: inherit;
}
.stat-tile.tappable::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 2px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 0.25s ease;
}
.stat-tile.tappable.on { border-color: var(--line-accent); }
.stat-tile.tappable.on::after { transform: scaleX(1); }
.stat-tile.tappable.on .st-icon {
  color: var(--accent);
  border-color: var(--line-accent);
  background: var(--accent-soft);
}
.stat-tile.tappable.on .st-label { color: var(--ink-dim); }

/* ---------- status pills with a glyph ---------- */

.status-pill {
  display: inline-flex; align-items: center; gap: 5px;
}
.status-pill svg { width: 12px; height: 12px; flex: none; }
/* Actively being worked on. Distinct from "progress" on purpose: this is
   the one a second dev must not pick up, so it gets its own colour. */
.status-pill.wip {
  color: var(--warn); background: rgba(255, 207, 92, 0.10);
  border-color: rgba(255, 207, 92, 0.30);
}

/* ---------- team: a switch per role on each row ---------- */

.ur-switches { display: flex; align-items: flex-end; gap: 16px; flex: none; }
.sw-cell { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.sw-cap {
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted-2);
}
.user-row.is-editor { border-color: var(--line-accent); }
.user-row.is-editor .ur-sub { color: var(--accent); }

@media (max-width: 620px) {
  .ur-switches { gap: 11px; }
  .sw-cap { display: none; }
}

/* ---------- sort control ---------- */

.sort-wrap {
  display: inline-flex; align-items: center; gap: 8px;
  /* Pushed to the far end so sort never sits among the filters. Harmless
     next to the bug search box, which is the flexible element there. */
  margin-left: auto;
  flex: none;
}
.sort-cap {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted-2); white-space: nowrap;
}
@media (max-width: 720px) {
  .sort-wrap { margin-left: 0; width: 100%; }
  .sort-wrap .select { flex: 1; }
}


/* Three tiles read better with the label a hair off the icon. */
.stat-tile > span:not(.st-icon) { padding-left: 3px; }


/* ============================================================
   VIEW AS (owner only)

   A preview of the interface at a lower tier. Never a security
   boundary - row level security still answers to who you really are -
   so the banner says so plainly rather than implying otherwise.
   ============================================================ */

.auth-menu .menu-label {
  padding: 9px 14px 5px;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--muted-2);
}
.auth-menu .menu-view {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
}
.menu-view .mv-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  border: 1px solid var(--line-strong);
  transition: background 0.18s ease, border-color 0.18s ease;
}
.menu-view.on { color: var(--accent); }
.menu-view.on .mv-dot { background: var(--accent); border-color: var(--accent); }

/* Bottom centre: out of the way of the fixed nav, and impossible to
   mistake for part of the page you are previewing. */
.view-bar {
  position: fixed; z-index: 120;
  left: 50%; bottom: 22px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  max-width: min(560px, calc(100vw - 32px));
  padding: 11px 12px 11px 16px;
  border-radius: 999px;
  background: rgba(20, 20, 22, 0.92);
  border: 1px solid var(--line-accent);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(10px);
  animation: vbIn 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes vbIn {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.view-bar > svg { width: 17px; height: 17px; flex: none; color: var(--accent); }
.view-bar span {
  flex: 1; min-width: 0;
  font-size: 0.8rem; line-height: 1.4; color: var(--muted);
}
.view-bar span b { color: var(--ink); font-weight: 600; }
.view-bar button {
  flex: none; cursor: pointer; font: inherit;
  font-size: 0.76rem; font-weight: 600;
  padding: 7px 13px; border-radius: 999px;
  color: var(--ink-dim);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line-soft);
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.view-bar button:hover {
  color: var(--ink); border-color: var(--line);
  background: rgba(255, 255, 255, 0.1);
}

@media (max-width: 620px) {
  .view-bar { bottom: 16px; padding: 10px 10px 10px 13px; gap: 9px; }
  .view-bar span { font-size: 0.74rem; }
}

/* Reserve the account button's height so the nav cannot change height
   between the cached paint and the real one. */
#authArea { display: flex; align-items: center; min-height: 42px; }
/* A switch that is on because a higher role implies it, not because it
   was set. Reads as active but clearly not yours to flip. */
.switch.implied { opacity: 0.5; cursor: default; }
/* The Critical tile is the one thing on the page that should never read
   as "all good", so it takes the danger colour instead of the accent -
   both when it has a count and when it is the active filter. */
.stat-tile[data-quick="critical"].live { border-color: rgba(255, 92, 108, 0.34); }
.stat-tile[data-quick="critical"].live .st-icon {
  background: rgba(255, 92, 108, 0.10);
  border-color: rgba(255, 92, 108, 0.34);
  color: var(--danger);
}
.stat-tile[data-quick="critical"].live .st-num { color: var(--danger); }
/* No ::after glow override here: on a tappable tile that pseudo-element
   is the active-filter underline, and .live's radial glow never survives
   it anyway. The underline colour is set below. */
.stat-tile[data-quick="critical"].on { border-color: rgba(255, 92, 108, 0.45); }
.stat-tile[data-quick="critical"].tappable::after { background: var(--danger); }
.stat-tile[data-quick="critical"].on .st-icon {
  color: var(--danger);
  border-color: rgba(255, 92, 108, 0.45);
  background: rgba(255, 92, 108, 0.12);
}
/* The number and its label sit flush against each other (no whitespace
   between the spans in the markup), so the gap has to come from here. */
.stat-tile .st-label { margin-left: 4px; }

/* ============================================================
   TEAM: NEW USERS (endless list)
   ============================================================ */

.new-users { display: block; }

/* Promoted rows collapse out of this list and reappear under Current
   team. Mirrors .report-card.leaving rather than inventing a second
   disappearing animation. */
.user-row.leaving {
  animation: none;
  overflow: hidden;
  opacity: 0;
  transform: translateX(24px);
  max-height: 0 !important;
  margin-bottom: -10px;
  padding-top: 0; padding-bottom: 0;
  transition: opacity 0.22s ease, transform 0.22s ease, max-height 0.26s ease,
              margin 0.26s ease, padding 0.26s ease;
}

.nu-foot { min-height: 8px; }
.nu-sentinel { height: 1px; }

.nu-loading {
  display: flex; justify-content: center; gap: 6px; padding: 18px 0;
}
.nu-loading span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--muted-2);
  animation: nuBounce 1s infinite ease-in-out both;
}
.nu-loading span:nth-child(2) { animation-delay: 0.14s; }
.nu-loading span:nth-child(3) { animation-delay: 0.28s; }
@keyframes nuBounce {
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
  40%           { opacity: 1;    transform: translateY(-4px); }
}

.nu-end, .nu-none {
  text-align: center; padding: 16px 0;
  font-size: 0.8rem; color: var(--muted-2);
}
.nu-none {
  border: 1px dashed var(--line-soft); border-radius: var(--radius);
  padding: 22px 16px;
}

@media (prefers-reduced-motion: reduce) {
  .user-row.leaving { transition: opacity 0.2s ease; }
  .nu-loading span { animation: none; opacity: 0.5; }
}
/* ============================================================
   SOLID BUTTON FILLS

   Every button gets an opaque background. The translucent rgba fills
   used before took their colour from whatever was behind them, so the
   same button looked different on a card, on a panel and on the page -
   and on the darkest surfaces it read as no button at all.

   These are the previous tints flattened against the panel surfaces, so
   the weight is unchanged; only the see-through is gone. Text-style
   controls (comment actions, segmented-control items, dropdown menu
   rows) are deliberately not included - they are text, not buttons.
   ============================================================ */

.btn-quiet          { background: #232326; }
.btn-quiet:hover    { background: #2c2c30; }

.btn-ghost          { background: #14211d; }
.btn-ghost:hover    { background: #1b3129; }

.btn-danger         { background: #2a1a1c; }
.btn-danger:hover   { background: #3c2226; }

.chip-filters button.on { background: #2f2f33; }

.nav-burger         { background: #1e1e21; }

.view-bar button        { background: #2a2a2e; }
.view-bar button:hover  { background: #35353a; }

/* Weakest first, so the active state wins on a tile that is both lit
   and selected. Same specificity within each pair - order decides. */
.stat-tile .st-icon { background: #232326; }
.stat-tile.live .st-icon { background: #16302a; }
.stat-tile[data-quick="critical"].live .st-icon { background: #2c181b; }
.stat-tile.tappable.on .st-icon { background: #16302a; }
.stat-tile[data-quick="critical"].on .st-icon { background: #33191d; }
/* ============================================================
   AUDIT LOG (owner only)
   ============================================================ */

.audit-list { display: block; }

.audit-row {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 16px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  margin-bottom: 8px;
  animation: card-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 28ms);
}
.audit-row:hover { border-color: var(--line); }

.audit-row .au-icon {
  flex: none; width: 30px; height: 30px;
  display: grid; place-items: center; border-radius: 9px;
  background: #232326; border: 1px solid var(--line-soft);
  color: var(--muted-2);
}
.audit-row .au-icon svg { width: 15px; height: 15px; }

.audit-row .au-face {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  border: 1px solid var(--line);
}

.audit-row .au-main { flex: 1; min-width: 0; }
.audit-row .au-line {
  font-size: 0.88rem; color: var(--muted); line-height: 1.45;
}
.audit-row .au-line b { color: var(--ink-dim); font-weight: 600; }
.audit-row .au-who { color: var(--ink); font-weight: 600; }

.audit-row .au-sub {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 3px; font-size: 0.78rem; color: var(--muted-2);
  min-width: 0;
}
.audit-row .au-sub a {
  color: var(--muted-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 46ch;
}
.audit-row .au-sub a:hover { color: var(--accent); }
.audit-row .au-kind {
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted-2);
  border: 1px solid var(--line-soft); border-radius: 999px;
  padding: 1px 7px; flex: none;
}
.audit-row time { flex: none; font-size: 0.76rem; color: var(--muted-2); }

@media (max-width: 640px) {
  .audit-row { flex-wrap: wrap; gap: 10px; }
  .audit-row .au-main { flex: 1 1 140px; }
  .audit-row time { flex: 1 0 100%; text-align: right; }
}
/* STATUS DROPDOWN TINTS - moved.

   These targeted the native <select>, which is now hidden behind the
   custom control and paints nothing. The live rules are on
   .cselect.field .cs-trigger, near the end of this file. */
/* ============================================================
   JUMP TO DISCORD

   Replaced the "Discord" text button. The brand mark carries the
   meaning on its own, and moving it out of the actions row leaves that
   row as nothing but settings.
   ============================================================ */

/* Time and mark stacked at the far end of the card head. */
.report-head .rh-end {
  margin-left: auto;
  display: flex; flex-direction: column; align-items: flex-end; gap: 5px;
  flex: none;
}
/* The old rule pushed the bare <time> over; it is the wrapper's job now. */
.report-head .rh-end time { margin-left: 0; }

.go-discord {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 7px;
  color: var(--muted-2);
  background: #212124;
  border: 1px solid var(--line-soft);
  transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}
.go-discord svg { width: 15px; height: 15px; }
.go-discord:hover {
  color: #fff;
  background: #5865f2;
  border-color: #5865f2;
}

/* In the feature row it sits beside the status dropdown. */
.ticket-controls .go-discord { width: 30px; height: 30px; flex: none; }
/* Discord mark: brand colour at rest, full strength on hover. Sitting
   in blurple all the time means it reads as "Discord" without needing
   the hover to explain itself. */
.go-discord {
  width: 30px; height: 30px;
  color: #5865f2;
  opacity: 0.75;
  transition: opacity 0.18s ease, border-color 0.18s ease;
}
.go-discord svg { width: 19px; height: 19px; }
.go-discord:hover {
  opacity: 1;
  color: #5865f2;
  background: #212124;
  border-color: var(--line);
}
/* Nudged down off the timestamp. Margin rather than a relative offset
   so the card head still reserves the space it occupies. */
.report-head .rh-end .go-discord { margin-top: 4px; }
/* Guides page toolbar: count on the left, sort on the right. */
.guides-bar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 4px;
}
.guides-count {
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted-2);
}
/* The first row drops its own top border - the bar already draws one. */
.guides-bar + .guide-rows .guide-row:first-child { padding-top: 44px; }
/* The sort itself is a .cselect, styled at the end of this file. Only
   its caption needs matching to the count beside it. */
.guides-bar .sort-wrap { gap: 7px; }
.guides-bar .sort-cap {
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted-2);
}


/* ============================================================
   CUSTOM SELECT

   A native select paints its option list with the operating system, so
   it can never match the page. This is our own trigger and menu drawn
   over a real <select>, which stays in the DOM for its value, its
   change events and form submission.

   Two looks:
     .field  an input-shaped control (default)
     .bare   text and chevron only
   ============================================================ */

.cselect { position: relative; display: inline-flex; }

/* Present for its value and events; never seen, never focusable. */
.cs-native {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0; pointer-events: none;
  appearance: none; -webkit-appearance: none;
  border: none; padding: 0; margin: 0;
}

.cs-trigger {
  display: inline-flex; align-items: center; justify-content: space-between;
  gap: 9px; width: 100%;
  cursor: pointer;
  font-family: var(--font-body);
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.cs-chev {
  width: 11px; height: 11px; flex: none;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cselect.open .cs-chev { transform: rotate(180deg); }
.cs-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- field look ---- */

.cselect.field .cs-trigger {
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 0.95rem;
  padding: 11px 14px;
}
.cselect.field .cs-trigger:hover { border-color: var(--line); }
.cselect.field.open .cs-trigger {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(12, 210, 157, 0.14);
}
.cselect.field .cs-chev { color: var(--muted); }

/* ---- bare look ---- */

.cselect.bare .cs-trigger {
  background: none; border: none; padding: 0;
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-dim);
  gap: 7px;
}
.cselect.bare .cs-trigger:hover { color: var(--ink); }
.cselect.open.bare .cs-trigger { color: var(--accent); }

.cs-trigger:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* ---- menu ---- */

/* Fixed, and parented to <body>. Cards clip their contents, so an
   absolutely positioned menu was cut off at the card edge; this one
   overlays anything. Position is set in JS from the trigger's rect. */
.cs-menu {
  position: fixed; z-index: 200;
  top: 0; left: 0;
  display: flex; flex-direction: column;
  padding: 5px;
  border-radius: 12px;
  background: var(--panel-3);
  border: 1px solid var(--line);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
  max-height: 320px; overflow-y: auto;
  /* visibility, not opacity alone: a menu at opacity 0 is still in the
     tab order and would swallow focus while invisible. */
  opacity: 0; visibility: hidden;
  transform: translateY(-6px) scale(0.98);
  transform-origin: top right;
  transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
              visibility 0s linear 0.18s;
}
/* .open is set on the menu itself now, not just its wrapper - it is no
   longer a descendant, so the wrapper's class cannot reach it. */
.cs-menu.open {
  opacity: 1; visibility: visible;
  transform: none;
  transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.2, 0.8, 0.2, 1),
              visibility 0s;
}

.cs-menu [data-v] {
  display: flex; align-items: center; gap: 9px;
  width: 100%; text-align: left;
  padding: 6px 12px 6px 9px;
  border: none; background: none; border-radius: 8px; cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.85rem; font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
/* The menu is on <body>, so it cannot be reached through .cselect.bare
   any more. JS copies the variant onto the menu itself. */
.cs-menu.bare [data-v] {
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase;
}
.cs-menu [data-v]:hover,
.cs-menu [data-v]:focus-visible {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
  outline: none;
}
.cs-menu [data-v].on { color: var(--accent); }

.cs-tick {
  width: 12px; height: 12px; flex: none;
  opacity: 0; transition: opacity 0.15s ease;
}
.cs-menu [data-v].on .cs-tick { opacity: 1; }

/* ---- per-context sizing, mirroring what .select used to get ---- */

.form-row .cselect { width: 100%; }

.filter-bar .cselect { flex: none; }

.ticket-controls .cselect .cs-trigger { padding: 7px 12px; font-size: 0.8rem; }
.ticket-controls .cselect .cs-trigger { color: var(--ink-dim); }

.staff-toolbar .cselect .cs-trigger { padding: 9px 14px; font-size: 0.86rem; }

/* Status colour, carried across from the native element by sync().

   Qualified with .cselect.field on purpose: `.cs-trigger.sel-wip` alone
   is (0,2,0) and loses to `.cselect.field .cs-trigger` at (0,3,0), so
   the panel background won and In Progress never went amber. Matching
   that selector's weight and coming after it is what makes it apply. */
.cselect.field .cs-trigger.sel-wip {
  color: var(--warn);
  border-color: rgba(255, 207, 92, 0.34);
  background: #2b2419;
}
.cselect.field .cs-trigger.sel-wip:hover { border-color: rgba(255, 207, 92, 0.55); }

.cselect.field .cs-trigger.sel-done {
  color: var(--accent);
  border-color: var(--line-accent);
  background: #16302a;
}
.cselect.field .cs-trigger.sel-done:hover { border-color: var(--accent); }

/* The chevron takes the tint too, otherwise it stays grey on a coloured
   field and looks like a rendering fault. */
.cs-trigger.sel-wip .cs-chev  { color: var(--warn); }
.cs-trigger.sel-done .cs-chev { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .cs-menu, .cs-chev { transition: none; }
}

/* ============================================================
   SOLID FILLS, ROUND 2

   The first pass covered the .btn family and the staff controls but
   missed the pill-shaped toggles: showcase tabs on the home page and
   the tag picker on the upload form. Their ACTIVE state was a 10% white
   wash, so the same selected tab looked different depending on what sat
   behind it. Flattened against the surfaces they actually sit on.
   ============================================================ */

.show-tab.on        { background: #2f2f33; }
.tag-picker button.on { background: #2f2f33; }

/* Not buttons, but the same see-through problem: small labels whose
   fill came from whatever was behind them. */
.chip               { background: #212124; }
.news-date .cat     { background: #212124; }

/* ============================================================
   HOME: GUIDES SECTION

   Distinct from /guides on purpose. That page runs full-width
   alternating spreads; these are numbered tiles with the episode
   numeral set large and hollow, half over the still and half over the
   section background. Repeating the spread layout here would make the
   home page read as a duplicate of a page one click away.
   ============================================================ */

.guides-sec { position: relative; }

.home-guides {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 26px;
  margin-bottom: 44px;
}

/* A link, not a player: the tile hands off to /guides, which owns the
   video and starts it on arrival. */
.hg-card {
  display: flex; flex-direction: column;
  color: inherit;
  animation: card-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}

/* Padding at the top leaves room for the numeral to hang above the
   still without being clipped. */
.hg-media {
  position: relative; padding-top: 30px;
  border-radius: var(--radius-lg);
}

.hg-num {
  position: absolute; top: 0; left: -18px; z-index: 3;
  font-family: var(--font-display);
  font-size: 3.6rem; font-weight: 800; line-height: 1;
  letter-spacing: -0.04em;
  /* Hollow: the outline reads over both the still and the page behind
     it, which a solid fill would not. */
  color: transparent;
  /* Fixed 50% rather than --line-strong: the numeral sits half over a
     darkened still and half over the page, and the shared token was too
     faint to read against the image. */
  -webkit-text-stroke: 1px rgba(255, 255, 255, 0.4);
  transition: -webkit-text-stroke-color 0.3s ease, transform 0.3s ease;
  pointer-events: none;
  user-select: none;
}
.hg-card:hover .hg-num {
  -webkit-text-stroke-color: var(--accent);
  transform: translateY(-2px);
}

.hg-still {
  display: block; width: 100%; aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-soft);
  transition: border-color 0.3s ease, transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.hg-card:hover .hg-still { border-color: var(--line); transform: scale(1.01); }

/* Darkens left to right so the hollow numeral has something to sit on
   without flattening the whole still. */
.hg-shade {
  position: absolute; inset: 30px 0 0 0;
  border-radius: var(--radius-lg);
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.1));
  pointer-events: none;
  z-index: 1;
}

.hg-play {
  position: absolute; z-index: 2;
  left: 50%; top: calc(50% + 15px); transform: translate(-50%, -50%);
  width: 54px; height: 54px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(12, 210, 157, 0.92);
  color: #04120d;
  box-shadow: 0 0 0 8px rgba(12, 210, 157, 0.09);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  pointer-events: none;
}
.hg-play svg { width: 22px; height: 22px; margin-left: 3px; }
.hg-card:hover .hg-play {
  transform: translate(-50%, -50%) scale(1.08);
  box-shadow: 0 0 0 12px rgba(12, 210, 157, 0.07);
}

.hg-meta { padding: 16px 2px 0; }
.hg-series {
  display: block;
  font-family: var(--font-display);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--muted-2);
  margin-bottom: 7px;
}
.hg-name {
  font-family: var(--font-display);
  font-size: 1.12rem; font-weight: 600; letter-spacing: -0.015em;
  line-height: 1.25; color: var(--accent);
  margin: 0 0 8px;
}
.hg-meta p {
  margin: 0; font-size: 0.88rem; line-height: 1.6; color: var(--muted);
}

@media (max-width: 900px) {
  .home-guides { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
}
@media (max-width: 620px) {
  .home-guides { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .hg-num { font-size: 3rem; }
}

/* Discord mark in a duplicate suggestion: smaller than the card's, and
   pinned beside the confirm button so the pair reads as read-then-act. */
.dup-row .go-discord { width: 26px; height: 26px; flex: none; margin-left: auto; }
.dup-row .go-discord svg { width: 15px; height: 15px; }
/* The name already flexes; without this the mark and the button would
   both be pushed by it and lose their pairing. */
.dup-row .dup-name { flex: 1; min-width: 0; }
.dup-row .go-discord + .btn { margin-left: 0; }

/* The suggestion pills were see-through like the others. */
.dup-row .dup-sim { background: #212124; }
/* A card repainted in place has already arrived once. Replaying the
   entry animation makes a one-field edit look like the list reloaded. */
.report-card.no-anim { animation: none; }

/* ============================================================
   REPORT BODIES

   The helper bot's template rendered as labelled chips plus a
   description, instead of a wall of asterisks and a raw user id.
   ============================================================ */

.report-body { margin-top: 12px; }

/* ---- the short labelled fields ---- */

.rb-fields {
  display: flex; flex-wrap: wrap; gap: 7px;
  margin-bottom: 12px;
}
.rb-field {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px 4px 9px;
  background: #212124;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  font-size: 0.78rem;
  max-width: 100%;
}
.rb-label {
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--muted-2);
  white-space: nowrap;
}
.rb-value {
  color: var(--ink-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- the description ---- */

.rb-text {
  position: relative;
  font-size: 0.9rem; line-height: 1.65; color: var(--muted);
  overflow-wrap: anywhere;
}
.rb-text b { color: var(--ink-dim); font-weight: 600; }
.rb-text a { color: var(--accent); border-bottom: 1px solid var(--line-accent); }
.rb-text a:hover { border-bottom-color: var(--accent); }

.rb-text code {
  font-family: var(--font-mono); font-size: 0.85em;
  background: #212124; border: 1px solid var(--line-soft);
  border-radius: 5px; padding: 1px 5px;
  color: var(--ink-dim);
}
/* Pasted Lua errors are the main reason this exists: they must keep
   their line breaks and scroll sideways rather than wrapping into
   nonsense. */
.rb-pre {
  font-family: var(--font-mono); font-size: 0.8rem; line-height: 1.55;
  background: var(--bg-1); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 11px 13px; margin: 10px 0;
  color: var(--ink-dim);
  white-space: pre; overflow-x: auto;
}

.rb-mention {
  display: inline-block;
  padding: 0 6px; border-radius: 5px;
  background: rgba(88, 101, 242, 0.15);
  border: 1px solid rgba(88, 101, 242, 0.35);
  color: #9aa5f5; font-weight: 600; font-size: 0.85em;
}

/* ---- clamp + toggle ---- */

/* Roughly five lines. line-clamp rather than a fixed height so it lands
   on a line boundary at any font size. */
.rb-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* A clamped code block would be cut mid-line and look broken, so the
   clamp only applies to the prose around it. */
.rb-clamp .rb-pre { display: none; }
.rb-text.open { display: block; }

.rb-more {
  margin-top: 9px; padding: 0;
  background: none; border: none; cursor: pointer;
  font: inherit; font-size: 0.8rem; font-weight: 600;
  color: var(--muted-2);
  transition: color 0.18s ease;
}
.rb-more:hover { color: var(--accent); }

/* Prose blocks below the chips. Description leads unlabelled; anything
   else carries a quiet label so two paragraphs are not read as one. */
.rb-para { margin: 0 0 10px; }
.rb-para:last-child { margin-bottom: 0; }
/* A real subtitle rather than a faint kicker - these two blocks carry
   different information and the reader needs to know which is which at
   a glance. */
.rb-sub {
  display: block;
  font-family: var(--font-display);
  font-size: 0.82rem; font-weight: 700; letter-spacing: -0.005em;
  color: var(--ink-dim);
  margin-bottom: 4px;
}

/* ============================================================
   PATCH NOTES

   One block per release, three sections inside it. The content is
   generated from the addon's own What's New data, so the styling is
   the only thing that lives here.
   ============================================================ */

.pn-release {
  padding: 30px 0 34px;
  border-top: 1px solid var(--line-soft);
}
.pn-release:first-child { border-top: none; padding-top: 6px; }

/* ---- release header ----
   The header is the release's open / close button: version, date and the
   Latest tag, with a chevron at the far end that turns to point right
   while the release is closed. */

.pn-head { margin-bottom: 22px; transition: margin-bottom 0.25s ease; }
.pn-toggle {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  width: 100%; margin: 0; padding: 0;
  background: none; border: 0; color: inherit; font: inherit; text-align: left;
  cursor: pointer;
}
.pn-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: 6px; }
.pn-chev {
  align-self: center; flex: none; margin-left: auto;
  width: 18px; height: 18px; color: var(--muted-2);
  transition: transform 0.2s ease, color 0.15s ease;
}
.pn-toggle:hover .pn-chev { color: var(--ink); }
.pn-release.is-collapsed .pn-chev { transform: rotate(-90deg); }
.pn-release.is-collapsed .pn-head { margin-bottom: 0; }
.pn-release.is-collapsed { padding-bottom: 30px; }

/* The release's sections fold shut: one grid row from 1fr to 0fr. */
.pn-rel-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.25s ease, opacity 0.2s ease; }
.pn-rel-inner { min-height: 0; overflow: hidden; }
.pn-release.is-collapsed .pn-rel-body { grid-template-rows: 0fr; opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .pn-head, .pn-chev, .pn-rel-body { transition: none; }
}
.pn-ver {
  font-family: var(--font-display);
  font-size: 1.7rem; font-weight: 800; letter-spacing: -0.03em;
  color: var(--ink); line-height: 1;
}
.pn-head time { font-size: 0.84rem; color: var(--muted-2); }
.pn-latest {
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid var(--line-accent);
  padding: 3px 9px; border-radius: 999px;
}

/* ---- sections ---- */

.pn-sec { margin-bottom: 22px; }
.pn-sec:last-child { margin-bottom: 0; }
.pn-sec h3 {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted-2);
  margin: 0 0 12px;
}
.pn-sec-icon {
  display: grid; place-items: center;
  width: 20px; height: 20px; flex: none;
}
.pn-sec-icon svg { width: 13px; height: 13px; }
/* Main features earn the accent; the other two stay quiet so the eye
   lands on what actually changed this release. */
.pn-main h3 { color: var(--accent); }

/* ---- entries ---- */

.pn-list { list-style: none; margin: 0; padding: 0; }
.pn-list > li {
  display: flex; gap: 12px;
  padding: 9px 0;
  border-top: 1px solid var(--line-soft);
}
.pn-list > li:first-child { border-top: none; }

/* Fixed-width module column so the text edges line up down the page
   and the module names read as a scannable index. */
.pn-mod {
  flex: none; width: 132px;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted-2);
  padding-top: 3px;
}
/* "WoW Forever" line above the module name on a Forever-only entry: the
   Forever theme's bronze, the same marker the in-game What's New shows. */
.pn-mod .pn-forever { display: block; color: #dca77f; }
.pn-body { flex: 1; min-width: 0; }

.pn-title {
  display: block;
  font-family: var(--font-display);
  font-size: 0.98rem; font-weight: 600; color: var(--ink);
  letter-spacing: -0.01em;
}
.pn-desc {
  display: block; margin-top: 3px;
  font-size: 0.89rem; line-height: 1.6; color: var(--muted);
}

/* Main features get a little more presence than the small ones. */
.pn-hero > li { padding: 13px 0; }
.pn-hero .pn-title { font-size: 1.12rem; font-weight: 700; color: var(--accent); }
.pn-hero .pn-desc { font-size: 0.94rem; color: var(--ink-dim); }

/* Feature spotlight: a hero with a spotlight page leaves the Main Features
   list for one wide card above it, a link to that page. Its key screenshot
   fades in from the right behind the text; a phone gets the plain card. */
.pn-spot {
  position: relative; display: flex; align-items: center;
  margin: 2px 0 28px; padding: 26px 28px; min-height: 164px;
  border: 1px solid var(--line-soft); border-radius: 14px;
  background: var(--bg-1); color: var(--ink); text-decoration: none;
  overflow: hidden;
  transition: border-color 0.25s ease, transform 0.25s ease, box-shadow 0.3s ease;
}
.pn-spot:hover {
  border-color: var(--line); color: var(--ink);
  transform: translateY(-2px);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);
}
.pn-spot-art {
  position: absolute; top: 0; right: 0; bottom: 0; left: 40%;
  background-size: cover; background-position: center;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 48%);
  mask-image: linear-gradient(90deg, transparent, #000 48%);
  opacity: 0.5;
  transition: opacity 0.3s ease;
}
.pn-spot:hover .pn-spot-art { opacity: 0.66; }
.pn-spot-body {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: flex-start;
  max-width: 58%;
}
.pn-spot-eyebrow {
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 9px;
}
.pn-spot-title {
  display: block;
  font-family: var(--font-display);
  font-size: 1.38rem; font-weight: 700; line-height: 1.2;
  letter-spacing: -0.015em; color: var(--ink);
}
.pn-spot-desc {
  display: block; margin-top: 7px;
  font-size: 0.94rem; line-height: 1.6; color: var(--ink-dim);
}
.pn-spot-cta {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 15px;
  font-size: 0.86rem; font-weight: 600; color: var(--accent);
}
.pn-spot-cta svg { width: 15px; height: 15px; transition: transform 0.2s ease; }
.pn-spot:hover .pn-spot-cta svg { transform: translateX(3px); }
@media (max-width: 640px) {
  /* The text fills the card on a phone, so the screenshot would only sit
     behind it: a plain card there. */
  .pn-spot { padding: 20px 20px 22px; min-height: 0; }
  .pn-spot-art { display: none; }
  .pn-spot-body { max-width: none; }
  .pn-spot-title { font-size: 1.2rem; }
}
@media (prefers-reduced-motion: reduce) {
  .pn-spot, .pn-spot-art, .pn-spot-cta svg { transition: none; }
  .pn-spot:hover { transform: none; }
  .pn-spot:hover .pn-spot-cta svg { transform: none; }
}

/* A module with several fixes gets one row, its fixes stacked - far
   easier to scan than twenty rows repeating "Raid Frames". */
.pn-fix {
  display: block;
  font-size: 0.89rem; line-height: 1.6; color: var(--muted);
}
.pn-fix + .pn-fix { margin-top: 7px; }

/* The closing thank-you of a release; "contributors" links to the
   Legends contributors on the support page. */
.pn-thanks { margin: 22px 0 0; font-size: 0.92rem; color: var(--ink-dim); }
.pn-thanks a { font-weight: 600; }

/* ---- footer ---- */

.pn-more { text-align: center; padding: 32px 0 8px; }
.pn-end { font-size: 0.84rem; color: var(--muted-2); margin: 0; }

@media (max-width: 640px) {
  .pn-list > li { flex-direction: column; gap: 4px; }
  .pn-mod { width: auto; padding-top: 0; }
  .pn-ver { font-size: 1.4rem; }
}

/* ---- home page: latest release teaser ----
   Same data as the patch notes page, same vocabulary, one card. */

.pn-teaser {
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  padding: 24px 26px 20px;
}
.pn-teaser-head {
  display: flex; align-items: baseline; gap: 13px; flex-wrap: wrap;
  padding-bottom: 16px; margin-bottom: 4px;
  border-bottom: 1px solid var(--line-soft);
}
.pn-teaser-head .pn-ver { font-size: 1.35rem; }
.pn-teaser-head time { font-size: 0.82rem; color: var(--muted-2); }

.pn-teaser-item {
  display: flex; gap: 12px;
  padding: 13px 0;
  border-top: 1px solid var(--line-soft);
}
/* Sibling selector, NOT :first-of-type - the header is a div too, so
   "first div" is the header and the first item kept its border, drawing
   a second line a few pixels under the header's own. */
.pn-teaser-head + .pn-teaser-item { border-top: none; }
.pn-teaser-more {
  margin: 14px 0 0; padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.84rem; color: var(--muted-2);
}

@media (max-width: 640px) {
  .pn-teaser-item { flex-direction: column; gap: 4px; }
  .pn-teaser { padding: 20px 18px 16px; }
}

/* ============================================================
   NOTIFICATION BELL  (Feedback Hub, every page)

   Sits in front of the account button for signed-in members and opens
   a small panel beside it. The unread count is a live number, so it
   takes the accent; everything else stays as neutral as the account
   menu next door.
   ============================================================ */

#authArea { gap: 10px; }

.nav-bell-wrap { position: relative; display: flex; }

.nav-bell {
  position: relative;
  width: 42px; height: 42px; flex: none;
  display: grid; place-items: center;
  padding: 0; border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  color: var(--ink-dim);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.nav-bell:hover,
.nav-bell[aria-expanded="true"] { border-color: var(--line-strong); color: var(--ink); }
.nav-bell:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.nav-bell svg { width: 18px; height: 18px; }

/* Cut out of the bell by a ring in the page colour, so it reads as a
   badge on the button rather than a dot floating beside it. */
.nav-bell-count {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px;
  background: var(--accent); color: #04120d;
  font-family: var(--font-display); font-size: 0.66rem; font-weight: 700;
  line-height: 18px; text-align: center;
  box-shadow: 0 0 0 2px var(--bg-0);
}
.nav-bell-count[hidden] { display: none; }

/* ---- the panel ---- */

/* visibility, not display, like the custom select menus: it can fade,
   and while hidden nothing inside it is focusable. */
.bell-panel {
  position: absolute; right: 0; top: calc(100% + 10px);
  z-index: 95;
  width: 360px;
  max-height: min(600px, calc(100vh - var(--nav-h) - 24px));
  display: flex; flex-direction: column;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  opacity: 0; visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
}
.bell-panel.open {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s;
}

/* Fixed height, so the header does not shrink when "Mark all read"
   hides itself. */
.bell-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 54px; padding: 0 16px;
  border-bottom: 1px solid var(--line-soft);
}
/* A focus target for the panel opening, not a control: no ring. Its auto
   margin keeps "Mark all read" and the pings switch together on the right. */
.bell-h {
  margin: 0 auto 0 0; outline: none;
  font-family: var(--font-display);
  font-size: 0.98rem; font-weight: 700; letter-spacing: -0.01em;
  color: var(--ink);
}
.bell-markall,
.bell-retry {
  flex: none; padding: 4px 2px;
  background: none; border: none; cursor: pointer;
  font: inherit; font-size: 0.8rem; font-weight: 600;
  color: var(--muted);
  transition: color 0.15s ease;
}
.bell-markall:hover,
.bell-retry:hover { color: var(--ink); }
.bell-markall:focus-visible,
.bell-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.bell-markall[hidden] { display: none; }

/* "Ping me on Discord" (Discord pings): one row under the heading, its
   icon the size of the rows' and its switch a step smaller than the
   forms', so it sits level with them. On, the icon takes the accent. */
.bell-ping {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line-soft);
}
.bell-ping[hidden] { display: none; }
.bell-ping-ic {
  width: 30px; height: 30px; flex: none;
  display: grid; place-items: center;
  border-radius: 8px;
  background: #232326; border: 1px solid var(--line-soft);
  color: var(--ink-dim);
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}
.bell-ping-ic svg { width: 15px; height: 15px; }
.bell-ping:has(input:checked) .bell-ping-ic {
  background: var(--accent-soft); border-color: var(--line-accent);
  color: var(--accent);
}
.bell-ping-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bell-ping-t { font-size: 0.86rem; font-weight: 600; line-height: 1.35; color: var(--ink); }
.bell-ping-s { font-size: 0.76rem; line-height: 1.4; color: var(--muted); }
.bell-ping-s a {
  color: var(--ink-dim);
  text-decoration: underline; text-underline-offset: 2px;
  transition: color 0.15s ease;
}
.bell-ping-s a:hover { color: var(--ink); }
.bell-ping-s a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.bell-ping-s.is-error { color: var(--danger); }
.switch.bell-ping-sw { width: 40px; height: 22px; }
.switch.bell-ping-sw .knob { top: 3px; left: 3px; width: 16px; height: 16px; }
.switch.bell-ping-sw input:checked ~ .knob { transform: translateX(18px); }

/* Pings on: the switch sits beside the heading with the Discord glyph
   after it, and a tooltip under them says what switching it off does -
   on hover, after a short pause, and on keyboard focus. Past "Mark all
   read", a hairline keeps the two apart. */
/* Its slide-in animation (filled) makes it a layer of its own, so it is
   lifted above the rows below (positioned, later in the page), or they
   would paint over its tooltip. */
.bell-ping-mini {
  position: relative; z-index: 2; flex: none;
  display: flex; align-items: center; gap: 9px;
  animation: bellPingIn 0.22s ease both;
}
.bell-ping-mini[hidden] { display: none; }
.bell-markall:not([hidden]) + .bell-ping-mini { padding-left: 12px; border-left: 1px solid var(--line-soft); }
.bell-ping-mini-ic {
  display: grid; place-items: center;
  color: var(--ink-dim);
  transition: color 0.15s ease;
}
.bell-ping-mini-ic svg { width: 17px; height: 17px; }
.bell-ping-mini:hover .bell-ping-mini-ic { color: var(--ink); }
.bell-tip {
  position: absolute; top: calc(100% + 11px); right: -8px; z-index: 3;
  width: max-content; max-width: 232px;
  padding: 8px 11px;
  background: var(--panel-3);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-1);
  font-size: 0.76rem; font-weight: 500; line-height: 1.45; color: var(--ink-dim);
  text-align: left; white-space: normal;
  pointer-events: none;
  opacity: 0; visibility: hidden; transform: translateY(-3px);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s linear 0.14s;
}
/* The caret points up at the switch and the glyph between them. */
.bell-tip::before {
  content: ""; position: absolute; top: -5px; right: 36px;
  width: 8px; height: 8px;
  background: var(--panel-3);
  border-left: 1px solid var(--line); border-top: 1px solid var(--line);
  transform: rotate(45deg);
}
.bell-ping-mini:hover .bell-tip,
.bell-ping-mini:has(input:focus-visible) .bell-tip {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity 0.14s ease 0.25s, transform 0.14s ease 0.25s, visibility 0s linear 0.25s;
}
.bell-ping-mini:has(input:focus-visible) .bell-tip { transition-delay: 0s; }
@keyframes bellPingIn {
  from { opacity: 0; transform: translateX(6px); }
  to { opacity: 1; transform: none; }
}

.bell-list {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 6px;
}
/* More below: the last visible row fades out rather than ending in a
   half-cut line of text. Off again once scrolled to the end. */
.bell-panel.bell-more .bell-list {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
}

/* ---- rows ---- */

.bell-row {
  position: relative;
  display: flex; align-items: flex-start; gap: 12px;
  width: 100%; min-height: 56px;
  padding: 11px 30px 11px 10px;
  background: none; border: none; border-radius: 9px;
  text-align: left; cursor: pointer;
  font: inherit; color: inherit;
  transition: background 0.15s ease;
}
.bell-row:hover { background: rgba(255, 255, 255, 0.05); color: inherit; }
.bell-row:focus-visible {
  outline: none;
  background: rgba(255, 255, 255, 0.05);
  box-shadow: inset 0 0 0 1px var(--line-accent);
}
/* A row whose link could not be trusted is shown but goes nowhere. */
button.bell-row { cursor: default; }

.bell-ic {
  width: 30px; height: 30px; flex: none;
  display: grid; place-items: center;
  border-radius: 8px;
  background: #232326; border: 1px solid var(--line-soft);
  color: var(--ink-dim);
}
.bell-ic svg { width: 15px; height: 15px; }
/* The team's notice (the writer has a badge): its icon takes the accent. */
.bell-row.is-team .bell-ic {
  background: var(--accent-soft); border-color: var(--line-accent);
  color: var(--accent);
}

.bell-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bell-title {
  font-size: 0.86rem; font-weight: 600; line-height: 1.35;
  color: var(--ink-dim); overflow-wrap: anywhere;
}
.bell-row.is-unread .bell-title { color: var(--ink); }
.bell-body {
  font-size: 0.8rem; line-height: 1.45; color: var(--muted);
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Who wrote it (the rows about something a person wrote) and when, on
   one line: a long name gives way first, never the badge or the time. */
.bell-meta {
  display: flex; align-items: center; gap: 13px;
  min-width: 0; margin-top: 3px;
}
.bell-meta > * { position: relative; }
/* The dot between the byline and the time is drawn, never read aloud. */
.bell-meta > * + *::before {
  content: ""; position: absolute; left: -8px; top: 50%;
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--muted-2); transform: translateY(-50%);
}
.bell-time { flex: none; font-size: 0.72rem; color: var(--muted); }

.bell-by {
  display: inline-flex; align-items: center; gap: 6px;
  min-width: 0; max-width: 100%;
  font-size: 0.74rem; font-weight: 600; color: var(--ink-dim);
}
.bell-av {
  width: 16px; height: 16px; flex: none;
  border-radius: 50%; object-fit: cover;
  background: #212124; border: 1px solid var(--line-soft);
}
.bell-av.is-ghost { display: inline-grid; place-items: center; color: var(--muted); }
.bell-av.is-ghost svg { width: 10px; height: 10px; }
.bell-by-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bell-by-name.is-architect { color: #0cd29d; }
.bell-by-name.is-staff { color: #3fcf6e; }
.bell-by.is-ghost .bell-by-name { color: var(--muted); font-weight: 500; }
/* The Architect's mark beside the name, the EllesmereUI logo, so the
   developer's reply carries the same mark here as in the thread. The
   accent is earned: it is the one sign of the real developer that a
   borrowed name cannot copy. */
.bell-arch { display: inline-flex; flex: none; color: #0cd29d; }
.bell-arch svg { width: 14px; height: 14px; }
/* The hub's comment badge for the rest of the team. */
.bell-badge {
  flex: none;
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  line-height: 1.5; white-space: nowrap;
  padding: 0 6px; border-radius: 999px;
}
.bell-badge.is-dev { color: var(--accent); border: 1px solid var(--line-accent); }
.bell-badge.is-mod { color: var(--ink-dim); border: 1px solid var(--line-strong); }

.bell-dot {
  position: absolute; right: 12px; top: 22px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  opacity: 0; transition: opacity 0.2s ease;
}
.bell-row.is-unread .bell-dot { opacity: 1; }

.bell-foot {
  display: block; flex: none;
  padding: 12px 16px;
  border-top: 1px solid var(--line-soft);
  border-radius: 0 0 var(--radius) var(--radius);
  font-size: 0.84rem; font-weight: 600; text-align: center;
  color: var(--ink-dim);
  transition: color 0.15s ease, background 0.15s ease;
}
.bell-foot:hover { color: var(--ink); background: rgba(255, 255, 255, 0.03); }
.bell-foot:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- loading, error, empty ---- */

.bell-skel { display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 10px; }
.bell-skel i,
.bell-skel b {
  display: block;
  background: linear-gradient(100deg, var(--panel-3) 40%, #2f2f33 50%, var(--panel-3) 60%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
}
.bell-skel i { width: 30px; height: 30px; flex: none; border-radius: 8px; }
.bell-skel span { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.bell-skel b { height: 9px; width: 58%; border-radius: 5px; }
.bell-skel b + b { width: 86%; }

.bell-msg {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 10px;
  font-size: 0.86rem; color: var(--muted);
}

/* Celebratory, so the ring keeps the accent like the hub's other
   "all done" states. */
.bell-empty {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 26px 20px 24px; text-align: center;
}
.bell-ring {
  width: 46px; height: 46px; margin-bottom: 10px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-soft); border: 1px solid var(--line-accent);
  color: var(--accent);
}
.bell-ring svg { width: 20px; height: 20px; }
.bell-empty b {
  font-family: var(--font-display);
  font-size: 0.95rem; font-weight: 700; color: var(--ink);
}
.bell-empty > span:not(.bell-ring) { font-size: 0.82rem; line-height: 1.5; color: var(--muted); max-width: 250px; }

/* Read aloud, never seen ("Unread: ..."). */
.bell-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* ---- header fit ----
   Five links, the Resources button, the bell and the account button
   share one row from 761 px up, so the name gives way first: long names
   end in an ellipsis, and below 1181 px the button shows the avatar
   alone (the name stays its accessible label). Then the spacing
   tightens - never the type - and from 880 px down the account button
   drops its chevron and becomes the bare avatar circle it is on phones,
   10 px from the bell as there. Measured in the site's own fonts (Sora,
   Inter), which run wider than the system fonts that stand in for them. */
.nav-links a, .nav-link-btn { white-space: nowrap; }
.auth-avatar-btn > span {
  max-width: 140px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 1180px) {
  .auth-avatar-btn { gap: 6px; padding-right: 10px; }
  .auth-avatar-btn > span { display: none; }
}
@media (min-width: 761px) and (max-width: 1180px) {
  .nav-links { gap: 18px; }
}
@media (min-width: 761px) and (max-width: 880px) {
  .nav-inner { gap: 16px; }
  .nav-links { gap: 11px; }
  .auth-avatar-btn { padding: 4px; }
  .auth-avatar-btn .chev { display: none; }
}
@media (min-width: 761px) and (max-width: 800px) {
  .nav-inner { gap: 8px; }
  .nav-links { gap: 9px; }
  .nav-actions { gap: 10px; }
  .nav-signin { padding-left: 10px; padding-right: 10px; }
}

@media (max-width: 760px) {
  .nav-inner { gap: 16px; }
  .nav-actions { gap: 10px; }
  .nav-bell { width: 40px; height: 40px; }
  /* Just the avatar, a circle the size of the bell beside it. */
  .auth-avatar-btn { padding: 4px; }
  .auth-avatar-btn .chev { display: none; }
  /* A sheet under the header instead of a dropdown: the full width,
     less a margin, scrolling inside itself. */
  .bell-panel {
    position: fixed; top: var(--nav-h); left: 8px; right: 8px;
    width: auto; max-height: 70vh;
  }
  /* The floating Discord button would sit on top of the sheet, or on the
     phone menu's last rows when it scrolls on a short screen. */
  body.bell-sheet-open .fab-wrap,
  body.nav-sheet-open .fab-wrap { display: none; }
}

/* The narrowest phones cannot fit the wordmark beside three round
   buttons; the mark alone still says whose site this is. Signed out
   there is only the burger, so the wordmark stays. */
@media (max-width: 359px) {
  .nav-inner:has(.nav-bell) .nav-logo span { display: none; }
}
/* Up to 374 px the wordmark and the three round buttons fit only with a
   little less space between them. */
@media (min-width: 360px) and (max-width: 374px) {
  .nav-inner:has(.nav-bell) { gap: 12px; }
  .nav-inner:has(.nav-bell) .nav-actions,
  .nav-inner:has(.nav-bell) #authArea { gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .bell-panel, .bell-panel.open { transition: none; transform: none; }
  .bell-skel i, .bell-skel b { animation: none; }
  .nav-bell, .bell-row, .bell-dot, .bell-foot, .bell-ping-ic, .bell-ping-mini-ic { transition: none; }
  .bell-ping-mini { animation: none; }
  .bell-tip,
  .bell-ping-mini:hover .bell-tip,
  .bell-ping-mini:has(input:focus-visible) .bell-tip { transition: none; transform: none; }
}

/* ---- Plain video tiles (privacy update 2026-10-22) ----
   A preset video whose thumbnail copy is not in the site's storage yet
   (or a guide still that fails to load) shows the same dark fill a
   preset card shows without a screenshot, with its usual play button. */
.video-thumb.is-blank,
.detail-thumbs button.is-blank,
.hg-still.is-blank {
  background: linear-gradient(140deg, var(--panel-3), var(--panel));
}


/* ============================================================
   SUPPORTER BANNER
   Under the header of the Feature Hub's, the Bug Tracker's and the
   preset gallery's home pages, and at the top of a request's, a bug's
   and a preset's page; never on a form. Here rather than in
   css/hub.css, because the preset pages load this file alone. One
   picture, the wide one on desktop and the boxed one (at most 560 px,
   centered) from 860 px down, where the wide one's small line would be
   too small to read. Its frame and corners are in the pictures
   themselves.
   ============================================================ */

.hub-ad {
  display: block; margin: 0 0 34px; border-radius: 8px; line-height: 0;
  transition: transform 0.18s ease, filter 0.18s ease;
}
.hub-ad picture, .hub-ad img { display: block; width: 100%; height: auto; }
.hub-ad:hover { transform: translateY(-1px); filter: brightness(1.08); }
.hub-ad:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (max-width: 860px) {
  .hub-ad { max-width: 560px; margin: 0 auto 26px; }
}
@media (prefers-reduced-motion: reduce) {
  .hub-ad { transition: none; }
  .hub-ad:hover { transform: none; }
}
/* Nobody on the team is asked: the owner, Valkyries, Staff and
   Contributors (js/app.js sets html.hub-team). */
html.hub-team .hub-ad { display: none; }

/* Words only a screen reader reads: the banner's "(opens in a new tab)"
   and every such line of the hub pages. Pinned to the corner of the box
   that holds it: left at its natural spot - after a long title that is
   clipped on screen, as in a mini row - the 1 px box lands past the
   row's edge and widens a phone page sideways. */
.hub-sr {
  position: absolute !important;
  top: 0; left: 0;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
