/* =========================================================================
   northlandhorror.co.nz
   Hand-written. No framework, no CDN, no third-party request at runtime.

   Reading order:
     1. tokens        2. reset       3. type        4. effects layer
     5. header/nav    6. buttons     7. sections    8. components
     9. forms        10. footer     11. dash       12. motion + print
   ========================================================================= */

/* ------------------------------------------------------------- 1. tokens */

@font-face {
  font-family: 'Anton';
  src: url('/assets/fonts/anton.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  /* Latin Extended-A. Carries the macrons - Whangārei, whānau, tamariki, Ngā. Without this
     block those words fall back mid-heading and the line visibly changes typeface. */
  font-family: 'Anton';
  src: url('/assets/fonts/anton-ext.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
                 U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  /* Creepster ships LATIN ONLY - it has no macrons. Use it for fixed decorative strings that
     you have personally checked, never for headings that may contain te reo or client copy. */
  font-family: 'Creepster';
  src: url('/assets/fonts/creepster.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --ink:        #08070a;
  --ink-2:      #0d0b12;
  --ink-3:      #14111c;
  --ink-line:   #241f31;

  --bone:       #ece7dd;
  --bone-dim:   #a49c93;
  --bone-faint: #6d6660;

  --blood:      #a3121b;
  --blood-hot:  #e11d26;
  --blood-deep: #4a070c;

  --toxic:      #7ce23a;   /* the green off their own event poster */
  --amber:      #ff8a1f;
  --violet:     #7b4bd6;   /* the witch's orb */

  --font-display: 'Anton', 'Arial Narrow', 'Haettenschweiler', sans-serif;
  --font-drip:    'Creepster', 'Anton', cursive;
  --font-body:    ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --wrap:    76rem;
  --wrap-nr: 58rem;         /* narrow: reading measure */
  --gut:     clamp(1.25rem, 4vw, 3rem);
  --sec:     clamp(4.5rem, 11vw, 9rem);

  --hdr-h:   10.6rem;   /* logo 9.2rem + .7rem padding top and bottom */
  --radius:  2px;           /* almost none. Rounded corners read friendly, not haunted. */
  --ease:    cubic-bezier(.22, 1, .36, 1);

  --shadow-lift: 0 1.5rem 3rem -1rem rgb(0 0 0 / .85);
  --glow-blood:  0 0 2.5rem -.5rem rgb(225 29 38 / .55);
  --glow-toxic:  0 0 2.5rem -.5rem rgb(124 226 58 / .45);
}

/* -------------------------------------------------------------- 2. reset */

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

html {
  background: var(--ink);
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
}
html.js.is-locked, html.is-navopen { overflow: hidden; }

body {
  font-family: var(--font-body);
  font-size: clamp(1rem, .96rem + .2vw, 1.125rem);
  line-height: 1.65;
  color: var(--bone);
  background: var(--ink);
  overflow-x: clip;
  min-height: 100svh;
  position: relative;
  -webkit-font-smoothing: antialiased;
}

/* The grain. One repeating SVG, no image request, sits above everything but the effects layer.
   Without it the flat blacks band badly on cheap panels. */
body::after {
  content: '';
  position: fixed; inset: -50%;
  z-index: 9999;
  pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  will-change: transform;
  animation: grain 6s steps(6) infinite;
}
@keyframes grain {
  0%,100% { transform: translate(0,0); }
  20%  { transform: translate(-4%, 3%); }
  40%  { transform: translate(3%, -4%); }
  60%  { transform: translate(-2%, -3%); }
  80%  { transform: translate(4%, 2%); }
}

img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
img { background: var(--ink-2); }
/* ...but NOT the logo, which is the one image on the site with real transparency.
   That box is deliberate for photographs - it reserves a visible tile so a lazy image does
   not flash - and on a transparent PNG/WebP it paints an opaque rectangle behind the
   artwork. It is invisible on the page background, because the tile and the page are both
   near-black, and it is glaring behind the loader's violet drop-shadow, where the mark
   sits in an obvious dark rectangle. Verified by screenshotting the loader, not by reading
   the rule - compositing the asset itself over violet looks perfectly clean, because the
   fault is in the CSS and not in the image. */
.hdr__brand img, .loader__mark img, .ftr__brand img { background: none; }

a { color: inherit; text-decoration-color: rgb(225 29 38 / .55); text-underline-offset: .22em; }
a:hover { text-decoration-color: var(--blood-hot); }

:focus-visible {
  outline: 2px solid var(--toxic);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: 50%; top: 0; translate: -50% -120%;
  z-index: 200; padding: .75rem 1.25rem;
  background: var(--toxic); color: #05210a; font-weight: 700; text-decoration: none;
  transition: translate .18s var(--ease);
}
.skip:focus-visible { translate: -50% 0; }

.wrap    { width: min(100% - var(--gut) * 2, var(--wrap));    margin-inline: auto; }
.wrap-nr { width: min(100% - var(--gut) * 2, var(--wrap-nr)); margin-inline: auto; }

/* ---------------------------------------------------------------- 3. type */

h1, h2, h3, h4, .display {
  font-family: var(--font-display);
  font-weight: 400;
  /* 1.0, not the .92 a condensed display face invites. Uppercased te reo carries macrons -
     WHANGĀREI, WHĀNAU - and at .92 the macron of a wrapped second line collides with the
     descenders of the line above and reads as a smudge. Checked at 1440px. */
  line-height: 1;
  letter-spacing: -.005em;
  text-transform: uppercase;
  text-wrap: balance;
}

/* Headings on this site are written by whoever is at /dash, so they run from four characters to
   a hundred. One clamp() cannot serve both - the long ones take five lines and shove the CTA off
   screen. The build measures each heading and stamps data-len, and the size steps down with it. */
h1, .h1 { font-size: clamp(2.75rem, 1.6rem + 6.4vw, 7rem); }
h1[data-len='m'], .h1[data-len='m'] { font-size: clamp(2.4rem, 1.4rem + 5.2vw, 5.5rem); }
h1[data-len='l'], .h1[data-len='l'] { font-size: clamp(2rem, 1.25rem + 4vw, 4.25rem); }
h1[data-len='xl'], .h1[data-len='xl'] { font-size: clamp(1.75rem, 1.1rem + 3.1vw, 3.25rem); line-height: 1; }

h2, .h2 { font-size: clamp(2rem, 1.3rem + 3.4vw, 4rem); }
h2[data-len='l'], .h2[data-len='l'] { font-size: clamp(1.7rem, 1.2rem + 2.4vw, 3rem); }
h2[data-len='xl'], .h2[data-len='xl'] { font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2.4rem); line-height: 1.02; }

h3, .h3 { font-size: clamp(1.35rem, 1.1rem + 1.1vw, 1.9rem); line-height: 1.05; }
h4, .h4 { font-size: clamp(1.1rem, 1rem + .5vw, 1.35rem); line-height: 1.15; }

p, li { text-wrap: pretty; }
p + p { margin-top: 1em; }

.eyebrow {
  font-family: var(--font-body);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--blood-hot);
  display: flex; align-items: center; gap: .7rem;
}
.eyebrow::before {
  content: ''; width: 2.25rem; height: 1px; background: currentColor; flex: none;
}
.eyebrow--toxic { color: var(--toxic); }

.lede {
  font-size: clamp(1.1rem, 1rem + .55vw, 1.4rem);
  line-height: 1.5;
  color: #d8d2c8;
}
.dim { color: var(--bone-dim); }

/* The word that bleeds. Reserved for single decorative words - Creepster has no macrons. */
.drip {
  font-family: var(--font-drip);
  color: var(--blood-hot);
  text-transform: none;
  letter-spacing: .01em;
  text-shadow: 0 0 1.5rem rgb(225 29 38 / .5);
}

/* A heading that flickers like a failing tube. Applied to the brand word only, and only once
   per page - more than that and it stops being a scare and starts being a migraine. */
.flicker { animation: flicker 7s linear infinite; }
@keyframes flicker {
  0%, 42%, 44%, 47%, 60%, 62%, 100% { opacity: 1;   text-shadow: 0 0 2rem rgb(225 29 38 / .45); }
  43%, 61%                          { opacity: .55; text-shadow: none; }
  46%                               { opacity: .18; text-shadow: none; }
}

/* ------------------------------------------------------- 4. effects layer */

/* Two fixed, non-interactive layers. Nothing in either can swallow a click or trap a reader. */
.fx {
  position: fixed; inset: 0;
  pointer-events: none;
  contain: strict;
}
/* Atmosphere, behind the content - opaque sections are meant to cover it. */
.fx--back  { z-index: 0; }
/* Bats and lightning, in front of the content but under the sticky header (z-index 100) and
   anything modal. A bat drawn behind every section background is a bat nobody ever sees. */
.fx--front { z-index: 90; }

.fx__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

.fx__lightning {
  position: absolute; inset: 0;
  background: radial-gradient(120% 80% at 50% -10%, #fff 0%, rgb(190 205 255 / .55) 25%, transparent 62%);
  opacity: 0;
  mix-blend-mode: screen;
}

.fx__fog {
  position: absolute; left: -25%; width: 150%; height: 60%;
  background: radial-gradient(60% 100% at 50% 100%, rgb(123 75 214 / .10), transparent 70%),
              radial-gradient(45% 80% at 20% 90%, rgb(140 150 190 / .07), transparent 70%);
  filter: blur(30px);
  opacity: .9;
}
.fx__fog--a { bottom: -10%; animation: drift 44s linear infinite; }
.fx__fog--b { bottom: -20%; animation: drift 67s linear infinite reverse; opacity: .6; }
@keyframes drift {
  from { transform: translateX(-8%); }
  to   { transform: translateX(8%); }
}

.fx__vignette {
  position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 50% 40%, transparent 45%, rgb(0 0 0 / .75) 100%);
}

/* The site content sits above the atmosphere and below the bats. */
main, .ftr { position: relative; z-index: 2; }
.mobnav, .floatbar, .loader { position: relative; }

/* Loader. Removed from the DOM once the first paint settles; never blocks content for
   no-JS visitors because it is only shown when the js class is present. */
.loader { display: none; }
.js .loader {
  display: grid; place-content: center; gap: 1.75rem;
  position: fixed; inset: 0; z-index: 500;
  background: var(--ink);
  transition: opacity .5s var(--ease), visibility .5s;
}
.loader.is-done { opacity: 0; visibility: hidden; }
.loader__mark img { width: min(38vw, 11rem); filter: drop-shadow(0 0 2rem rgb(123 75 214 / .5)); animation: hover 2.6s ease-in-out infinite; }
@keyframes hover { 0%,100% { transform: translateY(-6px) } 50% { transform: translateY(6px) } }
.loader__bar { width: min(50vw, 14rem); height: 2px; background: var(--ink-line); overflow: hidden; }
.loader__bar i { display: block; height: 100%; width: 0; background: var(--blood-hot); box-shadow: var(--glow-blood); transition: width .3s linear; }

/* -------------------------------------------------------- 5. header + nav */

.hdr {
  position: sticky; top: 0; z-index: 100;
  display: flex; align-items: center; gap: clamp(1rem, 3vw, 2.5rem);
  padding: .7rem var(--gut);
  min-height: var(--hdr-h);
  background: color-mix(in srgb, var(--ink) 72%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid transparent;
  transition: background .3s var(--ease), border-color .3s var(--ease);
}
.hdr.is-stuck {
  background: color-mix(in srgb, var(--ink) 94%, transparent);
  border-bottom-color: var(--ink-line);
}

.hdr__brand {
  flex: none;
  display: flex; align-items: center; gap: .6rem;
  text-decoration: none;
}
.hdr__brand img {
  /* The logo carries its own wordmark, so it needs HEIGHT to be legible - it is not a mark
     sitting beside typed text any more. Anything under about 3rem and the lettering smears.
     Doubled on Richard's instruction 2026-08-20. It does NOT shrink on scroll: an is-stuck rule
     that quietly put it back to the old size would read as the change not having been made. */
  width: auto; height: clamp(6.8rem, 14vw, 9.2rem);
  filter: drop-shadow(0 .15rem .5rem rgb(0 0 0 / .9));
}
.hdr__word {
  display: grid;
  font-family: var(--font-display);
  text-transform: uppercase;
  line-height: .85;
  letter-spacing: .01em;
}
.hdr__word b { font-weight: 400; font-size: clamp(.9rem, 2vw, 1.1rem); color: var(--bone); }
.hdr__word i {
  font-style: normal;
  font-size: clamp(1.15rem, 2.7vw, 1.5rem);
  color: var(--blood-hot);
  text-shadow: 0 0 1.2rem rgb(225 29 38 / .45);
}

.hdr__nav { margin-inline-start: auto; }
.hdr__nav ul { display: flex; gap: clamp(.9rem, 2vw, 1.9rem); list-style: none; padding: 0; }
.hdr__nav a {
  position: relative;
  font-size: .8rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  text-decoration: none; color: var(--bone-dim);
  padding-block: .4rem;
  transition: color .2s var(--ease);
}
.hdr__nav a::after {
  content: ''; position: absolute; left: 0; right: 100%; bottom: 0; height: 2px;
  background: var(--blood-hot);
  transition: right .28s var(--ease);
}
.hdr__nav a:hover, .hdr__nav a[aria-current] { color: var(--bone); }
.hdr__nav a:hover::after, .hdr__nav a[aria-current]::after { right: 0; }

.hdr__burger { display: none; }

@media (max-width: 60rem) {
  /* .hdr .hdr__cta, not .hdr__cta. The button also carries .btn, which sets display:inline-flex
     and is declared later in this file - equal specificity, so source order wins and a bare
     .hdr__cta{display:none} loses. The CTA stayed visible on every phone. */
  .hdr__nav, .hdr .hdr__cta { display: none; }
  .hdr__burger {
    display: grid; gap: 5px; margin-inline-start: auto;
    background: none; border: 0; padding: .55rem; cursor: pointer;
  }
  .hdr__burger span { display: block; width: 26px; height: 2px; background: var(--bone); transition: .28s var(--ease); }
  .hdr__burger[aria-expanded='true'] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .hdr__burger[aria-expanded='true'] span:nth-child(2) { opacity: 0; }
  .hdr__burger[aria-expanded='true'] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

.mobnav {
  position: fixed; inset: var(--hdr-h) 0 0; z-index: 99;
  display: grid; align-content: start; gap: .25rem;
  padding: 2rem var(--gut) 4rem;
  background: color-mix(in srgb, var(--ink) 97%, transparent);
  backdrop-filter: blur(20px);
  overflow-y: auto;
}
.mobnav[hidden] { display: none; }
.mobnav ul { list-style: none; padding: 0; display: grid; gap: .25rem; }
.mobnav a {
  display: block; padding: .85rem 0;
  font-family: var(--font-display); font-size: 2rem; text-transform: uppercase;
  text-decoration: none; border-bottom: 1px solid var(--ink-line);
}
.mobnav .btn { margin-top: 1.75rem; justify-self: start; }

/* ----------------------------------------------------------- 6. buttons */

.btn {
  --btn-bg: var(--blood);
  --btn-fg: #fff;
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .95rem 1.7rem;
  font-family: var(--font-body);
  font-size: .82rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  text-decoration: none; color: var(--btn-fg);
  background: var(--btn-bg);
  border: 0; border-radius: var(--radius);
  cursor: pointer;
  position: relative; overflow: hidden; isolation: isolate;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
.btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--blood-hot);
  transform: translateY(101%);
  transition: transform .35s var(--ease);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--glow-blood); }
.btn:hover::before { transform: translateY(0); }
.btn:active { transform: translateY(0); }

.btn--toxic { --btn-bg: var(--toxic); --btn-fg: #06230b; }
.btn--toxic::before { background: #96f556; }
.btn--toxic:hover { box-shadow: var(--glow-toxic); }

.btn--ghost {
  --btn-bg: transparent; --btn-fg: var(--bone);
  box-shadow: inset 0 0 0 1px var(--ink-line);
}
.btn--ghost::before { background: var(--bone); }
.btn--ghost:hover { --btn-fg: var(--ink); box-shadow: inset 0 0 0 1px var(--bone); }

.btn--sm { padding: .7rem 1.15rem; font-size: .72rem; }
.btn[disabled], .btn[aria-disabled='true'] { opacity: .45; pointer-events: none; }

.btnrow { display: flex; flex-wrap: wrap; gap: .85rem; }

/* --------------------------------------------------------- 7. sections */

.sec { padding-block: var(--sec); position: relative; }
.sec--tight { padding-block: clamp(3rem, 7vw, 5rem); }
/* A hidden section must take NO space. The UA sheet already does this, but every other toggled
   element in this file states it explicitly, and a .sec left visible around a hidden child shows
   up as an empty 72-144px black band rather than as nothing. */
.sec[hidden] { display: none; }
.sec--ink2 { background: var(--ink-2); }
.sec--ink3 { background: var(--ink-3); }

/* Torn-paper divider. A single SVG path, flipped for the bottom edge. */
.sec--torn::before,
.sec--torn::after {
  content: ''; position: absolute; left: 0; right: 0; height: 2.5rem;
  background: inherit;
  -webkit-mask: var(--torn) repeat-x bottom / 120px 100%;
          mask: var(--torn) repeat-x bottom / 120px 100%;
}
.sec--torn::before { top: -2.4rem; }
.sec--torn::after  { bottom: -2.4rem; transform: scaleY(-1); }
:root {
  --torn: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 40V16l7 5 6-9 8 11 9-14 7 12 8-8 6 10 9-13 8 12 7-7 6 9 8-11 9 13 7-9 5 10v13z' fill='%23000'/%3E%3C/svg%3E");
}

.sechead { display: grid; gap: 1rem; max-width: 46rem; margin-bottom: clamp(2.25rem, 5vw, 3.5rem); }
.sechead--centre { margin-inline: auto; text-align: center; justify-items: center; }
.sechead--centre .eyebrow::before { display: none; }

/* Scroll reveal. threshold is 0 with rootMargin only - a ratio threshold never fires for an
   element taller than the viewport, and the copy column on a long event page is exactly that.
   Scoped to .js so no-JS visitors and crawlers always see everything. */
.js .rv { opacity: 0; transform: translateY(28px); }
.js .rv.is-in { opacity: 1; transform: none; transition: opacity .8s var(--ease), transform .8s var(--ease); }
.js .rv-d1.is-in { transition-delay: .09s; }
.js .rv-d2.is-in { transition-delay: .18s; }
.js .rv-d3.is-in { transition-delay: .27s; }

/* ------------------------------------------------------- 8. components */

/* --- hero --- */

.hero {
  position: relative;
  min-height: min(100svh, 58rem);
  display: grid; align-items: end;
  padding: calc(var(--hdr-h) + 3rem) 0 clamp(3rem, 8vw, 6rem);
  overflow: clip;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
.hero__media img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 38%;
  will-change: transform;
  /* Lifts the PHOTOGRAPH, not just the scrims over it. Easing a scrim only ever changes the
     pixels that scrim covers, and this hero's dark half is dark because the source frame is -
     which is why three rounds of scrim work measured well and still read as "no change". */
  filter: brightness(1.12);
}
/* Four layers, and the LAST one is a lift, not a scrim.
   The hero photograph is shot in bush at dusk, so its left third is already near-black. Easing
   the old .82 left scrim (written for the McKay Stadium photo, bright evening sky, white nav on
   cloud) moved the measured luminance of that band from 11.0 to 12.9 and looked like nothing
   had changed - there was no detail there to stop darkening. So the left band is now LIFTED
   with a low-alpha violet-grey underneath the scrims, which raises the black point and lets the
   trees read as trees. Violet rather than neutral grey because a neutral lift on a night
   photograph goes straight to dishwater; this is the same hue family as --ink-line.
   Keep a scrim over the lift: at narrow widths the crop swings the (much brighter) crew behind
   the copy, so the h1 and lede cannot rely on the photograph being dark there. Contrast is
   measured off the rendered pixels, not assumed - see the note in CLAUDE.md. */
.hero__media::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, rgb(8 7 10 / .83) 0%, rgb(8 7 10 / .32) 22%, transparent 40%),
    linear-gradient(to top, var(--ink) 2%, rgb(8 7 10 / .52) 42%, rgb(8 7 10 / .16) 100%),
    linear-gradient(to right, rgb(8 7 10 / .20), rgb(8 7 10 / 0) 56%),
    linear-gradient(to right, rgb(104 94 130 / .72), rgb(104 94 130 / 0) 58%);
}
.hero__inner { position: relative; z-index: 1; display: grid; gap: 1.5rem; max-width: 44rem; }
.hero h1 { text-shadow: 0 .4rem 2.5rem rgb(0 0 0 / .8); }
.hero h1 .line { display: block; overflow: hidden; }
.hero h1 .line > span { display: block; }
.hero__scroll {
  position: absolute; left: 50%; bottom: 1.5rem; translate: -50% 0; z-index: 1;
  display: grid; justify-items: center; gap: .55rem;
  font-size: .68rem; letter-spacing: .24em; text-transform: uppercase; color: var(--bone-faint);
}
.hero__scroll i { display: block; width: 1px; height: 3rem; background: linear-gradient(var(--blood-hot), transparent); }

/* --- stat strip --- */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1px; background: var(--ink-line); }
.stat { background: var(--ink); padding: clamp(1.5rem, 3.5vw, 2.5rem); text-align: center; }
.stat__n { font-family: var(--font-display); font-size: clamp(2.5rem, 5vw, 4rem); line-height: 1; color: var(--toxic); }
.stat__l { font-size: .74rem; letter-spacing: .16em; text-transform: uppercase; color: var(--bone-dim); margin-top: .5rem; }

