/* ==========================================================================
   FiveLeak

   Design intent: a quiet catalogue. The library is large and trustworthy, so
   the interface behaves like it has nothing to prove — it states things once,
   at a comfortable size, with room around them.

   The four rules that hold it together:
     1. Space separates, not lines. There is almost no border in this file.
        Where two things need distinguishing, they get distance instead of a
        hairline. Rules are reserved for genuine document structure.
     2. Three text sizes carry the whole site. Body, small, caption. Hierarchy
        comes from weight, colour and space — adding a fourth size is how a
        page starts to look assembled.
     3. One accent, one call to action. The violet appears on links and on the
        single primary action of the current view. Never twice on one screen.
     4. Motion eases, it does not snap. 300ms on Apple's own curve. Things
        arrive rather than appear.

   Signature: the soft select. The index has no row dividers. Hovering paints a
   rounded surface that bleeds past the content edge and settles into place,
   the way a macOS list selection does.

   Sections: tokens · reset · primitives · header · index rows · hero ·
             browse · detail · requests · auth · admin · legal · 404 · ban ·
             footer · responsive
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */

:root {
  /* Near-black rather than pure black: a few points of lift stop the surface
     step from having to be aggressive to register as a separate plane. */
  --bg:          #08080a;
  --bg-sunk:     #050506;

  /* One surface, not five. The old ramp had --surface through --surface-3 and
     the extra steps were doing nothing that space could not do better. */
  --surface:     #151518;
  --surface-2:   #1e1e22;

  --line:        #232328;
  --line-soft:   #191a1d;
  --line-hi:     #33333a;

  /* Three steps. Contrast against --bg: 18.4 / 7.8 / 5.2. Against --surface,
     where the weakest step also has to survive: 16.7 / 7.1 / 4.8. All clear
     WCAG AA, solved numerically rather than by eye. */
  --text:        #f5f5f7;
  --text-2:      #a1a1a6;
  --text-3:      #828289;
  /* Retained so older rules keep resolving; deliberately identical to --text-3
     rather than a fourth, dimmer step that would fail AA. */
  --text-4:      #828289;

  /* The periwinkle from the logo, as one flat colour. 7.3:1 on the canvas and
     7.1:1 against --on-accent, so it works both as link text and as a filled
     button — legible, not merely decorative. */
  --accent:      #9f8cff;
  --accent-soft: #b6a8ff;
  --accent-dim:  #6f5fd0;
  --accent-wash: rgba(159, 140, 255, .10);
  --on-accent:   #100a20;

  --rust:        #ff8071;
  --rust-wash:   rgba(255, 128, 113, .10);
  --sage:        #5fd6a4;
  --sage-wash:   rgba(95, 214, 164, .10);
  --amber:       #ffc46b;

  /* Framework identity. Desaturated on purpose: these categorise, they do not
     decorate, and they must never read as louder than the accent. */
  --fw-standalone: #90929c;
  --fw-esx:        #7cb8db;
  --fw-qbcore:     #e39586;
  --fw-qbox:       #c495e3;
  --fw-other:      #90929c;

  /* Radii vary by role rather than being one value applied everywhere:
     pill for actions, generous for panels, modest for inline chrome. */
  --r-xs:   6px;
  --r-sm:   10px;
  --r:      14px;
  --r-lg:   20px;
  --r-pill: 980px;

  --font:  'Schibsted Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /*
    Type scale. Four display steps and three text steps — that is the entire
    vocabulary. Fluid so the hero can be genuinely large on a desktop without
    breaking a phone.
  */
  --t-caption: .8125rem;   /* 13 — timestamps, counts, fine print          */
  --t-small:   .9375rem;   /* 15 — secondary copy, controls, table cells   */
  --t-body:    1.0625rem;  /* 17 — body. Apple's reading size, and correct */
  --t-lead:    clamp(1.125rem, 1.06rem + .3vw, 1.3125rem);
  --t-h3:      clamp(1.0625rem, 1.03rem + .18vw, 1.1875rem);
  --t-h2:      clamp(1.5rem, 1.29rem + 1.05vw, 2.25rem);
  --t-h1:      clamp(1.875rem, 1.55rem + 1.6vw, 2.875rem);
  --t-display: clamp(2.5rem, 1.62rem + 4.4vw, 4.75rem);

  /*
    Spacing. Every gap in this file is one of these. The jump from --sp-7 up is
    deliberately steep — generous space between sections is most of what makes
    a layout read as calm rather than crowded.
  */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  24px;
  --sp-6:  32px;
  --sp-7:  48px;
  --sp-8:  clamp(56px, 7vw, 80px);
  --sp-9:  clamp(72px, 10vw, 112px);
  --sp-10: clamp(96px, 14vw, 160px);

  --shell:  1120px;
  --measure: 34rem;

  /* Apple's own easing curve for web motion: a long, decelerating settle. */
  --ease: cubic-bezier(.32, .72, 0, 1);
  --fast: 180ms cubic-bezier(.32, .72, 0, 1);
  --slow: 300ms cubic-bezier(.32, .72, 0, 1);
}

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

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

html { -webkit-text-size-adjust: 100%; }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: 1.6;
  letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4 {
  margin: 0;
  line-height: 1.12;
  /* Optical tracking: large type needs to be pulled tighter than small type,
     which is the single most reliable difference between typeset and default. */
  letter-spacing: -.022em;
  font-weight: 600;
}
h1 { letter-spacing: -.032em; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
figure { margin: 0; }

a { color: var(--accent); text-decoration: none; transition: color var(--fast); }
@media (hover: hover) {
  a:hover { color: var(--accent-soft); }
}

img, svg { display: block; max-width: 100%; }
svg { width: 1em; height: 1em; }

code, kbd { font-family: var(--mono); font-size: .875em; color: var(--text-2); }

::selection { background: var(--accent); color: var(--on-accent); }

/* A soft halo rather than a hard rectangle — matches the radii around it. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

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

.dim, .muted { color: var(--text-3); }
.is-accent { color: var(--accent) !important; }
.is-muted { color: var(--text-3) !important; }

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--sp-5); }
.site-main { flex: 1; padding-bottom: var(--sp-10); }
.section { padding-top: var(--sp-9); }

/*
  Everything that starts invisible lives under `.has-motion`, which /js/boot.js
  adds before first paint — and only when scripting is on and the visitor has
  not asked for reduced motion. If GSAP never arrives, boot.js's watchdog
  removes the class and every rule below stops applying.

  Kept together deliberately: this block is the complete list of things that
  can be invisible on arrival, so it is the only place to look when something
  fails to appear.
*/
/*
  INVARIANT: every selector below must also appear in SCROLL_HIDDEN in
  motion.js, and something in that file must animate it back to visible. A
  selector hidden here with no matching animation there is invisible forever —
  which is exactly what happened to a generic `[data-reveal]` hook that was
  hidden here after the code that revealed it had moved out of app.js.
*/
.has-motion .section-head h2,
.has-motion .res-index .res-row,
.has-motion .cat-grid .cat-cell,
.has-motion .footer-brand,
.has-motion .footer-links > div,
.has-motion .prose > *,
.has-motion .legal-doc section,
.has-motion .empty-state { opacity: 0; }

.has-motion .section-head h2,
.has-motion .res-index .res-row,
.has-motion .footer-brand,
.has-motion .footer-links > div,
.has-motion .prose > *,
.has-motion .legal-doc section { transform: translateY(16px); }

.has-motion .cat-grid .cat-cell { transform: translateX(-18px); }
.has-motion .empty-state { transform: translateY(14px) scale(.985); }
.has-motion .res-index .res-thumb { transform: scale(.9); }
.has-motion .section-head .link-arrow { opacity: 0; transform: translateX(14px); }
.has-motion .cat-grid .cat-count { opacity: 0; }

/* The hero is split into words by script; hiding the heading until that has
   happened avoids a flash of the unsplit line. */
.has-motion .masthead h1,
.has-motion .masthead-copy > p,
.has-motion .masthead .search-bar,
.has-motion .masthead-stats > span { opacity: 0; }

/* Word mask for the headline reveal. */
.masthead h1 .w { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: .06em; }
.masthead h1 .wi { display: inline-block; will-change: transform; }

/* Reading progress, appended to the header by script. */
.scroll-progress {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* The morphing navigation pill. Once script owns it, the static active
   background steps aside so the two do not double up. */
.site-nav { position: relative; }
.site-nav a { position: relative; z-index: 1; }
.nav-pill {
  position: absolute;
  top: 0; left: 0;
  height: 100%;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--surface);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
}
.site-nav.has-pill a.is-active { background: none; }

/* --- Loading states ------------------------------------------------------- */

/*
  Skeletons, applied only where something is genuinely still arriving.

  On a server-rendered site the text is already in the HTML by the time the
  browser paints, so a skeleton over a headline or a table row would be a
  placeholder for content that is sitting right behind it — theatre that
  delays the page rather than describing it. These are scoped to the three
  things that really do load late: cover images fetched from object storage,
  the bot-challenge iframe, and the wait between pages.

  The sweep is deliberately faint. A skeleton is a promise that content is
  coming, not an attraction.
*/

@keyframes skeleton-sweep {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}

.is-loading {
  position: relative;
  overflow: hidden;
}
.is-loading::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(100deg,
      transparent 20%,
      rgba(255, 255, 255, .05) 45%,
      rgba(255, 255, 255, .07) 50%,
      rgba(255, 255, 255, .05) 55%,
      transparent 80%),
    var(--surface-2);
  background-size: 220% 100%, 100% 100%;
  background-repeat: no-repeat;
  animation: skeleton-sweep 1.6s ease-in-out infinite;
  pointer-events: none;
}

