/* =========================================================================
   swag.black — fax transmission / dossier
   Brand colours unchanged: #D6E7F3 on #0C0E0B. Added 2026-08-12: --deep-
   violet (#5a26d3) as a second, stronger signal colour, used only where the
   design calls for real visual weight (hero backdrop, top-10 index numbers,
   support-us section) — everything else keeps the original --violet #8466f2.

   Direction: the page reads like a degraded fax sheet or a photocopied
   press dossier -- documentary, not corporate: hard edges (no radius, no
   shadow, no glow), mono micro-labels, perforation rules, and a heavy
   overlay-blended grain over everything (2026-08-13: switched from plain
   opacity to mix-blend-mode: overlay, which is what actually keeps black
   black instead of reading as grey -- see body::after below). The masthead
   and transmission strip sit on --bg-deep, a shade darker than the page
   body, so the header reads as its own panel. The hero is the one
   deliberate exception to the flat-black rule: a full-bleed dithered
   gradient behind the top post, the loudest moment on the page by design.
   No numbered section indices (01/02/...) anywhere -- removed 2026-08-13,
   they read as filler, not structure.

   Display face: Courbe Sans -- the live font, kept. Wosker was tried and
   reverted 2026-08-12: its DEMO cut watermarks every digit glyph (0-9 all
   share one placeholder shape, confirmed by drawing the outlines, not just
   checking cmap), which breaks "Top 10", dates, and every dollar figure on
   a site built on numbers. wosker.woff2 stays on disk -- see
   assets/fonts/README.md if a licensed copy is ever bought.

   Constraints from the display face (Courbe Sans):
     1. It has ONE weight, and it is already heavy. font-synthesis is off so
        no browser fakes a bolder cut; display text is always weight 400.
     2. It is UPPERCASE-ONLY — lowercase codepoints render the same capital
        forms. Headlines are therefore caps whatever the source text says.
        Sizes below are pulled down from the previous face accordingly: caps
        carry far more visual mass per em, and measures are set in fewer ch.
     3. It lacks "-", "&", "%", "$", "+", "=". Those fall through to IBM Plex
        Sans mid-word, so the fallback in --font-display must stay.
   ========================================================================= */