/* --- cards --- */

.grid { display: grid; gap: clamp(1rem, 2.2vw, 1.75rem); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }

.card {
  position: relative;
  display: grid; grid-template-rows: auto 1fr;
  background: var(--ink-2);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color .3s var(--ease), transform .35s var(--ease);
}
.card:hover { border-color: color-mix(in srgb, var(--blood-hot) 55%, transparent); transform: translateY(-4px); }
.card__media { aspect-ratio: 3 / 2; overflow: hidden; background: var(--ink-3); }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease), filter .7s var(--ease); filter: saturate(.85); }
.card:hover .card__media img { transform: scale(1.05); filter: saturate(1.05); }
.card__body { padding: clamp(1.15rem, 2.5vw, 1.75rem); display: grid; gap: .65rem; align-content: start; }
.card__meta { font-size: .74rem; letter-spacing: .15em; text-transform: uppercase; color: var(--blood-hot); }
.card__body p { color: var(--bone-dim); font-size: .95rem; }

/* A bare photo strip - the home page's "evidence" row. One shared portrait box so a set of
   photographs with different native ratios still lines up; without it the row stair-steps and
   reads as an accident rather than a layout. Richard was shown both and chose this (2026-08-17).
   Any image dropped in here is cropped, so set an object-position on the ones where the subject
   is not centred. */