/*
  The image fades up as the skeleton is removed. Keyed on `.is-loading`, which
  only script ever sets — so with JavaScript off nothing is hidden and images
  appear exactly as the browser paints them.
*/
.res-thumb img,
.detail-shot img,
.auth-wordmark { transition: opacity .35s var(--ease); }
.is-loading img,
img.is-loading { opacity: 0; }

/* Reserve the challenge's height and shimmer until Cloudflare's iframe lands,
   instead of leaving a 65px hole above the submit button. */
.turnstile-slot.is-loading { border-radius: var(--r-sm); }

/*
  Navigation progress. A server round-trip has no measurable progress to
  report, so the bar eases toward a ceiling it never reaches and completes only
  when the next document actually arrives — honest about what it does and does
  not know.
*/
.nav-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 100;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  opacity: 0;
  pointer-events: none;
  transition: transform .35s cubic-bezier(.1, .6, .3, 1), opacity .25s ease-out;
}
.nav-progress.is-active { opacity: 1; }
.nav-progress.is-done { transition: transform .18s ease-out, opacity .35s ease-out .1s; }

@media (prefers-reduced-motion: reduce) {
  /* Keep the surface, drop the sweep: the shape still says "loading". */
  .is-loading::after { animation: none; background-image: none; }
  .nav-progress { transition-duration: .01ms; }
}

/* Tag pill wipe. The fill is a gradient stop driven by --wipe. */
.res-tags a.has-wipe {
  background-image: linear-gradient(to right, var(--accent-wash) var(--wipe, 0%), transparent var(--wipe, 0%));
  transition: color var(--fast);
}
.res-tags a.has-wipe:hover { background-color: var(--surface-2); color: var(--accent); }

/* Auto-dismiss countdown on a success flash. */
.flash { position: relative; overflow: hidden; }
.flash-timer {
  position: absolute;
  left: 0; bottom: 0;
  height: 2px; width: 100%;
  background: var(--sage);
  opacity: .5;
  transform-origin: 0 50%;
  pointer-events: none;
}

/* --- Primitives ----------------------------------------------------------- */

/*
  Labels. These used to be 10.5px uppercase mono with .14em tracking, applied to
  roughly twenty different things. Shouting in a small voice is still shouting;
  they are now simply small, sentence-case and quiet.
*/
.rail-label, .field-label, .metric-label, .legal-toc-label, .admin-inline-label,
.ban-eyebrow, .download-eyebrow {
  display: block;
  font-size: var(--t-caption);
  font-weight: 500;
  color: var(--text-3);
  letter-spacing: 0;
  text-transform: none;
}

/* Section headings sit in space, with no rule beneath them. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: var(--sp-5);
}
.section-head h2 {
  font-size: var(--t-h2);
  font-weight: 600;
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
}
.section-head p { margin-top: var(--sp-2); color: var(--text-2); font-size: var(--t-small); }

/* Retained for markup that still carries it; renders as nothing. */
.section-index { display: none; }

.rule-head {
  font-size: var(--t-h3);
  font-weight: 600;
  margin-bottom: var(--sp-4);
}

/* Apple's trailing chevron link. The chevron nudges, the text does not move. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--accent);
  white-space: nowrap;
}
/* The nudge is driven by --chev so GSAP can ease it on the same curve as the
   rest of the site. Without scripting the chevron simply sits still. */
.link-arrow::after {
  content: '›';
  font-size: 1.25em;
  line-height: 1;
  transform: translate(var(--chev, 0px), -.05em);
}

/* A panel is a soft plane, not a bordered box: no outline, just a lift in tone. */
.panel, .card {
  background: var(--surface);
  border: 0;
  border-radius: var(--r);
}

/* The corner-tick motif is gone. Kept as a no-op so stray markup is harmless. */
.ticked { position: relative; }

/* Buttons: pill, flat, one accent. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 11px 22px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--fast), border-color var(--fast),
              color var(--fast), transform var(--fast);
}
.btn svg { width: 16px; height: 16px; }
.btn:active { transform: scale(.975); }

.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
@media (hover: hover) {
  .btn-primary:hover { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--on-accent); }
}

/* Secondary is a tinted plane, not an outline — outlines read as borders and
   this file is trying not to have any. */
.btn-ghost { background: var(--surface-2); border-color: transparent; color: var(--text); }
@media (hover: hover) {
  .btn-ghost:hover { background: #26262c; color: var(--text); }
}

/* Affirmative but subordinate: carries the accent as text only, so it reads as
   the lead of its group without competing with the page's one filled CTA. */
.btn-ghost.is-affirm { background: var(--accent-wash); color: var(--accent); }
@media (hover: hover) {
  .btn-ghost.is-affirm:hover { background: rgba(159, 140, 255, .17); color: var(--accent-soft); }
}

.btn-danger { background: var(--rust-wash); border-color: transparent; color: var(--rust); }
@media (hover: hover) {
  .btn-danger:hover { background: rgba(255, 128, 113, .18); color: var(--rust); }
}

/* A text-only action, for the quiet half of a CTA pair. */
.btn-plain { background: transparent; color: var(--accent); padding-inline: var(--sp-3); }
@media (hover: hover) {
  .btn-plain:hover { background: var(--accent-wash); color: var(--accent-soft); }
}

.btn-sm { padding: 7px 16px; font-size: var(--t-caption); }
.btn-lg { padding: 14px 28px; font-size: var(--t-body); }
.btn-block { width: 100%; }
.btn[disabled], .btn.is-busy { opacity: .45; pointer-events: none; }

/*
  Search. The mono `/` prefix read as a command line, which is a different
  product's personality; a magnifier is the plain, correct affordance.
*/
.search-bar {
  display: flex;
  align-items: center;
  gap: 0;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--surface);
  transition: border-color var(--fast), background var(--fast);
}
.search-bar:focus-within { border-color: var(--accent-dim); background: var(--surface-2); }
.search-prefix {
  display: grid;
  place-items: center;
  padding: 0 0 0 18px;
  color: var(--text-3);
  user-select: none;
  pointer-events: none;
}
.search-prefix svg { width: 17px; height: 17px; }
.search-bar input {
  flex: 1;
  min-width: 0;
  padding: 14px 18px;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: var(--t-small);
}
.search-bar input:focus { outline: none; }
.search-bar input::placeholder { color: var(--text-3); }
.search-bar button {
  margin: 5px;
  padding: 9px 20px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--fast);
}
@media (hover: hover) {
  .search-bar button:hover { background: var(--accent-soft); }
}
.search-bar--sm input { padding: 10px 14px; font-size: var(--t-caption); }
.search-bar--sm .search-prefix { padding-left: 14px; }
.search-bar--sm .search-prefix svg { width: 15px; height: 15px; }