@font-face {
  font-family: "Courbe Sans";
  src: url("/assets/fonts/courbesans.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  /* Shipped whole (7 KB) rather than subset: the licence forbids derivative
     versions, so the file is format-converted for the web and nothing else.
     It is missing "-", "&", "%", "$", "+" and "=" -- the stack below renders
     those from IBM Plex Sans, which is why the fallback is not optional. */
}

:root {
  /* ground — black-forward, panels barely lifted off it */
  --bg: #0c0e0b;
  --bg-deep: #050605;
  --surface: #0f110e;
  --surface-2: #121512;

  /* text */
  --ink: #f4f7f9;
  --ink-soft: #a9b0ae;
  --muted: #6e7671;
  --faint: #4a514c;

  /* structure */
  --rule: #1d201c;
  --rule-strong: #2b2f2a;

  /* brand */
  --accent: #d6e7f3;
  --accent-a10: rgba(214, 231, 243, 0.10);
  --accent-a20: rgba(214, 231, 243, 0.20);
  --accent-a45: rgba(214, 231, 243, 0.45);

  /* violet — signal, never the voice */
  --violet: #8466f2;
  --violet-soft: #a68eff;
  --violet-a15: rgba(132, 102, 242, 0.15);

  /* deep violet — the new, stronger signal colour. Reserved for the hero
     backdrop, top-10 index numerals and the support-us section; never body text. */
  --deep-violet: #5a26d3;
  --deep-violet-soft: #8c5ce8;
  --deep-violet-a15: rgba(90, 38, 211, 0.15);
  --deep-violet-a30: rgba(90, 38, 211, 0.3);

  --max: 1180px;

  --font-display: "Courbe Sans", "IBM Plex Sans", "Segoe UI", sans-serif;
  --font-body: "IBM Plex Sans", "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
}

* { box-sizing: border-box; }

/* overflow-x: clip kills the horizontal scrollbar the full-bleed sections
   (.masthead/.transmission/.hero) would otherwise cause. Those use the
   standard `width: 100vw; margin-left: calc(50% - 50vw)` break-out, and
   100vw INCLUDES the scrollbar width while the content box does not -- so
   on any desktop with a visible scrollbar the page overflowed by ~8px and
   could be scrolled sideways. `clip` rather than `hidden`: hidden on one
   axis forces the other axis to `auto`, which creates a second scroll
   container and breaks position:sticky; clip has no such side effect. */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
body { overflow-x: clip; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* Never let the browser fake a weight or slant for the display face. */
body { font-synthesis-weight: none; font-synthesis-style: none; }

a { color: inherit; }

/* ---------- photocopy grain -------------------------------------------
   A single fixed, non-interactive layer. Self-contained SVG turbulence,
   so it costs no request.

   2026-08-12, second pass: the first bump (0.035 -> 0.11 opacity, default
   blend mode) was the actual cause of "the site looks grey, not black" --
   default blending is additive, so an ~50%-grey noise texture at 11%
   opacity lightens EVERY black pixel uniformly, including the header,
   whether or not any grain is visible there as texture. Fixed by switching
   to mix-blend-mode: overlay (same technique already used on .hero::after):
   overlay leaves true black and true white untouched and only adds
   contrast in the midtones, so it can go much stronger -- opacity 0.11 ->
   0.4 -- while true black areas (background, header) actually get BLACKER,
   not greyer. Also swapped in the same feComponentTransfer contrast boost
   used on the hero, so the grain reads as distinct specks instead of a
   soft haze -- this is the "multiply the grain" ask.

   2026-08-13, third pass: the grain was sitting at z-index 9 while .frame
   (all the actual content) sits at z-index 10 -- so it only ever textured
   the bare background BEHIND the content, never the cards, headlines or
   body copy. That is why the page still read as clean/flat despite the
   opacity bump: on a layout where content covers most of the viewport,
   almost none of the grain was visible. Moved above the content (z-index
   11) so it lies over everything, which is what a photocopy/fax texture
   has to do to read as one. Opacity pulled back 0.4 -> 0.3 to compensate:
   overlay leaves near-white headline text essentially untouched (a base
   of 0.96 luma varies only ~0.91-1.0 under overlay) but swings midtone
   body copy much harder, so above-content grain has to be gentler than
   background-only grain to stay legible. pointer-events stays none so the
   layer never eats a click. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 11;
  pointer-events: none;
  opacity: 0.3;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='2' intercept='-0.5'/%3E%3CfeFuncG type='linear' slope='2' intercept='-0.5'/%3E%3CfeFuncB type='linear' slope='2' intercept='-0.5'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.frame { position: relative; z-index: 10; }

.frame {
  width: min(var(--max), 92vw);
  margin: 0 auto;
  padding: 0 0 6rem;
}

/* ---------- shared micro-typography ------------------------------------ */
.kicker,
.story-num,
time {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------- perforation rule -------------------------------------------
   A tear-off edge, drawn as a dash pattern rather than `border: dashed`
   so the dash length and gap are exact and identical at every zoom. */
.perf {
  height: 1px;
  border: 0;
  margin: 0;
  background-image: repeating-linear-gradient(
    90deg,
    var(--rule-strong) 0 6px,
    transparent 6px 12px
  );
}

/* ---------- masthead ----------------------------------------------------
   background var(--bg-deep) (#050605) rather than plain var(--bg)
   (#0c0e0b): the two are close enough that side-by-side they don't read as
   separate panels, but --bg-deep is measurably closer to true black --
   this is what makes the header read as black instead of a grey band,
   independent of the grain fix above. Full-bleed like .hero, so the panel
   reaches the viewport edge instead of stopping at .frame's padding. */
.masthead {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background: var(--bg-deep);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  padding: 2.4rem max(1.5rem, calc((100vw - var(--max)) / 2)) 1.3rem;
}

.logo {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.75rem, 3.4vw, 2.45rem);
  letter-spacing: 0.005em;
  line-height: 1;
  /* No text-transform: the face has only capitals, so lowercasing the
     wordmark would change nothing but the copied text. */
  text-decoration: none;
  color: var(--ink);
}

.logo em { font-style: normal; color: var(--accent); }

.masthead nav {
  display: flex;
  gap: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.masthead nav a {
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: color 140ms ease, border-color 140ms ease;
}
.masthead nav a:hover { color: var(--accent); border-bottom-color: var(--accent-a45); }

/* transmission strip — a static document header, deliberately NOT a live
   ticker: no scrolling, no status pill, no counts. It is structure.
   Same --bg-deep full-bleed panel as .masthead, so the two read as one
   header block rather than a stray band appearing only around the logo. */
.transmission {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background: var(--bg-deep);
  display: flex;
  gap: 1.2rem;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem max(1.5rem, calc((100vw - var(--max)) / 2));
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.transmission span { white-space: nowrap; }
.transmission .sig { color: var(--violet-soft); }

.tagline {
  width: 100%;
  margin: 0.55rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
}

/* ---------- lead ------------------------------------------------------
   Deliberately NOT boxed. The contrast between an open, full-width lead
   and the bordered modules below is what stops the page reading as a
   grid of cards. */
.lead { padding: 3rem 0 2.8rem; }

.kicker {
  margin: 0 0 1.2rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}

.kicker::before {
  content: "";
  width: 1.8rem;
  height: 2px;
  flex: none;
  background: var(--violet);
}

.lead h1,
.lead h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.05rem, 5.1vw, 3.75rem);
  line-height: 0.98;          /* caps have no descenders — tighter closes the gap */
  letter-spacing: 0.004em;
  max-width: 18ch;            /* caps are wider; ch is measured on "0", so this
                                 runs narrower than it reads — 15ch stranded
                                 single words on their own line */
}

.lead h2 a { text-decoration: none; transition: color 160ms ease; }
.lead h2 a:hover { color: var(--accent); }

.lead .standfirst {
  margin: 1.5rem 0 0;
  max-width: 54ch;
  font-size: 1.14rem;
  line-height: 1.64;
  color: var(--ink-soft);
}

.byline {
  margin-top: 1.7rem;
  display: flex;
  gap: 1.3rem;
  flex-wrap: wrap;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.byline a { color: var(--accent); text-decoration: none; }
.byline a:hover { color: var(--ink); }

/* =========================================================================
   Story list
   ========================================================================= */
.stories { display: grid; gap: 0; }

.story {
  position: relative;
  z-index: 0;
  display: grid;
  grid-template-columns: 3.6rem 1fr;
  gap: 0 1.3rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
}

.story:last-child { border-bottom: 0; }

/* Violet, not the pale-blue --accent-a10 this used before: every other
   interactive accent on the page moved to deep violet, and a blue wash
   here was the last thing still hovering in the old colour.

   The gradient lives on a full-bleed ::before (same 100vw break-out as
   .hero/.masthead) rather than directly on .story's own background --
   .story is constrained inside .frame (centered, max 1180px), so a plain
   background on it stopped short of the real browser edge instead of
   reaching all the way to the side like every other full-bleed panel on
   the page. z-index: 0 on .story traps the pseudo's z-index: -1 within
   its own stacking context so it can't sink below unrelated page content. */
.story::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 50vw);
  width: 100vw;
  z-index: -1;
  background: linear-gradient(90deg, var(--deep-violet-a15), transparent 60%);
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
}
.story:hover::before { opacity: 1; }
.story:hover h3 { color: var(--deep-violet-soft); }

.story-num {
  /* the ranking is the point of this list, so the index number carries the
     new deep-violet accent and reads at near-headline size — everything
     else in .story stays on the original palette. */
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--deep-violet-soft);
  padding-top: 0.1rem;
}

