/* ==========================================================================
   CRAWLLOWEEN design tokens
   The single source of truth for colour, type, space and motion.

   Never hardcode a colour, font, space or duration anywhere else.
   `_recon/verify.py` fails on a hex value outside this file.

   The palette is taken from the two real 2025 Crawlloween flyers: a burning
   Chicago skyline over black water, bats, and a dimensional dripping wordmark.
   River North's flyer is pumpkin orange with toxic-green drips; West Loop's is
   bone chrome with blood-red drips. Everything here extends those two, it does
   not invent a new look.
   ========================================================================== */

:root {

  /* ---------------------------------------------------------------- surface */
  /* Warm near-black with a red undertone, deliberately not #000. Pure black
     flattens the photography, which is all high-ISO low-light nightlife. */
  --bg:            #0b0709;
  --bg-soft:       #120c0f;
  --bg-elev:       #1b1317;
  --bg-sunk:       #070405;

  --line:          rgba(246, 236, 221, 0.09);
  --line-strong:   rgba(246, 236, 221, 0.18);

  /* Translucent surfaces: a blurred nav over photography, a modal backdrop, a
     sticky buy bar. These are tokens rather than inline rgba() because they are
     the same two colours at five opacities, and getting one of them slightly
     wrong reads as a rendering bug rather than a design choice. */
  --veil-nav:      rgba(11, 7, 9, 0.92);
  --veil-bar:      rgba(11, 7, 9, 0.96);
  --veil-control:  rgba(11, 7, 9, 0.70);
  --veil-modal:    rgba(7, 4, 5, 0.90);
  --veil-lightbox: rgba(7, 4, 5, 0.95);

  /* Scrims for laying type over photography. The photos are busy, so text
     never sits on one directly. */
  --scrim-soft:    linear-gradient(180deg, rgba(11,7,9,0.35) 0%, rgba(11,7,9,0.78) 100%);
  --scrim-hard:    linear-gradient(180deg, rgba(11,7,9,0.55) 0%, rgba(11,7,9,0.93) 100%);
  --scrim-side:    linear-gradient(90deg, rgba(11,7,9,0.92) 0%, rgba(11,7,9,0.35) 55%, rgba(11,7,9,0) 100%);

  /* Entrance gate: the haunted house scene. A silhouette against a burning sky,
     so it needs its own darks below --bg-sunk and a moon warm enough to read as
     moonlight rather than as another ember. */
  --gate-void:       #000000;   /* the doorway. The darkest thing in the frame. */

  /* The house is near-black, so the SKY has to carry the contrast or the
     silhouette disappears and its lit windows look like they are floating in
     space. The first version had the sky's top band at --bg-sunk, the exact
     value the house is filled with, and the gable read as a detached triangle.
     A deep plum top also matches the flyer skies, which are purple and magenta
     rather than black. */
  --gate-house:      #050304;
  --gate-house-trim: #0e0709;
  --gate-bat:        #070405;
  --gate-sky-top:    #241033;
  --gate-sky-mid:    #4a1526;
  --gate-sky-low:    #8a2f0c;
  --gate-moon-edge:  #cbbb9c;
  --gate-moonglow:   rgba(246, 236, 221, 0.17);

  /* Entrance-gate door surface: a faint brushed grain and a warm wash from above,
     so the closed doors read as material rather than as two flat rectangles. */
  --door-grain:    rgba(246, 236, 221, 0.014);
  --door-wash:     rgba(255, 122, 24, 0.07);

  /* Narrow-viewport hero scrim. Stronger throughout, and especially at the top,
     because on a phone the whole text block sits in the band where --scrim-hard
     is weakest. See the note on .hero__scrim in page.css. */
  --scrim-phone:   linear-gradient(180deg, rgba(11,7,9,0.82) 0%, rgba(11,7,9,0.88) 45%, rgba(11,7,9,0.96) 100%);

  /* Mid-page video band. Deliberately flatter than --scrim-hard rather than
     darker on average: a band carries body copy at body size across its whole
     height, not one enormous H1 at the top, so it cannot afford the weak 0.55
     upper stop that a hero gets away with. */
  --scrim-band:    linear-gradient(180deg, rgba(11,7,9,0.88) 0%, rgba(11,7,9,0.80) 50%, rgba(11,7,9,0.93) 100%);

  /* ------------------------------------------------------------------- ink */
  /* Bone, lifted off the cream date type on both flyers. */
  --text:          #f6ecdd;
  --text-dim:      #a89a8d;
  --text-faint:    #6d635b;
  --text-on-accent:#0b0709;

  /* ----------------------------------------------------------- brand core */
  /* Shared by every neighborhood. The burning skyline and the drip. */
  --ember:         #ff7a18;
  --ember-deep:    #b84a05;
  --blood:         #c1121f;
  --blood-deep:    #7a0a13;
  --bone:          #f2e6cf;
  --toxic:         #8ddb2c;   /* the 2025 River North wristband green */

  /* ------------------------------------------------- neighborhood accents */
  /* SIX accents, one per neighborhood, sampled from the six commissioned 2026
     flyers rather than invented. Each is clearly distinguishable from the other
     five at a glance, because the navigation model is "pick a neighborhood" and
     colour is the fastest way a returning buyer finds theirs.

     Three of the six flyers lean blue, so they are deliberately pulled apart:
     Logan Square keeps the saturated violet, Lincoln Park is a cold desaturated
     steel, and Wrigley uses the gold from its wordmark rather than its royal
     blue background. Without that split, half the site would read as one colour.

     Each carries a -deep for hover/pressed and a -glow for the rgba washes. */

  /* River North: jack-o-lanterns on the river, Marina City, toxic green drips. */
  --rn:            #ff7a18;
  --rn-deep:       #c25505;
  --rn-glow:       rgba(255, 122, 24, 0.22);

  /* West Loop: the giant bat over brick and the L, blood red on gunmetal. */
  --wl:            #d4202a;
  --wl-deep:       #8e1119;
  --wl-glow:       rgba(212, 32, 42, 0.22);

  /* Wicker Park: zombies at Six Corners, hot magenta with acid yellow-green. */
  --wp:            #ff2d95;
  --wp-deep:       #b80a63;
  --wp-glow:       rgba(255, 45, 149, 0.22);

  /* Logan Square: ghosts around the Centennial Monument, violet and teal. */
  --ls:            #a855f7;
  --ls-deep:       #7127c4;
  --ls-glow:       rgba(168, 85, 247, 0.22);

  /* Lincoln Park: the werewolf under a full moon, cold steel and silver. */
  --lp:            #8fb8d8;
  --lp-deep:       #4a7799;
  --lp-glow:       rgba(143, 184, 216, 0.22);

  /* Wrigley: skeleton ballplayers at the marquee. Gold, from the wordmark. */
  --wg:            #e5a52a;
  --wg-deep:       #a06f08;
  --wg-glow:       rgba(229, 165, 42, 0.22);

  /* Each flyer's SECOND ink: the drip, the counter-glow, the accent on the
     accent. Used sparingly for a rule or a hover, never for body text. */
  --rn-2:          #8ddb2c;   /* toxic green drips */
  --wl-2:          #b9bfc7;   /* gunmetal chrome */
  --wp-2:          #d4e619;   /* acid yellow-green */
  --ls-2:          #5fe0d0;   /* teal ghost light */
  --lp-2:          #c0522f;   /* autumn red leaves */
  --wg-2:          #2f6fb5;   /* royal blue */

  /* `--accent` is what components actually read. A page or a card sets it
     once, from the five above, and everything inside inherits. Default is the
     brand ember so a component is never unstyled. */
  --accent:        var(--ember);
  --accent-deep:   var(--ember-deep);
  --accent-glow:   rgba(255, 122, 24, 0.22);
  --accent-2:      var(--toxic);

  /* ------------------------------------------------------------------ map */
  /* Lake Michigan. The one place on the page where a cold note is correct, so
     the whole composition does not read as a single temperature. The stroke is
     Chicago flag light blue, Pantone 298. */
  --water-fill:    #0c2740;
  --water-line:    #41b6e6;

  /* -------------------------------------------------------------- feedback */
  --ok:            #4ade80;
  --warn:          #fbbf24;
  --err:           #f87171;

  /* ------------------------------------------------------------------ type */
  /* Two families, no more.

     Big Shoulders Display is the Bizzy Nights display face, so Crawlloween
     reads as related without looking like a subpage. It is a heavy condensed
     poster face, which is what the flyer date line actually is.

     Deliberately NOT a novelty horror font. A dripping-blood webfont reads as
     cheesy at body sizes and the flyer already carries the dimensional
     lettering as artwork. The Halloween character comes from colour, texture,
     the wordmark art and the motion, not from the typeface.

     TWO DISPLAY-ONLY FACES WERE ADDED ON 2026-08-04, at the owner's request,
     after Scare City (scarecity.co.uk) was given as a type reference. That site
     runs Burnaby Stencil for headings over Bulletin Typewriter for labels, both
     Adobe Fonts, plus Brandon Grotesque and Another Danger, all four licensed
     commercially and none of them self-hostable here. So these are the licensed
     equivalents rather than the originals:

       --font-stencil  Saira Stencil One, for Scare City's stencil headings.
       --font-type     Special Elite, for the stamped-notice texture Scare City
                       gets from Bulletin Typewriter.

     WHY SAIRA STENCIL ONE AND NOT BIG SHOULDERS STENCIL. The obvious choice was
     Big Shoulders Stencil: same superfamily as the display face already here,
     same skeleton, same designer. It was wired in first and it had to come out,
     for a reason worth recording so nobody puts it back.

     Its L is cut across the elbow, which separates the stem from the foot
     completely. A single L survives that on context. A DOUBLE L does not: it
     renders as "I.I.", which reads as punctuation rather than as letters. So
     CRAWLLOWEEN came out as CRAWI.I.OWEEN, HALLOWEEN as HAI.I.OWEEN, and ALL
     ACCESS as AI.I. ACCESS.

     That is disqualifying rather than merely unfortunate, because the double L is
     the one thing about this brand that is actively defended: it is owner-
     confirmed, verify.py blocks the one-L spelling, and audit_stage.py blocks it
     again at deploy. Shipping a wordmark whose two Ls look like full stops would
     have undone all of that in the one place customers actually look. And
     "Halloween" carries the same double L, so it was never containable to the
     wordmark: it would have hit most headings on the site.

     Saira Stencil One cuts only the corner of the L and leaves stem and foot
     visually joined, so all three of those words stay legible. It is also a
     heavy geometric poster stencil, which is closer to Burnaby Stencil than Big
     Shoulders Stencil was. It costs 37% more width at the same size, which the
     heading sizes below already account for.

     IT SHIPS ONE WEIGHT (400). Asking for 700 or 900 gets a synthesised bold that
     thickens the strokes until the stencil bridges close up and the cuts
     disappear, which removes the only reason to use it. Every rule that sets
     --font-stencil must also set font-weight 400.

     BOTH ADDED FACES ARE DISPLAY ONLY. The original rule stands where it matters:
     body copy is Manrope and nothing else. A stencil's open counters break up at
     paragraph sizes, and a distressed typewriter loses its ink texture below about
     13px and just reads as a rendering fault. So --font-stencil is for the
     wordmark and the two largest heading levels, --font-type is for eyebrows and
     short labels, and neither ever sets a sentence. */
  --font-display: 'Big Shoulders Display', 'Arial Narrow', system-ui, sans-serif;
  --font-stencil: 'Saira Stencil One', 'Big Shoulders Display', 'Arial Narrow', system-ui, sans-serif;
  --font-type:    'Special Elite', 'Courier New', ui-monospace, monospace;
  --font-sans:    'Manrope', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;

  /* Fluid scale. Every step clamps so nothing needs a media query to be
     readable at 390px or sane at 1600px. */
  --fs-hero:  clamp(3.25rem, 12vw, 9rem);
  --fs-h1:    clamp(2.5rem, 7vw, 5rem);
  --fs-h2:    clamp(1.9rem, 4.4vw, 3.1rem);
  --fs-h3:    clamp(1.35rem, 2.6vw, 1.85rem);
  --fs-h4:    clamp(1.1rem, 1.9vw, 1.3rem);
  --fs-lead:  clamp(1.05rem, 1.9vw, 1.3rem);
  --fs-body:  1rem;
  --fs-sm:    0.9rem;
  --fs-xs:    0.8rem;
  --fs-micro: 0.72rem;

  --lh-tight: 0.92;
  --lh-snug:  1.14;
  --lh-body:  1.62;

  /* Display type is set tight and uppercase; the eyebrow labels are wide.
     Named --track-* rather than --ls-* on purpose: --ls is Logan Square's
     accent, and --ls-deep sitting next to --ls-wide was a genuine trap. */
  --track-tight:   -0.02em;
  --track-normal:  0;
  --track-wide:    0.08em;
  --track-widest:  0.18em;

  --fw-body:    400;
  --fw-medium:  500;
  --fw-semi:    600;
  --fw-bold:    700;
  --fw-black:   900;

  /* ----------------------------------------------------------------- space */
  --sp-1:   0.25rem;
  --sp-2:   0.5rem;
  --sp-3:   0.75rem;
  --sp-4:   1rem;
  --sp-5:   1.5rem;
  --sp-6:   2rem;
  --sp-7:   3rem;
  --sp-8:   4rem;
  --sp-9:   6rem;
  --sp-10:  8rem;

  /* Section rhythm, fluid so the page breathes on desktop without stranding
     content on a phone. */
  --sp-section: clamp(3.5rem, 9vw, 7.5rem);

  /* Minimum touch target. Not a spacing step, which is why it is not --sp-N: it
     is a fixed accessibility floor that must not scale with the rhythm. Every
     button, nav link, tab, gallery tile and footer link is audited against it at
     390x844, 430x932 and 375x667. Reach for min-height rather than more padding,
     so a target grows without moving the type off its baseline. */
  --tap-min: 44px;

  /* Height of the sticky mobile buy bar, and therefore the amount of bottom
     padding <body> reserves so the bar cannot cover the end of the footer.

     This has to be a token because two rules 500 lines apart depend on the same
     number, and they were previously an unrelated 76px and a padding sum that
     happened to add up. The reservation adds env(safe-area-inset-bottom) on top,
     because the bar does too: on a notched iPhone the bar is this tall PLUS the
     home-indicator inset, and a flat 76px left about 34px of footer underneath
     it on a Pro Max. Sized for the two-line label case, which is what a 25
     character pre-launch label does at 375px. */
  --cta-bar-h: 96px;

  --gutter: clamp(1.15rem, 4vw, 2.5rem);
  --max:    1240px;
  --max-narrow: 760px;
  --max-wide:   1500px;

  /* ---------------------------------------------------------------- shape */
  --r-sm:   3px;
  --r-md:   6px;
  --r-lg:   12px;
  --r-pill: 999px;

  --border: 1px solid var(--line);

  /* --------------------------------------------------------------- shadow */
  --sh-sm:  0 2px 10px rgba(0, 0, 0, 0.4);
  --sh-md:  0 12px 32px rgba(0, 0, 0, 0.5);
  --sh-lg:  0 28px 70px rgba(0, 0, 0, 0.62);
  --sh-accent: 0 18px 50px -18px var(--accent-glow);
  /* An untinted halo in the component's own accent, for a hover bloom. Paired
     with --sh-accent rather than replacing it: --sh-accent is a lift (offset
     down, cast under the card), this is a glow (centred, no offset). */
  --sh-bloom:  0 0 48px -6px var(--accent-glow);

  /* --------------------------------------------------------------- motion */
  /* Lifted from miramar-theatre-redesign, which had these right.
     Nobody should ever type ease-in-out in this project. */
  --dur-instant: 80ms;
  --dur-fast:    160ms;
  --dur-base:    240ms;
  --dur-slow:    400ms;
  --dur-slower:  640ms;
  --dur-reveal:  760ms;
  --dur-marquee: 38s;

  --ease-entrance: cubic-bezier(0.16, 1, 0.30, 1);
  --ease-exit:     cubic-bezier(0.70, 0, 0.84, 0);
  --ease-emphasis: cubic-bezier(0.83, 0, 0.17, 1);
  --ease-standard: cubic-bezier(0.32, 0.72, 0, 1);
  /* --ease-release overshoots. Only ever pair it with --dur-fast, or it reads
     bouncy and cheap. */
  --ease-release:  cubic-bezier(0.34, 1.42, 0.64, 1);

  /* ------------------------------------------------------- torn edges ---
     Scare City's most distinctive shape is not its type, it is the ripped
     block: buttons and labels sit on red rectangles with rough torn ends, like
     tape pulled off a roll. Reproduced here as clip-path polygons, so it costs
     no image, no request and no licence, and it scales to any button width.

     Three variants because one repeated tear reads as a logo rather than as
     torn paper. Points are irregular on purpose. Percentages, not pixels, so
     the tear tracks the element's own box.

     A NOTE THAT MATTERS FOR ACCESSIBILITY. clip-path clips the focus ring too,
     which would silently delete the keyboard indicator. So the tear is never
     applied to the interactive element itself, only to a background layer
     behind it. See .btn--tear in components.css.

     POINT COUNT MATTERS MORE THAN AMPLITUDE. The first version used eight points
     an edge with a gentle 6% swing and read as a wobble, like a badly drawn
     rectangle. Torn paper is high frequency and uneven: short runs, sharp
     reversals, and the occasional deep bite. So these carry fourteen points an
     edge with the depth varying between 1% and 15%, and no two adjacent points
     move by the same amount.

     AND THE X SPACING HAS TO BE UNEVEN TOO. Fourteen points at even intervals with
     a smooth depth range produced tidy scallops, which read as a decorative
     ticket edge rather than as a tear. Varying the RUN LENGTH between points, and
     letting the depth jump from 1% to 15% between neighbours, is what makes it
     read as paper that gave way at different rates. */
  --tear-a: polygon(
    0% 4%, 4% 12%, 11% 2%, 15% 8%, 24% 15%, 31% 3%, 36% 9%, 45% 1%,
    52% 13%, 59% 5%, 68% 11%, 74% 2%, 83% 14%, 90% 6%, 96% 12%, 100% 3%,
    100% 97%, 95% 88%, 88% 99%, 81% 92%, 73% 100%, 66% 86%, 58% 96%, 51% 100%,
    44% 89%, 37% 98%, 29% 91%, 22% 100%, 14% 87%, 8% 97%, 3% 92%, 0% 99%);
  /* Same grammar, different phase, so two torn things side by side are not
     obviously the same shape stretched. */
  --tear-b: polygon(
    0% 11%, 7% 3%, 12% 14%, 20% 5%, 27% 12%, 34% 1%, 42% 9%, 49% 15%,
    56% 4%, 64% 10%, 70% 2%, 78% 13%, 86% 5%, 92% 11%, 97% 2%, 100% 8%,
    100% 90%, 93% 99%, 86% 87%, 79% 97%, 71% 100%, 63% 90%, 55% 98%, 48% 88%,
    40% 100%, 33% 93%, 25% 99%, 18% 89%, 11% 96%, 5% 100%, 0% 91%);
  /* Shallower, for small chips where a deep tear would eat into the text. */
  --tear-c: polygon(
    0% 4%, 9% 0%, 19% 5%, 30% 1%, 42% 6%, 54% 1%, 66% 5%, 78% 0%, 89% 4%, 100% 1%,
    100% 97%, 90% 100%, 79% 95%, 67% 100%, 55% 94%, 43% 99%, 31% 95%,
    20% 100%, 10% 96%, 0% 99%);

  /* ------------------------------------------------- entrance gate scene ---
     The gate is footage now, not a drawing, so the old house/bat/moon inks are
     gone. What is left is the frame around the video and the type over it. */
  --gate-letterbox: #000000;
  /* The video is dark at the edges and the wordmark sits over it, so the plate
     gets a soft vignette rather than a flat scrim: enough separation for the
     type without dulling the one warm light source in the shot. */
  --gate-vignette: radial-gradient(120% 88% at 50% 42%,
                    transparent 34%, rgba(0, 0, 0, 0.42) 72%, rgba(0, 0, 0, 0.78) 100%);
  /* Holds its density down to about 44% of the height, which is where the plate
     ends. The first version had faded to nothing by then, and the house's brightest
     feature (a green-lit window with a figure standing in it, dead centre) sat
     directly behind the age line and the skip link and swallowed both. */
  --gate-topveil:  linear-gradient(180deg, rgba(0, 0, 0, 0.74) 0%,
                    rgba(0, 0, 0, 0.46) 24%, rgba(0, 0, 0, 0.26) 44%,
                    transparent 66%);

  /* ------------------------------------------------------ soundtrack dock ---
     The floating player. Sits above the sticky CTA bar but below the drawer,
     because a nav that cannot be closed over a player is the worse failure. */
  --dock-w: 340px;
  --z-dock: 150;

  /* ----------------------------------------------------------------- misc */
  --nav-h: 68px;
  --z-nav:    100;
  --z-drawer: 200;
  --z-modal:  300;
  /* The entrance gate sits above everything, including the drop modal, because
     while it is closed nothing behind it should be reachable. */
  --z-gate:   400;
}