.input {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-small);
  transition: border-color var(--fast), background var(--fast);
}
.input::placeholder { color: var(--text-3); }
.input:focus { outline: none; border-color: var(--accent-dim); background: var(--surface-2); }
.input-sm { padding: 7px 11px; font-size: var(--t-caption); }

select.input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23828289' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px;
  padding-right: 34px;
}

.textarea { resize: vertical; min-height: 140px; line-height: 1.65; font-family: var(--font); }

.input-file { padding: 10px 12px; cursor: pointer; font-size: var(--t-caption); }
.input-file::file-selector-button {
  margin-right: var(--sp-3);
  padding: 6px 14px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font: inherit;
  font-size: var(--t-caption);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.input-file::file-selector-button:hover { background: #26262c; color: var(--text); }

.field { display: block; margin-bottom: var(--sp-5); }
.field-label { margin-bottom: var(--sp-2); }
.field-label em { font-style: normal; color: var(--accent); margin-left: var(--sp-1); }
.field-hint {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--t-caption);
  color: var(--text-3);
  letter-spacing: 0;
  text-transform: none;
  font-family: var(--font);
}

.checkbox, .radio {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  font-size: var(--t-small);
  color: var(--text-2);
  cursor: pointer;
}
.checkbox input, .radio input { accent-color: var(--accent); width: 16px; height: 16px; cursor: pointer; }
.checkbox:hover, .radio:hover { color: var(--text); }

.form-error {
  padding: var(--sp-4);
  margin-bottom: var(--sp-5);
  border: 0;
  border-radius: var(--r-sm);
  background: var(--rust-wash);
  color: #ffb0a4;
  font-size: var(--t-small);
}
.form-error ul { margin-top: var(--sp-2); padding-left: var(--sp-4); list-style: disc; }

.form-actions { display: flex; gap: var(--sp-3); margin-top: var(--sp-2); }

/* Turnstile renders a fixed-height iframe; the slot reserves the space so the
   submit button does not jump when the widget finishes loading. */
.turnstile-slot { margin: 0 0 var(--sp-5); min-height: 65px; }
.turnstile-slot:empty { display: none; }
.field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }

/*
  The spec list. Metadata as aligned key/value pairs. It used to be mono
  throughout with a dashed rule under every row — fourteen dashed lines in a
  column is texture, not information. Labels are now plain text and only the
  values that are genuinely data stay in mono.
*/
.manifest { margin: 0; display: grid; gap: 0; font-size: var(--t-small); }
.manifest > div {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: var(--sp-4);
  padding: 9px 0;
}
.manifest dt { color: var(--text-3); }
.manifest dd { margin: 0; color: var(--text); font-family: var(--mono); font-size: var(--t-caption); }

/* A filename is one long unbreakable token and the value column is narrow, so
   it gets its own line rather than being hyphen-hacked in half. */
.manifest > div.is-stacked { grid-template-columns: minmax(0, 1fr); gap: var(--sp-1); }
.manifest dd.is-wrap { overflow-wrap: anywhere; color: var(--text-2); }

/* Framework tag: a coloured dot plus plain text. */
.fw-tag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-caption);
  color: var(--text-2);
  white-space: nowrap;
}
.fw-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--fw, var(--fw-standalone)); flex-shrink: 0; }
.fw-tag--lg { font-size: var(--t-small); gap: var(--sp-2); }
.fw-tag--lg .fw-dot { width: 7px; height: 7px; }

.res-row--standalone { --fw: var(--fw-standalone); }
.res-row--esx        { --fw: var(--fw-esx); }
.res-row--qbcore     { --fw: var(--fw-qbcore); }
.res-row--qbox       { --fw: var(--fw-qbox); }
.res-row--other      { --fw: var(--fw-other); }

/*
  One quiet marker style, replacing what used to be four near-identical ones
  (.res-flag, .chip, .pill, .badge, each with its own borders and casing).
*/
.res-flag, .chip, .badge, .pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--font);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}
.res-flag { background: var(--accent-wash); color: var(--accent); }
.res-flag--warn { background: rgba(255, 196, 107, .12); color: var(--amber); }

.badge-ok     { background: var(--sage-wash); color: var(--sage); }
.badge-muted  { background: var(--surface-2); color: var(--text-3); }
.badge-accent { background: var(--accent-wash); color: var(--accent); }
.badge-danger { background: var(--rust-wash); color: var(--rust); }
.badge-soft   { background: var(--surface-2); color: var(--text-2); }
.badge-draft  { background: rgba(255, 196, 107, .12); color: var(--amber); }
.pill-accent  { background: var(--accent-wash); color: var(--accent); }

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

/*
  Translucent and blurred. This is the one element in the file allowed to use
  glass: it is fixed chrome that content scrolls beneath, which is the only
  place the effect actually means something.
*/
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(8, 8, 10, .72);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--line-soft);
}
@supports not (backdrop-filter: blur(1px)) {
  .site-header { background: var(--bg); }
}

.header-inner { display: flex; align-items: center; gap: var(--sp-6); height: 60px; }

.brand { display: inline-flex; align-items: center; gap: var(--sp-2); color: var(--text); flex-shrink: 0; }
.brand:hover { color: var(--text); }

/*
  The monogram, two-tone like the original: chrome-white F, violet L. The source
  logo carries a bevel, a gradient and an outer glow — all of which turn to mush
  at this size, so the UI uses a flat cut of the same geometry and leaves the
  glossy lockup for display contexts.
*/
.mark { width: 22px; height: 22px; flex-shrink: 0; overflow: visible; }
.mark .mark-f { fill: currentColor; }
.mark .mark-l { fill: var(--accent); stroke: var(--bg); }

.brand-text { font-size: var(--t-body); font-weight: 600; letter-spacing: -.03em; }

/* Footer sits on --bg-sunk, so the knockout stroke has to match that instead. */
.footer-brand .mark { width: 24px; height: 24px; }
.footer-brand .mark .mark-f { fill: var(--text-3); }
.footer-brand .mark .mark-l { stroke: var(--bg-sunk); }

.brand-mark-lg { display: block; margin-bottom: var(--sp-4); color: var(--text); }
.brand-mark-lg .mark { width: 32px; height: 32px; }
.brand-mark-lg .mark .mark-l { stroke: var(--surface); }

/* Display lockup. Given room to breathe and nothing stacked on top of it. */
.auth-wordmark { width: 100%; max-width: 190px; height: auto; margin-bottom: var(--sp-6); }