.story h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.1rem, 1.75vw, 1.34rem);
  line-height: 1.1;
  letter-spacing: 0.008em;
  transition: color 160ms ease;
  max-width: 31ch;
}

.story p {
  margin: 0.65rem 0 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
  line-height: 1.58;
  max-width: 58ch;
}

.story time {
  display: block;
  margin-top: 0.8rem;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- footer ----------------------------------------------------- */
.site-foot {
  margin-top: 4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule-strong);
  display: flex;
  justify-content: space-between;
  gap: 1.3rem;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* =========================================================================
   Article
   ========================================================================= */
/* Centred measure. Left-aligning a 44rem column inside the 1180px frame
   stranded ~475px of dead space down the right of every article. */
.article { padding: 2.8rem 0 0; max-width: 44rem; margin-inline: auto; }

.article-head { padding-bottom: 1.8rem; margin-bottom: 2.4rem; border-bottom: 1px solid var(--rule-strong); }

.article-head h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.85rem, 4.2vw, 2.9rem);
  line-height: 1.0;
  letter-spacing: 0.005em;
  max-width: 17ch;
}

.article-head time {
  display: block;
  margin-top: 1.2rem;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.prose { font-size: 1.06rem; line-height: 1.76; color: var(--ink-soft); }
.prose > p:first-of-type { font-size: 1.18rem; line-height: 1.66; color: var(--ink); }
.prose p { margin: 0 0 1.45rem; }

.prose h2,
.prose h3 {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: 0.01em;
  line-height: 1.12;
  margin: 2.8rem 0 1rem;
}

.prose h2 { font-size: 1.5rem; }
.prose h3 { font-size: 1.18rem; }

.prose strong { color: var(--ink); font-weight: 600; }
.prose a { color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--accent-a45); }
.prose a:hover { border-bottom-color: var(--accent); }