.figs figure { aspect-ratio: 2 / 3; overflow: hidden; background: var(--ink-3); border-radius: var(--radius); }
.figs figure img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.85); }
/* One stretched link so the whole card is the target without nesting interactive elements. */
.card__link { position: static; text-decoration: none; }
.card__link::after { content: ''; position: absolute; inset: 0; }

.card--event .card__media { aspect-ratio: 4 / 5; }

/* --- charity marquee --- */

.marquee { overflow: hidden; border-block: 1px solid var(--ink-line); padding-block: 1.15rem; }
.marquee__track { display: flex; gap: 3.5rem; width: max-content; padding-left: 3.5rem; }
.js .marquee__track { animation: marquee 38s linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }
.marquee span {
  font-family: var(--font-display); font-size: clamp(1.25rem, 2.6vw, 2rem);
  text-transform: uppercase; color: var(--bone-faint); white-space: nowrap;
  display: flex; align-items: center; gap: 3.5rem;
}
.marquee span::after { content: ''; width: .5rem; height: .5rem; background: var(--blood); rotate: 45deg; flex: none; }

/* --- split feature --- */

.split { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (min-width: 52rem) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--flip .split__media { order: 2; }
}
.split__media { position: relative; }
.split__media img { width: 100%; border-radius: var(--radius); }
.split__media::before {
  content: ''; position: absolute; inset: 0;
  box-shadow: inset 0 0 8rem 1rem rgb(8 7 10 / .75);
  pointer-events: none; z-index: 1;
}
.split__body { display: grid; gap: 1.15rem; align-content: start; }