.site-nav { display: flex; gap: var(--sp-1); }
.site-nav a {
  padding: 6px 12px;
  border-radius: var(--r-pill);
  color: var(--text-2);
  font-size: var(--t-small);
  font-weight: 450;
  transition: color var(--fast), background var(--fast);
}
@media (hover: hover) {
  .site-nav a:hover { color: var(--text); }
}
.site-nav a.is-active { color: var(--text); background: var(--surface); }

.header-search {
  flex: 1;
  max-width: 240px;
  margin-left: auto;
  background: var(--surface);
}
.header-search input { padding: 8px 14px; font-size: var(--t-caption); }
.header-search .search-prefix { padding-left: 14px; }
.header-search .search-prefix svg { width: 15px; height: 15px; }

.header-actions { display: flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; }

.avatar {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--t-caption);
  font-weight: 600;
  flex-shrink: 0;
}

.user-chip { position: relative; }
.user-chip-button {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 4px 14px 4px 4px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: var(--t-small);
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.user-chip-button:hover { background: var(--surface-2); color: var(--text); }
.user-chip-name { max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.user-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 220px;
  padding: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .5);
}
.user-menu-head { padding: var(--sp-2) var(--sp-3) var(--sp-3); }
.user-menu-head strong { display: block; font-size: var(--t-small); }
.user-menu-head span { font-size: var(--t-caption); color: var(--text-3); }
.user-menu a, .user-menu-signout {
  display: block;
  width: 100%;
  padding: 9px var(--sp-3);
  border: 0;
  border-radius: var(--r-xs);
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: var(--t-small);
  text-align: left;
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.user-menu a:hover, .user-menu-signout:hover { background: var(--surface-2); color: var(--text); }
.user-menu-signout { color: var(--rust); }

/* --- Flash ---------------------------------------------------------------- */

.flash-stack { padding-top: var(--sp-4); display: grid; gap: var(--sp-2); }
.flash {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 0;
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: var(--t-small);
}
.flash-mark { font-weight: 600; color: var(--text-3); }
.flash-success { background: var(--sage-wash); }
.flash-success .flash-mark { color: var(--sage); }
.flash-error { background: var(--rust-wash); }
.flash-error .flash-mark { color: var(--rust); }
.flash-close {
  margin-left: auto;
  border: 0; background: none; color: var(--text-3);
  font-size: 1.25rem; line-height: 1; cursor: pointer; padding: 0 var(--sp-1);
}
.flash-close:hover { color: var(--text); }

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

/*
  Centred, and given the whole viewport width to be quiet in. One statement,
  one supporting line, one action. Everything that used to live here — the
  five-row stats table, the jump links, the eyebrow — was competing with the
  thing people actually came to do.
*/
/* The section below already contributes --sp-9 of its own top padding, so the
   hero only needs a little: stacking both reads as a gap, not as breathing. */
.masthead { padding: var(--sp-10) 0 var(--sp-4); }
.masthead-inner { display: block; text-align: center; }

/* A rem measure, not a ch one: `ch` here would be sized by the container's
   body-size font while the headline inside runs at four times that, which
   crushes the display type into a column. */
.masthead-copy { max-width: 44rem; margin-inline: auto; }

.masthead h1 {
  font-size: var(--t-display);
  font-weight: 600;
  letter-spacing: -.038em;
  line-height: 1.05;
  text-wrap: balance;
}
.masthead-copy > p {
  margin: var(--sp-5) auto 0;
  max-width: 46ch;
  color: var(--text-2);
  font-size: var(--t-lead);
  line-height: 1.45;
  text-wrap: pretty;
}

.masthead .search-bar {
  margin: var(--sp-7) auto 0;
  max-width: 520px;
  background: var(--surface);
}

.masthead-jump {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2) var(--sp-5);
  margin-top: var(--sp-5);
  font-size: var(--t-small);
  color: var(--text-3);
}
.masthead-jump a { color: var(--text-2); }
.masthead-jump a:hover { color: var(--accent); }

/* The stats line: one row of plain text, not a bordered table. */
.masthead-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2) var(--sp-5);
  margin-top: var(--sp-6);
  font-size: var(--t-small);
  color: var(--text-3);
}
.masthead-stats b { color: var(--text-2); font-weight: 600; font-variant-numeric: tabular-nums; }

.masthead-manifest { display: none; }

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

/*
  The signature: soft select. Lists on this site carry no dividers at all.
  Hovering paints a rounded surface that bleeds past the content edge and
  settles into place, the way a macOS list selection does. Nothing lifts,
  nothing shadows, nothing borders.

  Used by the resource index and the category list, so it reads as the site's
  behaviour rather than one screen's flourish.
*/
.res-row, .cat-cell { position: relative; }
.res-row::before, .cat-cell::before {
  content: '';
  position: absolute;
  inset: 2px calc(var(--sp-4) * -1);
  border-radius: var(--r);
  background: var(--surface);
  opacity: 0;
  transform: scale(.985);
  transition: opacity var(--slow), transform var(--slow);
  z-index: 0;
}
.res-row > *, .cat-cell > * { position: relative; z-index: 1; }
@media (hover: hover) {
  .res-row:hover::before, .cat-cell:hover::before { opacity: 1; transform: none; }
}
.res-row:focus-within::before, .cat-cell:focus-visible::before { opacity: 1; transform: none; }

/* A resource is a file plus metadata, so it is a row rather than a card. */
.res-index { display: grid; }

.res-row {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) auto;
  gap: var(--sp-5);
  align-items: center;
  padding: var(--sp-4) 0;
  border: 0;
}
.res-row.is-featured::before { opacity: .5; transform: none; }

.res-thumb {
  display: grid;
  place-items: center;
  width: 52px; height: 52px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  overflow: hidden;
}
.res-thumb img { width: 100%; height: 100%; object-fit: cover; }
.res-thumb-initials {
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--fw, var(--text-3));
  letter-spacing: -.03em;
}

.res-main { min-width: 0; }
.res-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-3); }

.res-title { font-size: var(--t-body); font-weight: 550; letter-spacing: -.017em; }
/* Whole-row click target without nesting links inside links. */
.res-title a { color: var(--text); }
.res-title a::after { content: ''; position: absolute; inset: 0; }
@media (hover: hover) {
  .res-row:hover .res-title a { color: var(--text); }
}

.res-summary {
  margin-top: 3px;
  max-width: 72ch;
  color: var(--text-2);
  font-size: var(--t-small);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/*
  One quiet metadata line, replacing a four-column right-hand rail of
  label/value pairs. Every value the rail carried is still here — framework,
  version, size, downloads — it just no longer needs eight elements and a set
  of uppercase captions to say them.
*/
.res-sub {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
  font-size: var(--t-caption);
  color: var(--text-3);
}
/*
  Separators are generated, not marked up, so that a wrapped line starts with
  "· 8.4 MB" rather than leaving an orphaned dot at the end of the line above.
*/
.res-sub > * + *::before { content: '·'; color: var(--line-hi); margin-right: var(--sp-2); }
/* No tabular figures here: they align columns, and this is running text now. */
.res-sub b { font-weight: 400; color: var(--text-3); }
.res-cat { color: var(--text-3); }

.res-tags { display: inline-flex; flex-wrap: wrap; gap: var(--sp-2); }
.res-tags a {
  position: relative;
  z-index: 1;
  padding: 3px 11px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--t-caption);
  transition: background var(--fast), color var(--fast);
}
.res-tags a:hover { background: var(--accent-wash); color: var(--accent); }
.res-tags--lg a { padding: 5px 14px; font-size: var(--t-small); }