.prose ul, .prose ol { margin: 0 0 1.45rem; padding-left: 1.2rem; }
.prose li { margin: 0.45rem 0; }
.prose li::marker { color: var(--violet-soft); }

.prose blockquote {
  margin: 2.1rem 0;
  padding: 0.2rem 0 0.2rem 1.4rem;
  border-left: 2px solid var(--violet);
  color: var(--ink);
  font-size: 1.14rem;
  line-height: 1.58;
}

.prose code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  padding: 0.1em 0.36em;
}

.back {
  display: inline-block;
  margin: 3rem 0 0;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  width: 100%;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  text-decoration: none;
}
.back:hover { color: var(--ink); }

/* =========================================================================
   Responsive
   ========================================================================= */
@media (max-width: 900px) {
  .lead { padding: 2.4rem 0 2.2rem; }
  .story { grid-template-columns: 2.6rem 1fr; gap: 0 1rem; }
}

@media (max-width: 620px) {
  .frame { width: 90vw; }
  .masthead { padding: 1.9rem 0 1.1rem; }
  .masthead nav { gap: 1rem; font-size: 0.72rem; }
  .transmission { font-size: 0.62rem; letter-spacing: 0.1em; }
  .story { grid-template-columns: 1fr; }
  .story-num { display: none; }
  .site-foot { flex-direction: column; gap: 0.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* Grain is decorative texture; drop it when the user asks for less. */
@media (prefers-contrast: more) {
  body::after { display: none; }
}

/* =========================================================================
   HERO — flagship post (highest view count; falls back to most recent
   while the counter is still empty — see build_swagblack.php's ORDER BY).

   Full-bleed dithered gradient: a radial deep-violet glow dissolving into
   black, with a grain layer on top so the colour transition looks like
   sprayed pigment rather than a flat CSS blend (matches the Ristle/gradient
   references — noisy violet-to-black, not a clean gradient). This is the
   one section on the page allowed to break the flat-black documentary
   rule; everywhere else keeps the fax-dossier restraint.
   ========================================================================= */
.hero {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding: 5rem max(1.5rem, calc((100vw - var(--max)) / 2)) 4.2rem;
  overflow: hidden;
  isolation: isolate;
  /* the colour: two radial blooms of deep-violet dissolving to black,
     positioned like the reference images (glow upper-right, fading to
     flat black lower-left) rather than a centred, symmetrical blend.

     2026-08-13: moved here directly from a ::before pseudo-element
     (position: absolute; inset: 0; z-index: -2). That combination --
     absolutely-positioned layer, negative z-index, isolation: isolate --
     is the exact one already diagnosed as unreliable on .support-us
     ("known to render inconsistently across browsers/GPU compositors...
     collapsed to nothing"), and it failed the same way here: the gradient
     blob visibly disappeared partway down .hero when scrolling past it.
     A plain background directly on the element cannot fail that way --
     same fix, same reasoning as .support-us below. */
  background:
    radial-gradient(ellipse 60% 55% at 78% 12%, var(--deep-violet) 0%, transparent 62%),
    radial-gradient(ellipse 70% 60% at 15% 35%, var(--deep-violet-a30) 0%, transparent 58%),
    #050307;
}

.hero::after {
  /* the dither: heavier turbulence than the sitewide photocopy grain
     (body::after), so the gradient reads as sprayed noise instead of a
     smooth CSS blend — this is what makes it look like the reference
     images rather than a generic radial-gradient hero. Bumped 2026-08-12
     at the user's request: opacity 0.5 -> 0.9, plus a feComponentTransfer
     contrast boost (slope 2.4 / intercept -0.7) so the turbulence reads as
     distinct light/dark specks instead of a soft blur -- the smooth
     version undershot the reference images, which show real per-pixel
     grain, not a hazy overlay.

     2026-08-13: z-index flipped from -1 (behind .hero's own text, inside
     the isolated stacking context) to 1 (plainly above it) -- same
     negative-z-index-on-an-absolute-layer pattern implicated in the
     gradient fix above, and body::after already proved the "grain sits
     above content, positive z-index" version works (see that rule).
     pointer-events: none is now load-bearing, not decorative: the grain
     sits on top of .hero h2's link and the byline link, and without it
     those become unclickable. Opacity pulled back 0.9 -> 0.5 to compensate
     -- body::after needed the same kind of reduction (0.4 -> 0.3) when it
     moved above content, for the same reason: overlay leaves near-white
     headline text (.hero h1/h2) essentially untouched, but hits the
     mid-tone .standfirst copy much harder once it's above instead of
     behind. Worth a visual check after deploy in case it needs further
     tuning either direction. */
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0.5;
  mix-blend-mode: overlay;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='hn'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='2.4' intercept='-0.7'/%3E%3CfeFuncG type='linear' slope='2.4' intercept='-0.7'/%3E%3CfeFuncB type='linear' slope='2.4' intercept='-0.7'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23hn)'/%3E%3C/svg%3E");
}

.hero .kicker { color: var(--accent); }
.hero .kicker::before { background: var(--deep-violet-soft); }

.hero h1,
.hero h2 {
  position: relative;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.4rem, 6.4vw, 4.6rem);
  line-height: 0.96;
  letter-spacing: 0.004em;
  max-width: 18ch;
  color: var(--ink);
}