/* --- pull quote --- */

.quote { max-width: 44rem; margin-inline: auto; text-align: center; display: grid; gap: 1.5rem; }
.quote blockquote {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1rem + 1.9vw, 2.35rem);
  line-height: 1.14; text-transform: none; letter-spacing: 0;
  text-wrap: balance;
}
.quote blockquote::before { content: '\201C'; color: var(--blood-hot); }
.quote blockquote::after  { content: '\201D'; color: var(--blood-hot); }
.quote figcaption { font-size: .8rem; letter-spacing: .14em; text-transform: uppercase; color: var(--bone-dim); }
.quote figcaption b { color: var(--bone); font-weight: 700; }

/* Three testimonials in a row.
   The single pull quote this replaced was set in the DISPLAY face at up to 2.35rem, which does
   not survive being tripled - three of those is a wall of condensed capitals and nobody reads
   the third. So the card variant drops to the body face at a readable size and lets the
   attribution carry the weight instead.
   `grid-template-rows: 1fr auto` on the figure is what matters visually: the blockquote takes
   the slack, so all three figcaptions sit on the same baseline however unevenly the quotes
   wrap. Without it each caption floats directly under its own text and the row looks ragged. */
.quotes { margin-top: clamp(2rem, 4vw, 3rem); }
.quote--card {
  max-width: none; margin-inline: 0; text-align: left;
  grid-template-rows: 1fr auto; gap: 1.15rem;
  background: var(--ink-2);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: clamp(1.35rem, 2.6vw, 2rem);
  transition: border-color .3s var(--ease), transform .35s var(--ease);
}
.quote--card:hover {
  border-color: color-mix(in srgb, var(--blood-hot) 55%, transparent);
  transform: translateY(-4px);
}
.quote--card blockquote {
  font-family: var(--font-body);
  font-size: clamp(1rem, .96rem + .18vw, 1.1rem);
  line-height: 1.52; text-wrap: pretty;
  color: var(--bone);
}
/* The name keeps the uppercase letterspaced treatment; the role does NOT. "Class Administrator,
   Glass Ceiling Arts Collective" set in uppercase at .14em tracking runs to three lines and
   competes with the quote it is attributing. */