/* The right-hand cell now holds only the download count. */
.res-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  text-align: right;
  color: var(--text-3);
  font-size: var(--t-caption);
}
.res-meta-cell { display: block; }
.res-meta-cell b { font-weight: 500; color: var(--text-2); font-variant-numeric: tabular-nums; }
.res-meta-cell em { font-style: normal; }

/* --- Categories ----------------------------------------------------------- */

/*
  A list, not a grid of cards. Seven identical rounded tiles in an auto-fill
  grid is the most generic thing a page can do, and it made seven category
  names — the actual content — compete with seven boxes. As a typographic list
  the names carry it, and the soft select from the index applies unchanged.
*/
.cat-grid { display: grid; background: none; border: 0; }
.cat-cell {
  display: grid;
  grid-template-columns: 210px minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--sp-5);
  padding: var(--sp-4) 0;
  background: none;
  color: var(--text);
}
.cat-name { font-size: var(--t-h3); font-weight: 550; letter-spacing: -.022em; }
.cat-desc { font-size: var(--t-small); color: var(--text-2); line-height: 1.5; }
.cat-count { font-size: var(--t-small); color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap; }

.empty-state {
  padding: var(--sp-9) var(--sp-5);
  border: 0;
  border-radius: var(--r);
  background: var(--surface);
  text-align: center;
}
.empty-state h3 { font-size: var(--t-h3); margin-bottom: var(--sp-2); }
.empty-state p { color: var(--text-2); font-size: var(--t-small); }

/* --- Page heads & browse -------------------------------------------------- */

.page-head { padding: var(--sp-9) 0 var(--sp-6); }
.page-head h1 { font-size: var(--t-h1); font-weight: 600; }
.page-head-meta, .page-head > p {
  margin-top: var(--sp-3);
  max-width: 52ch;
  font-size: var(--t-lead);
  color: var(--text-2);
  line-height: 1.45;
}
.page-head-meta { font-size: var(--t-small); }
.page-head-split { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-6); }

.narrow-page { max-width: 720px; }

.browse-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--sp-8); align-items: start; }

.filter-rail { position: sticky; top: 84px; }
.filter-group { margin-bottom: var(--sp-6); }
.filter-group .rail-label { margin-bottom: var(--sp-3); }
.rail-list { display: grid; gap: 1px; }

.rail-opt {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 6px 10px;
  margin-inline: -10px;
  border-radius: var(--r-xs);
  font-size: var(--t-small);
  color: var(--text-2);
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.rail-opt:hover { background: var(--surface); color: var(--text); }
.rail-opt input { appearance: none; width: 6px; height: 6px; border-radius: 50%; background: var(--line-hi); margin: 0; flex-shrink: 0; cursor: pointer; transition: background var(--fast), box-shadow var(--fast); }
.rail-opt input:checked { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.rail-opt:has(input:checked) { color: var(--text); }
.rail-opt em { margin-left: auto; font-style: normal; color: var(--text-3); font-size: var(--t-caption); font-variant-numeric: tabular-nums; }
.filter-actions { display: grid; gap: var(--sp-2); }

.results-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 34px;
  padding-bottom: var(--sp-4);
}
.active-filters { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sort-control {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--t-caption);
  color: var(--text-3);
  letter-spacing: 0;
  text-transform: none;
  flex-shrink: 0;
}

.pagination { display: flex; align-items: center; justify-content: center; gap: var(--sp-1); margin-top: var(--sp-8); }
.page-num, .page-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px; height: 36px;
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-2);
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
  transition: color var(--fast), background var(--fast);
}
.page-num:hover, .page-arrow:hover { background: var(--surface); color: var(--text); }
.page-num.is-current { background: var(--accent); color: var(--on-accent); font-weight: 500; }
.page-num.is-current:hover { background: var(--accent); color: var(--on-accent); }
.page-arrow.is-disabled { opacity: .3; pointer-events: none; }
.page-gap { color: var(--text-3); padding: 0 var(--sp-1); }

/* --- Resource detail ------------------------------------------------------ */

/* Marked up as an ordered list for assistive tech and rich results; the
   separators are generated so they are never read aloud as content. */
.breadcrumb { padding: var(--sp-6) 0 0; font-size: var(--t-caption); color: var(--text-3); }
.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.breadcrumb li { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.breadcrumb li + li::before { content: '/'; color: var(--line-hi); }
.breadcrumb li:last-child { overflow: hidden; }
.breadcrumb-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.breadcrumb a { color: var(--text-3); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb-current { color: var(--text-2); }

.detail-layout { display: grid; grid-template-columns: minmax(0, 1fr) 304px; gap: var(--sp-8); align-items: start; padding-top: var(--sp-6); }
.detail-main { min-width: 0; }

/* No rule under the header — the space below it is the separation. */
.detail-head { padding-bottom: var(--sp-6); }
.detail-flags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.detail-head h1 { font-size: var(--t-h1); font-weight: 600; text-wrap: balance; }
.detail-summary { margin-top: var(--sp-4); max-width: 58ch; color: var(--text-2); font-size: var(--t-lead); line-height: 1.45; }

.detail-byline { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-5); font-size: var(--t-small); color: var(--text-3); }
.detail-byline strong { color: var(--text-2); font-weight: 500; }

/* A fixed ratio reserves the space before the cover loads. The image's real
   dimensions are not stored anywhere, so without this the whole article below
   it jumps down on load — the largest layout shift on the page. */
.detail-shot {
  margin-top: var(--sp-6);
  border: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
  aspect-ratio: 16 / 9;
}
.detail-shot img { width: 100%; height: 100%; object-fit: cover; }

.detail-section { margin-top: var(--sp-8); }

.prose { color: var(--text-2); font-size: var(--t-body); line-height: 1.7; max-width: 66ch; }
.prose p { margin-bottom: var(--sp-4); }
.prose p:last-child { margin-bottom: 0; }

.detail-side { position: sticky; top: 84px; display: grid; gap: var(--sp-3); }

/* The one filled action on the page, given room around it. */
.download-panel { padding: var(--sp-5); }
.download-panel .manifest { margin-top: var(--sp-5); }
.download-panel .btn-ghost { margin-top: var(--sp-3); }
.download-note { margin-top: var(--sp-3); font-size: var(--t-caption); color: var(--text-3); line-height: 1.5; text-align: center; }

.admin-inline-card { padding: var(--sp-4); }
.admin-inline-label { margin-bottom: var(--sp-3); color: var(--amber); }

/* --- Requests ------------------------------------------------------------- */

.status-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.status-tabs a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  color: var(--text-2);
  font-size: var(--t-small);
  transition: background var(--fast), color var(--fast);
}
.status-tabs a span { color: var(--text-3); font-variant-numeric: tabular-nums; }
.status-tabs a:hover { background: var(--surface); color: var(--text); }
.status-tabs a.is-active { background: var(--surface); color: var(--text); }
.status-tabs a.is-active span { color: var(--accent); }

.sort-link { font-size: var(--t-caption); color: var(--text-3); padding: 2px 4px; }
.sort-link.is-active { color: var(--accent); }

.request-list { display: grid; gap: var(--sp-2); }
.request-card {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: var(--sp-5);
  padding: var(--sp-5);
  border: 0;
  border-radius: var(--r);
  background: var(--surface);
  transition: background var(--fast);
}
.request-card:hover { background: var(--surface-2); }
.request-card.is-closed { opacity: .55; }
.request-card:target { background: var(--accent-wash); }