.hero h2 a { text-decoration: none; transition: color 160ms ease; }
.hero h2 a:hover { color: var(--accent); }

.hero .standfirst {
  position: relative;
  margin: 1.5rem 0 0;
  max-width: 54ch;
  font-size: 1.14rem;
  line-height: 1.64;
  color: var(--ink-soft);
}

.hero .byline { position: relative; }
.hero .byline a { color: var(--deep-violet-soft); }
.hero .byline a:hover { color: var(--ink); }

@media (prefers-contrast: more) {
  .hero::after { display: none; }
}

/* =========================================================================
   SECTION HEAD
   Bumped 2026-08-13: everything here used to differentiate itself from the
   black page with a 1px, 15%-opacity violet line -- correct in theory, but
   against near-black-on-near-black it reads as no boundary at all. Every
   rule below is now full-opacity colour or a real fill, not a faint tint.
   ========================================================================= */
.section-head {
  padding: 2.4rem 0 1.2rem;
  border-top: 1px solid var(--rule-strong);
  display: flex;
  align-items: center;
  gap: 0.8rem;
}

/* Replaces the removed 01/02 numeral: a plain colour block, not a label --
   a graphic anchor for the eye without reviving "numbered sections." */
.section-head::before {
  content: "";
  width: 0.55rem;
  height: 1.3rem;
  flex: none;
  background: var(--deep-violet);
}

.sec-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.3rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}


/* =========================================================================
   CATEGORIES GRID
   ========================================================================= */
.categories { padding: 1rem 0 3rem; }

.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1rem;
}