.quote--card figcaption { display: grid; gap: .3rem; }
.quote--card figcaption span {
  text-transform: none; letter-spacing: .01em;
  font-size: .85rem; line-height: 1.35; color: var(--bone-dim);
}

/* --- gallery --- */

/* Mosaic on a fixed row unit, not per-tile aspect-ratio.
   With aspect-ratio on the tiles, a grid row stretches to its tallest member and the shorter
   tiles leave black gaps beside them. Spanning a fixed auto-row instead means every tile fills
   its cell exactly, and `dense` back-fills the holes the wide tiles leave. */
.gal {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  grid-auto-rows: 10rem;
  grid-auto-flow: dense;
  gap: .6rem;
}
.gal__i {
  position: relative; overflow: hidden; border-radius: var(--radius);
  background: var(--ink-2); cursor: zoom-in; border: 0; padding: 0;
  grid-row: span 2;
}
.gal__i:nth-child(7n+1)  { grid-column: span 2; }
.gal__i:nth-child(11n+5) { grid-row: span 3; }
@media (max-width: 34rem) {
  .gal { grid-auto-rows: 8rem; }
  .gal__i:nth-child(7n+1) { grid-column: span 1; }
}
.gal__i img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.8) contrast(1.05); transition: transform .8s var(--ease), filter .5s var(--ease); }
.gal__i:hover img, .gal__i:focus-visible img { transform: scale(1.07); filter: saturate(1.1) contrast(1); }