.vote-cell { display: flex; justify-content: center; }
.vote-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 52px;
  padding: var(--sp-2) 0;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-2);
  font: inherit;
  cursor: pointer;
  transition: background var(--fast), color var(--fast), transform var(--fast);
}
.vote-button svg { width: 15px; height: 15px; }
.vote-count { font-size: var(--t-small); font-weight: 600; font-variant-numeric: tabular-nums; }
.vote-button:hover { background: var(--accent-wash); color: var(--accent); }
.vote-button:active { transform: scale(.94); }
.vote-button.is-voted { background: var(--accent-wash); color: var(--accent); }
.vote-button.is-static { cursor: default; opacity: .6; }
.vote-button.is-static:hover { background: var(--surface-2); color: var(--text-2); }

.request-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.request-category { font-size: var(--t-caption); color: var(--text-3); }
.request-title { font-size: var(--t-h3); font-weight: 550; margin-bottom: var(--sp-2); }
.request-details { font-size: var(--t-small); color: var(--text-2); white-space: pre-wrap; max-width: 76ch; line-height: 1.55; }
.request-reference { display: inline-block; margin-top: var(--sp-2); font-size: var(--t-small); }

.request-resolution {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  color: var(--sage);
  font-size: var(--t-small);
}
.request-resolution::before { content: '→'; }

.request-note {
  margin-top: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  background: rgba(255, 196, 107, .07);
  font-size: var(--t-small);
  color: var(--text-2);
}
.request-note strong { color: var(--amber); font-weight: 500; margin-right: var(--sp-2); }
.request-foot { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); font-size: var(--t-caption); color: var(--text-3); }

/* --- Auth ----------------------------------------------------------------- */

/*
  A single centred column. The old layout put a four-bullet marketing list
  beside the form; nobody arriving at a sign-in page needs to be sold the
  product, and it was already hidden on anything narrower than a laptop.
*/
.auth-shell {
  display: grid;
  place-items: center;
  max-width: var(--shell);
  margin-inline: auto;
  padding: var(--sp-10) var(--sp-5);
}
.auth-card { width: min(420px, 100%); padding: 0; background: none; text-align: left; }
.auth-head { margin-bottom: var(--sp-6); text-align: center; }
.auth-head .brand-mark-lg { width: 32px; height: 32px; margin-inline: auto; margin-bottom: var(--sp-5); }
.auth-wordmark { margin-inline: auto; }
.auth-head h1 { font-size: var(--t-h2); font-weight: 600; }
.auth-head p { margin-top: var(--sp-3); color: var(--text-2); font-size: var(--t-small); }
.auth-alt { margin-top: var(--sp-5); font-size: var(--t-small); color: var(--text-3); text-align: center; }
.auth-aside { display: none; }
.auth-back { display: block; margin-top: var(--sp-6); font-size: var(--t-small); color: var(--text-3); text-align: center; }
.auth-back:hover { color: var(--accent); }

/* --- Admin ---------------------------------------------------------------- */

.admin-layout { display: grid; grid-template-columns: 196px minmax(0, 1fr); gap: var(--sp-8); padding-top: var(--sp-7); align-items: start; }

.admin-nav { display: grid; gap: 1px; position: sticky; top: 84px; }
.admin-nav a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 9px var(--sp-3);
  border-radius: var(--r-xs);
  color: var(--text-2);
  font-size: var(--t-small);
  transition: background var(--fast), color var(--fast);
}
.admin-nav a svg { width: 16px; height: 16px; opacity: .65; }
.admin-nav a:hover { background: var(--surface); color: var(--text); }
.admin-nav a.is-active { background: var(--surface); color: var(--text); box-shadow: none; }
.admin-nav a.is-active svg { opacity: 1; color: var(--accent); }
.admin-nav-cta { margin-top: var(--sp-3); background: var(--accent-wash); color: var(--accent) !important; }
.admin-nav-cta:hover { background: rgba(159, 140, 255, .17) !important; }

.admin-content { display: grid; gap: var(--sp-5); }
.admin-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-5); }
.admin-head h1 { font-size: var(--t-h2); font-weight: 600; }
.admin-head p { margin-top: var(--sp-2); color: var(--text-2); font-size: var(--t-small); }

.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: var(--sp-3); background: none; border: 0; }
.metric { padding: var(--sp-5); background: var(--surface); border: 0; border-radius: var(--r); }
.metric-value {
  display: block;
  margin: var(--sp-2) 0 var(--sp-1);
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.metric-sub { font-size: var(--t-caption); color: var(--text-3); }

.admin-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--sp-4); }
.admin-grid-sidebar { grid-template-columns: minmax(0, 1fr) 300px; align-items: start; }

.admin-panel { padding: var(--sp-5); }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.panel-head h2 { font-size: var(--t-h3); }
.panel-note, .panel-link { font-size: var(--t-caption); color: var(--text-3); }
.panel-link:hover { color: var(--accent); }

.bar-chart { display: flex; align-items: flex-end; gap: var(--sp-1); height: 140px; }
.bar-slot { flex: 1; display: flex; flex-direction: column; align-items: center; height: 100%; justify-content: flex-end; }
.bar { width: 100%; border-radius: var(--r-xs) var(--r-xs) 0 0; background: var(--accent-dim); transition: background var(--fast); }
.bar-slot:hover .bar { background: var(--accent); }
.bar-label { margin-top: var(--sp-2); font-size: var(--t-caption); color: var(--text-3); }

.rank-list, .stack-list { display: grid; gap: 1px; }
.rank-list li, .stack-list li {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  margin-inline: calc(var(--sp-3) * -1);
  border-radius: var(--r-xs);
  font-size: var(--t-small);
  transition: background var(--fast);
}
.rank-list li:hover, .stack-list li:hover { background: var(--surface-2); }
.rank-index { font-size: var(--t-caption); color: var(--text-3); width: 20px; flex-shrink: 0; font-variant-numeric: tabular-nums; }
.rank-list a, .stack-main a { color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-list a:hover, .stack-main a:hover { color: var(--accent); }
.rank-count { margin-left: auto; font-size: var(--t-caption); color: var(--text-3); font-variant-numeric: tabular-nums; }
.stack-main { display: grid; gap: 1px; min-width: 0; }
.stack-main span { font-size: var(--t-caption); }

.trail-list { display: grid; gap: var(--sp-3); }
.trail-list li { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--t-small); }
.trail-list code { color: var(--accent); }
.trail-target { color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trail-list .muted { margin-left: auto; font-size: var(--t-caption); white-space: nowrap; }

.toolbar { display: flex; gap: var(--sp-2); padding: var(--sp-4); }
.toolbar .input { flex: 1; }
.toolbar select.input { flex: 0 0 160px; }

.table-card { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--t-small); }
.data-table th {
  padding: var(--sp-3) var(--sp-4);
  border: 0;
  color: var(--text-3);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  white-space: nowrap;
}
.data-table td { padding: var(--sp-3) var(--sp-4); border: 0; vertical-align: middle; }
.data-table tbody tr { transition: background var(--fast); }
.data-table tbody tr:hover { background: var(--surface-2); }
.data-table tbody tr.is-highlighted { background: var(--accent-wash); box-shadow: none; }
.data-table tbody tr.is-banned { opacity: .5; }

.table-title { display: block; color: var(--text); font-weight: 500; }
a.table-title:hover { color: var(--accent); }
.table-sub { display: block; margin-top: 1px; font-size: var(--t-caption); color: var(--text-3); }
.table-file { font-family: var(--mono); font-size: var(--t-caption); }
.table-user { display: flex; align-items: center; gap: var(--sp-3); }
.table-actions { display: flex; align-items: center; gap: var(--sp-1); justify-content: flex-end; }
.table-actions form { display: inline; }