.cat-card {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  padding: 1.5rem 1.2rem 1.4rem;
  text-decoration: none;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  transition: border-color 160ms ease, background 160ms ease, transform 160ms ease;
}

/* Top accent bar, full-strength colour -- on every card, not just on
   hover, so the grid reads as branded at rest, not just on interaction. */
.cat-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--deep-violet);
}

.cat-card:hover {
  border-color: var(--deep-violet-soft);
  background: var(--surface-2);
  transform: translateY(-2px);
}

.cat-icon {
  width: 2.2rem;
  height: 2.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  color: var(--deep-violet-soft);
  background: var(--deep-violet-a15);
  border: 1px solid var(--deep-violet-a30);
}

.cat-label {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.cat-desc {
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

/* Live story count -- pushed to the card's bottom edge so cards with
   different description lengths still line their counts up. */
.cat-count {
  margin-top: auto;
  padding-top: 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* =========================================================================
   CATEGORY PAGE HEADER
   Same shape as the article header, one step down in scale from .hero --
   a category page is a landing page, not the front page.
   ========================================================================= */
.cat-head {
  padding: 3rem 0 2.4rem;
  border-bottom: 1px solid var(--rule-strong);
  margin-bottom: 0.6rem;
}

.cat-head h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2rem, 4.6vw, 3.1rem);
  line-height: 1;
  letter-spacing: 0.004em;
  color: var(--ink);
}

.cat-head .standfirst {
  margin: 1.1rem 0 0;
  max-width: 54ch;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* The article kicker becomes a link back up to its category. Keep it
   looking like the kicker it replaced, only interactive. */
.kicker a {
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 140ms ease, border-color 140ms ease;
}
.kicker a:hover {
  color: var(--deep-violet-soft);
  border-bottom-color: var(--deep-violet-soft);
}

/* =========================================================================
   SUPPORT US — one single strip, deliberately (not two separate promos).
   Standalone section, not a card inside the categories grid, so it reads
   as its own module rather than a fifth category.

   2026-08-13, third pass: the previous version used the same trick as
   .hero -- an absolutely-positioned ::before/::after pair with negative
   z-index, isolation: isolate, and mix-blend-mode: overlay for the grain.
   That combination is known to render inconsistently across browsers/GPU
   compositors -- confirmed live: it rendered correctly in one browser and
   collapsed to nothing (zero visible content, no gap in page flow that
   the eye could even attribute to a bug) in the user's. Rather than chase
   a specific engine bug, dropped the whole pseudo-element + blend-mode
   approach here and replaced it with a plain gradient background directly
   on the element -- no absolute positioning, no isolation, no blend mode.
   Less fancy, but it cannot fail this way. .hero keeps the fancier version
   since it's independently confirmed working. */
.support-us {
  border: 2px solid var(--deep-violet);
  background:
    linear-gradient(135deg, var(--deep-violet-a30), rgba(90, 38, 211, 0.04) 55%),
    var(--bg-deep);
  padding: 1.8rem 1.8rem 2rem;
  display: grid;
  gap: 1.2rem;
}

.support-us-head {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
}

.support-us-icon { font-size: 1.2rem; color: var(--deep-violet-soft); }

.support-us-label {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.support-us p {
  margin: 0;
  max-width: 62ch;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.support-us-links {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

.support-us-link {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.9rem 1.05rem;
  border: 1px solid var(--deep-violet-a30);
  background: rgba(5, 6, 5, 0.55);
  text-decoration: none;
  transition: border-color 160ms ease, background 160ms ease, transform 160ms ease;
}

.support-us-link:hover {
  border-color: var(--deep-violet-soft);
  background: rgba(5, 6, 5, 0.75);
  transform: translateY(-1px);
}

.support-us-link strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1rem;
  letter-spacing: 0.02em;
  color: var(--accent);
}

.support-us-link span {
  font-size: 0.76rem;
  color: var(--ink-soft);
}

@media (max-width: 620px) {
  .support-us-links { grid-template-columns: 1fr; }
}

/* =========================================================================
   EMPTY STATE
   ========================================================================= */
.empty {
  padding: 2rem 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.82rem;
}