.lb { position: fixed; inset: 0; z-index: 400; display: grid; place-items: center; padding: var(--gut); background: rgb(4 3 6 / .95); backdrop-filter: blur(8px); }
.lb[hidden] { display: none; }
.lb img { max-width: 100%; max-height: 82svh; width: auto; object-fit: contain; }
.lb__cap { margin-top: 1rem; text-align: center; color: var(--bone-dim); font-size: .9rem; max-width: 44rem; }
.lb__x, .lb__prev, .lb__next {
  position: absolute; background: none; border: 0; color: var(--bone); cursor: pointer;
  font-size: 2rem; line-height: 1; padding: 1rem;
}
.lb__x { top: .5rem; right: .5rem; }
.lb__prev { left: .25rem; top: 50%; translate: 0 -50%; }
.lb__next { right: .25rem; top: 50%; translate: 0 -50%; }

/* --- event list --- */

.evlist { display: grid; gap: 1px; background: var(--ink-line); border-block: 1px solid var(--ink-line); }
.ev {
  position: relative;
  background: var(--ink);
  display: grid; gap: 1rem 2rem; align-items: center;
  padding: clamp(1.5rem, 3.5vw, 2.5rem) 0;
  transition: background .3s var(--ease);
}
.ev:hover { background: var(--ink-2); }
@media (min-width: 46rem) { .ev { grid-template-columns: 8rem 1fr auto; } }
.ev__date { text-align: center; border: 1px solid var(--ink-line); padding: .7rem .5rem; }
.ev__date b { display: block; font-family: var(--font-display); font-size: 2.1rem; line-height: 1; }
.ev__date span { font-size: .7rem; letter-spacing: .18em; text-transform: uppercase; color: var(--blood-hot); }
.ev__body { display: grid; gap: .4rem; }
.ev__body p { color: var(--bone-dim); font-size: .95rem; }
.ev__where { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--bone-faint); }
.ev__link { text-decoration: none; }
.ev__link::after { content: ''; position: absolute; inset: 0; }