.form-columns { display: grid; grid-template-columns: minmax(0, 1fr) 316px; gap: var(--sp-4); align-items: start; }
.form-main, .form-side { display: grid; gap: var(--sp-4); }
.form-side { position: sticky; top: 84px; }
.form-section { padding: var(--sp-5); }
.form-section h2 { font-size: var(--t-h3); margin-bottom: var(--sp-5); }
.form-section .field:last-of-type { margin-bottom: 0; }
.form-submit { display: grid; gap: var(--sp-2); }
.upload-hint { text-align: center; }

/* Chunk progress. A single filled bar driven by a --pct custom property, so
   the script sets one value rather than touching layout. */
.upload-progress {
  height: 5px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  overflow: hidden;
}
.upload-progress::after {
  content: '';
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  border-radius: var(--r-pill);
  background: var(--accent);
  transition: width 200ms linear;
}

.admin-request-list { display: grid; gap: var(--sp-3); }
.admin-request { position: relative; padding: var(--sp-5); }
.admin-request-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); }
.admin-request-head h2 { font-size: var(--t-h3); }
.admin-request-details { margin-top: var(--sp-3); font-size: var(--t-small); color: var(--text-2); white-space: pre-wrap; max-height: 140px; overflow-y: auto; }
.admin-request-form { display: grid; grid-template-columns: 150px 1fr 84px; gap: var(--sp-3); align-items: start; margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--line-soft); }
.admin-request-form .field { margin-bottom: 0; }
.admin-request-note { grid-column: 1 / -1; }
.admin-request-actions { display: flex; align-items: flex-end; height: 100%; }
.admin-request-delete { position: absolute; right: var(--sp-5); bottom: var(--sp-5); }

/* --- Ban dialog ----------------------------------------------------------- */

.ban-dialog {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: var(--sp-5);
  background: rgba(4, 4, 6, .7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  overflow-y: auto;
}
.ban-dialog[hidden] { display: none; }
.ban-dialog-panel { width: min(520px, 100%); padding: var(--sp-6); border-radius: var(--r-lg); }
.ban-dialog-panel h2 { font-size: var(--t-h3); margin-bottom: var(--sp-1); }
.ban-dialog-panel > .field-hint { margin-bottom: var(--sp-5); }
.ban-scope-fields { border: 0; margin: var(--sp-1) 0 var(--sp-5); padding: 0; }
.ban-scope-fields legend { margin-bottom: var(--sp-2); padding: 0; }
.ban-scope-fields .checkbox { align-items: flex-start; padding: var(--sp-2) 0; }
.ban-scope-fields .checkbox input { margin-top: 3px; }
.ban-scope-fields .checkbox span { display: grid; gap: 2px; }
.ban-scope-fields .checkbox strong { font-size: var(--t-small); color: var(--text); font-weight: 500; }
.ban-scope-fields .checkbox em { font-style: normal; font-size: var(--t-caption); color: var(--text-3); line-height: 1.5; }

/* --- Legal ---------------------------------------------------------------- */

.legal-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--sp-8); align-items: start; padding-top: var(--sp-8); }
.legal-toc { position: sticky; top: 84px; display: grid; gap: var(--sp-3); }
.legal-toc nav { display: grid; gap: 1px; }
.legal-toc nav a {
  padding: 5px 10px;
  margin-inline: -10px;
  border-radius: var(--r-xs);
  color: var(--text-3);
  font-size: var(--t-small);
  transition: background var(--fast), color var(--fast);
}
.legal-toc nav a:hover { background: var(--surface); color: var(--text); }

.legal-doc { max-width: 680px; }
.legal-head { padding-bottom: var(--sp-6); margin-bottom: var(--sp-6); }
.legal-eyebrow { font-size: var(--t-caption); color: var(--accent); letter-spacing: 0; text-transform: none; }
.legal-head h1 { margin-top: var(--sp-3); font-size: var(--t-h1); font-weight: 600; }
.legal-updated { margin-top: var(--sp-3); font-size: var(--t-small); color: var(--text-3); }

.legal-callout {
  position: relative;
  padding: var(--sp-5);
  margin-bottom: var(--sp-8);
  border: 0;
  border-radius: var(--r);
  background: var(--surface);
  font-size: var(--t-small);
  color: var(--text-2);
  line-height: 1.6;
}
.legal-callout strong { color: var(--text); font-weight: 600; }
.legal-callout-sm { margin: var(--sp-1) 0 var(--sp-5); font-size: var(--t-caption); padding: var(--sp-4); }

.legal-doc section { margin-bottom: var(--sp-8); scroll-margin-top: 90px; }
.legal-doc h2 { font-size: var(--t-h3); margin-bottom: var(--sp-4); }
.legal-doc h3 { font-size: var(--t-body); font-weight: 600; margin: var(--sp-5) 0 var(--sp-2); }
.legal-doc p { margin-bottom: var(--sp-4); font-size: var(--t-body); color: var(--text-2); line-height: 1.7; }
.legal-doc p:last-child { margin-bottom: 0; }

.legal-list { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.legal-list li { position: relative; padding-left: var(--sp-5); font-size: var(--t-body); color: var(--text-2); line-height: 1.65; }
.legal-list li::before {
  content: '';
  position: absolute;
  left: 0; top: .68em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent-dim);
}
.legal-list strong { color: var(--text); font-weight: 600; }

/*
  Term/definition pairs. The label carries no mono, no dashed rule and no
  colour — the grid alone expresses the relationship, which is what a
  definition list is for.
*/
.def-list { margin: 0 0 var(--sp-5); display: grid; gap: var(--sp-4); }
.def-list > div {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: var(--sp-5);
  border: 0;
  padding: 0;
}
.def-list dt { font-size: var(--t-body); color: var(--text); font-weight: 500; line-height: 1.6; }
.def-list dt code { color: inherit; font-size: .9em; }
.def-list dd { margin: 0; font-size: var(--t-body); color: var(--text-2); line-height: 1.65; }

.legal-missing { padding: var(--sp-4); border-radius: var(--r-sm); background: var(--surface); color: var(--text-3) !important; font-size: var(--t-small) !important; }
.legal-foot { margin-top: var(--sp-8); padding-top: var(--sp-5); border-top: 1px solid var(--line-soft); }
.legal-foot p { font-size: var(--t-small); color: var(--text-3); }

/* --- 404 ------------------------------------------------------------------ */

.notfound {
  padding: var(--sp-10) 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 640px;
  margin-inline: auto;
}
.notfound-art, .notfound-console { display: none; }

.notfound h1 { font-size: var(--t-display); font-weight: 600; letter-spacing: -.038em; line-height: 1.05; }
.notfound-lead { margin-top: var(--sp-5); max-width: 44ch; color: var(--text-2); font-size: var(--t-lead); line-height: 1.45; }
.notfound-search { margin-top: var(--sp-7); width: min(440px, 100%); }

.notfound-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-5);
  width: 100%;
  margin-top: var(--sp-6);
  background: none;
  border: 0;
}
.notfound-link { display: inline-flex; padding: 0; background: none; color: var(--accent); font-size: var(--t-small); font-weight: 500; }
.notfound-link:hover { color: var(--accent-soft); }
.notfound-link strong { font-weight: 500; }
.notfound-link span { display: none; }

/* --- Error page ----------------------------------------------------------- */

/*
  Never had any rules, so a 500 or a 403 rendered as unstyled markup on top of
  a styled header — the worst possible moment to look broken. Same treatment as
  the 404, one step quieter.
*/
.error-page {
  padding: var(--sp-10) var(--sp-5);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 620px;
  margin-inline: auto;
}
.error-code { font-size: var(--t-small); color: var(--text-3); font-variant-numeric: tabular-nums; }
.error-page h1 { margin-top: var(--sp-3); font-size: var(--t-h1); font-weight: 600; text-wrap: balance; }
.error-message { margin-top: var(--sp-4); max-width: 44ch; color: var(--text-2); font-size: var(--t-lead); line-height: 1.45; }
.error-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-3); margin-top: var(--sp-7); }