/* --------------------------------------------------------------------------
   Neighborhood scopes.
   Put one of these on <body> (or any wrapper) and every component inside
   picks up that neighborhood's accent with no per-component overrides.
   -------------------------------------------------------------------------- */

/* --accent-2 is the flyer's secondary colour: the drip, the glow, the second
   ink. Use it sparingly, for a drip rule or a hover, never for body text. */

[data-hood="river-north"]  { --accent: var(--rn); --accent-deep: var(--rn-deep); --accent-glow: var(--rn-glow); --accent-2: var(--rn-2); }
[data-hood="west-loop"]    { --accent: var(--wl); --accent-deep: var(--wl-deep); --accent-glow: var(--wl-glow); --accent-2: var(--wl-2); }
[data-hood="wicker-park"]  { --accent: var(--wp); --accent-deep: var(--wp-deep); --accent-glow: var(--wp-glow); --accent-2: var(--wp-2); }
[data-hood="logan-square"] { --accent: var(--ls); --accent-deep: var(--ls-deep); --accent-glow: var(--ls-glow); --accent-2: var(--ls-2); }
[data-hood="lincoln-park"] { --accent: var(--lp); --accent-deep: var(--lp-deep); --accent-glow: var(--lp-glow); --accent-2: var(--lp-2); }
[data-hood="wrigley"]      { --accent: var(--wg); --accent-deep: var(--wg-deep); --accent-glow: var(--wg-glow); --accent-2: var(--wg-2); }

/* --------------------------------------------------------------------------
   Reduced motion.
   Clamp every duration to effectively zero at the token level, so a component
   author cannot accidentally animate past the user's preference.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-instant: 0.01ms;
    --dur-fast:    0.01ms;
    --dur-base:    0.01ms;
    --dur-slow:    0.01ms;
    --dur-slower:  0.01ms;
    --dur-reveal:  0.01ms;
    --dur-marquee: 0.01ms;
  }
}