.pill {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .3rem .7rem;
  font-size: .68rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  border-radius: 99px;
  background: color-mix(in srgb, var(--toxic) 18%, transparent);
  color: var(--toxic);
  border: 1px solid color-mix(in srgb, var(--toxic) 40%, transparent);
}
.pill--past { background: color-mix(in srgb, var(--bone) 8%, transparent); color: var(--bone-dim); border-color: var(--ink-line); }
.pill--soon { background: color-mix(in srgb, var(--blood-hot) 18%, transparent); color: #ff6b72; border-color: color-mix(in srgb, var(--blood-hot) 45%, transparent); }

/* --- article body (admin-authored copy) --- */

.prose { max-width: 42rem; }
.prose > * + * { margin-top: 1.15em; }
.prose h2 { margin-top: 2.2em; font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.1rem); }
.prose h3 { margin-top: 1.8em; }
.prose ul, .prose ol { padding-left: 1.35em; display: grid; gap: .4em; }
.prose li::marker { color: var(--blood-hot); }
.prose a { color: var(--toxic); }
.prose blockquote {
  border-left: 2px solid var(--blood); padding-left: 1.25rem;
  font-size: 1.1em; color: #d8d2c8;
}
.prose img { border-radius: var(--radius); margin-block: 2rem; }
.prose hr { border: 0; border-top: 1px solid var(--ink-line); margin-block: 2.5rem; }

/* --- eyes in the dark --- */

.eyes { display: flex; gap: clamp(2rem, 9vw, 6rem); justify-content: center; padding-block: 1rem; }
.eye {
  width: clamp(2.5rem, 6vw, 4rem); aspect-ratio: 2 / 1;
  background: radial-gradient(closest-side, rgb(255 200 60 / .18), transparent);
  display: grid; place-items: center;
}
.eye i {
  display: block; width: 38%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #ffd86b, #b8480c 60%, #3b0b02);
  box-shadow: 0 0 1.5rem rgb(255 170 40 / .55);
  transition: translate .12s linear;
}

/* ------------------------------------------------------------ 9. forms */

.form { display: grid; gap: 1.15rem; }
.field { display: grid; gap: .45rem; }
.field > label { font-size: .76rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--bone-dim); }
.field .req { color: var(--blood-hot); }

input[type=text], input[type=email], input[type=tel], input[type=url],
input[type=password], input[type=date], input[type=time], input[type=number],
select, textarea {
  width: 100%;
  padding: .85rem 1rem;
  font: inherit; font-size: 1rem;
  color: var(--bone);
  background: var(--ink-2);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
textarea { min-height: 9rem; resize: vertical; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--blood-hot);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--blood-hot) 22%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--bone-faint); }

/* Honeypot. Not type=hidden and not display:none - capable bots skip both. Off canvas,
   untabbable, hidden from assistive tech, and autocomplete off so no browser fills it. */
.hp {
  position: absolute !important;
  left: -9999px !important; top: auto !important;
  width: 1px !important; height: 1px !important;
  overflow: hidden !important;
}

.formnote { font-size: .85rem; color: var(--bone-faint); }
.formmsg { padding: .9rem 1.1rem; border-radius: var(--radius); font-size: .95rem; }
.formmsg--ok  { background: color-mix(in srgb, var(--toxic) 14%, transparent); border: 1px solid color-mix(in srgb, var(--toxic) 40%, transparent); }
.formmsg--err { background: color-mix(in srgb, var(--blood-hot) 14%, transparent); border: 1px solid color-mix(in srgb, var(--blood-hot) 40%, transparent); }
.formmsg[hidden] { display: none; }

/* ----------------------------------------------------------- 10. footer */

.ftr { background: var(--ink-2); border-top: 1px solid var(--ink-line); margin-top: 0; }
.ftr__inner {
  width: min(100% - var(--gut) * 2, var(--wrap)); margin-inline: auto;
  display: grid; gap: clamp(2rem, 5vw, 3.5rem);
  padding-block: clamp(3rem, 7vw, 5rem);
}
@media (min-width: 52rem) { .ftr__inner { grid-template-columns: 1.3fr 1fr 1fr; } }
/* The logo is SQUARE and contains its own wordmark, so it needs real height to be legible -
   at the 3.25rem the old landscape lockup used, the lettering was about 30px wide and unreadable. */