/* --- Ban screen ----------------------------------------------------------- */

.banned-page { background: var(--bg); }
.ban-wrap { flex: 1; display: grid; place-items: center; padding: var(--sp-9) var(--sp-5); }
.ban-card {
  width: min(560px, 100%);
  padding: var(--sp-7);
  border: 0;
  border-radius: var(--r-lg);
  background: var(--surface);
}
.ban-icon {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  margin-bottom: var(--sp-5);
  border: 0;
  border-radius: 50%;
  background: var(--rust-wash);
  color: var(--rust);
}
.ban-icon svg { width: 21px; height: 21px; }
.ban-eyebrow { color: var(--rust); }
.ban-card h1 { margin-top: var(--sp-3); font-size: var(--t-h2); font-weight: 600; }
.ban-lead { margin-top: var(--sp-4); color: var(--text-2); font-size: var(--t-body); line-height: 1.6; }

.ban-details { margin: var(--sp-6) 0 0; display: grid; font-size: var(--t-small); }
.ban-details > div { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: var(--sp-4); padding: 7px 0; }
.ban-details dt { color: var(--text-3); letter-spacing: 0; }
.ban-details dd { margin: 0; color: var(--text); }
.ban-ago { color: var(--text-3); }
.ban-scope { color: var(--rust) !important; }

.ban-appeal { margin-top: var(--sp-6); padding-top: var(--sp-6); border-top: 1px solid var(--line-soft); }
.ban-appeal h2 { font-size: var(--t-h3); margin-bottom: var(--sp-2); }
.ban-appeal p { font-size: var(--t-small); color: var(--text-2); margin-bottom: var(--sp-2); }
.ban-note { font-size: var(--t-caption) !important; color: var(--text-3) !important; }

.ban-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-top: var(--sp-6); padding-top: var(--sp-4); border-top: 1px solid var(--line-soft); font-size: var(--t-caption); color: var(--text-3); }
.ban-foot a { color: var(--text-3); }
.ban-foot a:hover { color: var(--accent); }

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

.site-footer { border-top: 1px solid var(--line-soft); background: var(--bg-sunk); padding-top: var(--sp-8); }
.footer-inner { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: var(--sp-8); padding-bottom: var(--sp-7); }
.footer-brand { display: grid; gap: var(--sp-4); justify-items: start; align-content: start; }
.footer-brand p { max-width: 38ch; font-size: var(--t-small); color: var(--text-3); line-height: 1.6; }

.footer-links { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-5); }
.footer-links h4 { margin-bottom: var(--sp-3); font-size: var(--t-caption); color: var(--text-3); letter-spacing: 0; text-transform: none; font-weight: 500; }
.footer-links a { display: block; padding: 5px 0; font-size: var(--t-small); color: var(--text-2); }
.footer-links a:hover { color: var(--accent); }

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  padding-top: var(--sp-5);
  padding-bottom: var(--sp-6);
  border-top: 1px solid var(--line-soft);
  font-size: var(--t-caption);
  color: var(--text-3);
}
.footer-note { max-width: 58ch; text-align: right; }

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

@media (max-width: 1080px) {
  .browse-layout, .detail-layout, .form-columns, .admin-grid-sidebar, .legal-layout { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
  .filter-rail, .detail-side, .form-side, .admin-nav, .legal-toc { position: static; }
  .admin-layout { grid-template-columns: minmax(0, 1fr); }
  .admin-nav, .legal-toc nav { grid-auto-flow: column; grid-auto-columns: max-content; overflow-x: auto; padding-bottom: var(--sp-1); }
  .admin-nav a, .legal-toc nav a { margin-inline: 0; }
  .admin-nav-cta { margin-top: 0; }
  .admin-request-form { grid-template-columns: minmax(0, 1fr); }
  .admin-request-delete { position: static; margin-top: var(--sp-3); }
}

@media (max-width: 860px) {
  .header-inner { gap: var(--sp-4); }
  .header-search { display: none; }
  .site-nav { margin-left: auto; }
  .footer-inner { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: var(--sp-2); }
  .footer-note { text-align: left; }
  .def-list > div { grid-template-columns: minmax(0, 1fr); gap: var(--sp-1); }
}

@media (max-width: 640px) {
  .shell { padding-inline: var(--sp-4); }
  .masthead { padding-top: var(--sp-9); }
  .masthead-copy { max-width: none; }

  /*
    A brand, three nav links and two actions cannot share 358px without one of
    them being clipped mid-word. Two rows instead: identity and actions above,
    navigation below. Nothing is hidden and nothing is abbreviated.
  */
  .header-inner {
    flex-wrap: wrap;
    height: auto;
    min-height: 0;
    padding-block: var(--sp-3);
    gap: var(--sp-3);
    row-gap: var(--sp-2);
  }
  .brand { order: 1; }
  .header-actions { order: 2; margin-left: auto; }
  .site-nav { order: 3; flex-basis: 100%; margin-left: calc(var(--sp-3) * -1); overflow-x: auto; scrollbar-width: none; }
  .site-nav::-webkit-scrollbar { display: none; }
  .site-nav a { white-space: nowrap; }

  .res-row { grid-template-columns: 44px minmax(0, 1fr); gap: var(--sp-1) var(--sp-4); }
  .res-row::before { inset: 2px calc(var(--sp-3) * -1); }
  .res-thumb { width: 44px; height: 44px; align-self: start; }
  /* Reads as one more line of metadata rather than an orphaned right column. */
  .res-meta { grid-column: 2; margin-top: var(--sp-1); justify-content: flex-start; text-align: left; }
  .res-summary { -webkit-line-clamp: 2; line-clamp: 2; }

  /* Explicit placement: the source order is name, description, count, and the
     description needs to drop to its own row without displacing the count. */
  .cat-cell { grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-1) var(--sp-4); }
  .cat-name { grid-area: 1 / 1; }
  .cat-count { grid-area: 1 / 2; }
  .cat-desc { grid-area: 2 / 1 / 3 / 3; }

  .footer-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .admin-head, .page-head-split { flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
  .results-bar { flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
  .user-chip-name { display: none; }
  .request-card { grid-template-columns: 46px minmax(0, 1fr); gap: var(--sp-4); padding: var(--sp-4); }
  .field-row { grid-template-columns: minmax(0, 1fr); }
  .toolbar { flex-wrap: wrap; }
  .toolbar select.input { flex: 1 1 100%; }
}

/*
  boot.js already refuses to add `.has-motion` under reduced motion, so nothing
  is pre-hidden and GSAP is never handed anything to animate. This is the belt
  to that braces: it flattens the plain CSS transitions too.
*/
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- Recovery ------------------------------------------------------------- */

.auth-forgot { margin: calc(var(--sp-3) * -1) 0 var(--sp-5); text-align: right; }
.auth-forgot a { font-size: var(--t-caption); color: var(--text-3); }
.auth-forgot a:hover { color: var(--accent); }

.notice {
  padding: var(--sp-4);
  border: 0;
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: var(--t-small);
  color: var(--text-2);
  line-height: 1.6;
}
.notice strong { display: block; color: var(--text); margin-bottom: var(--sp-1); }

.reset-dead { font-size: var(--t-small); color: var(--text-2); margin-bottom: var(--sp-5); line-height: 1.6; }