.ftr__brand img { width: auto; height: 6.5rem; margin-bottom: 1rem; }
.ftr__brand p { color: var(--bone-dim); max-width: 24rem; }
.ftr__nav ul { list-style: none; padding: 0; display: grid; gap: .55rem; }
.ftr__nav a, .ftr__contact a { text-decoration: none; color: var(--bone-dim); }
.ftr__nav a:hover, .ftr__contact a:hover { color: var(--bone); text-decoration: underline; }
.ftr__contact h2 { font-size: 1.15rem; margin-bottom: .85rem; }
.ftr__contact p + p { margin-top: .4rem; }

.ftr__base {
  border-top: 1px solid var(--ink-line);
  width: min(100% - var(--gut) * 2, var(--wrap)); margin-inline: auto;
  padding-block: 1.5rem;
  display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between;
  font-size: .8rem; color: var(--bone-faint);
}
/* The whole phrase is the link - one credit unit, and a bigger tap target on a phone. */
.ftr__credit { color: var(--bone-faint); text-decoration: none; }
.ftr__credit:hover { color: var(--bone-dim); text-decoration: underline; }

/* --- floating contact bar --- */

.floatbar {
  position: fixed; right: clamp(.75rem, 2vw, 1.25rem); bottom: clamp(.75rem, 2vw, 1.25rem);
  z-index: 120;
  display: grid; gap: .5rem; justify-items: center;
}
.floatbar[hidden] { display: none; }
.floatbar a, .floatbar button {
  --fb-bg: var(--blood);
  --fb-fg: #fff;
  display: grid; place-items: center;
  width: 3rem; height: 3rem;
  background: var(--fb-bg); color: var(--fb-fg);
  border: 0; border-radius: 50%;
  box-shadow: var(--shadow-lift);
  cursor: pointer;
  opacity: 0; transform: translateY(10px) scale(.7);
  transition: opacity .28s var(--ease), transform .28s var(--ease), background .2s;
}
.floatbar.is-open a, .floatbar.is-open button:not(.floatbar__toggle) { opacity: 1; transform: none; }
.floatbar__toggle { opacity: 1 !important; transform: none !important; --fb-bg: var(--ink-3); }
.floatbar.is-open .floatbar__toggle { rotate: 45deg; }
.floatbar svg { width: 1.35rem; height: 1.35rem; fill: currentColor; }
.floatbar a:hover, .floatbar button:hover { --fb-bg: var(--blood-hot); }

.fbpop {
  position: fixed; inset: 0; z-index: 130; display: grid; place-items: center;
  background: rgb(4 3 6 / .8); backdrop-filter: blur(6px); padding: var(--gut);
}
.fbpop[hidden] { display: none; }
.fbpop__card { background: var(--ink-2); border: 1px solid var(--ink-line); padding: 2.25rem; text-align: center; position: relative; border-radius: var(--radius); }
.fbpop__card a { font-family: var(--font-display); font-size: clamp(1.75rem, 6vw, 2.75rem); text-decoration: none; }
.fbpop__x { position: absolute; top: .35rem; right: .35rem; background: none; border: 0; color: var(--bone-dim); font-size: 1.6rem; cursor: pointer; padding: .5rem; }

/* ------------------------------------------------------------- 11. dash */

.dash { --wrap: 68rem; padding-block: clamp(2rem, 5vw, 3.5rem); }
.dash__bar { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; margin-bottom: 2rem; }
.dash__tabs { display: flex; flex-wrap: wrap; gap: .3rem; border-bottom: 1px solid var(--ink-line); margin-bottom: 2rem; }
.dash__tabs button {
  background: none; border: 0; border-bottom: 2px solid transparent;
  padding: .8rem 1.1rem; cursor: pointer;
  font: inherit; font-size: .8rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--bone-dim);
}
.dash__tabs button[aria-selected='true'] { color: var(--bone); border-bottom-color: var(--blood-hot); }
.dash__panel[hidden] { display: none; }
.dash__row {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between;
  padding: 1rem; border: 1px solid var(--ink-line); border-radius: var(--radius); background: var(--ink-2);
}
.dash__row + .dash__row { margin-top: .5rem; }
.dash__row h3 { font-size: 1.05rem; }
.dash__row .meta { font-size: .78rem; color: var(--bone-faint); letter-spacing: .08em; text-transform: uppercase; }
.dash__actions { display: flex; gap: .4rem; }
.dash table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.dash th, .dash td { text-align: left; padding: .7rem .5rem; border-bottom: 1px solid var(--ink-line); vertical-align: top; }
.dash th { font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--bone-dim); }
.dash__scroll { overflow-x: auto; }
.dash .grid2 { display: grid; gap: 1rem; }
@media (min-width: 44rem) { .dash .grid2 { grid-template-columns: 1fr 1fr; } }

/* -------------------------------------------------- 12. motion + print */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  body::after { animation: none; }
  .js .rv { opacity: 1; transform: none; }
  .fx__canvas, .fx__lightning { display: none; }
  .flicker { animation: none; }
  .js .marquee__track { animation: none; }
  .loader { display: none !important; }
}

@media print {
  .fx, .floatbar, .hdr__burger, .loader, .mobnav { display: none !important; }
  body { background: #fff; color: #000; }
}
