/* ==========================================================================
   TYPED TOKENS
   Custom properties are untyped by default, so the browser cannot interpolate
   them — it swaps one string for another and every colour that depends on them
   snaps. Registering these as <color> makes the TOKEN animatable, which means
   anything downstream of it (color, currentColor, fill, stroke, borders,
   backgrounds) interpolates for free, with no per-element transition.

   Only the tokens the adaptive header actually swaps are registered. Typing
   them is otherwise inert: it changes nothing about how they cascade.
   ========================================================================== */
@property --text-primary   { syntax: "<color>"; inherits: true; initial-value: #282827; }
@property --text-secondary { syntax: "<color>"; inherits: true; initial-value: #57534E; }
@property --text-muted     { syntax: "<color>"; inherits: true; initial-value: #78716C; }
@property --text-accent    { syntax: "<color>"; inherits: true; initial-value: #0C0A09; }
@property --accent-mark    { syntax: "<color>"; inherits: true; initial-value: #76C596; }
@property --border-subtle  { syntax: "<color>"; inherits: true; initial-value: #E7E5E4; }
@property --border-default { syntax: "<color>"; inherits: true; initial-value: #D6D3D1; }
@property --border-control { syntax: "<color>"; inherits: true; initial-value: #78716C; }
@property --surface-hover  { syntax: "<color>"; inherits: true; initial-value: #F5F5F4; }
/* The page ground and the tint it mixes towards. Registered for the same
   reason as the header's tones — base.css mixes these two into the body
   background, and typing them is what lets a theme change ease that ground
   instead of snapping it. The mix ratio itself is deliberately NOT typed:
   see the body rule in base.css. */
@property --surface-page   { syntax: "<color>"; inherits: true; initial-value: #FAFAF9; }
@property --surface-tint   { syntax: "<color>"; inherits: true; initial-value: #D6D3D1; }

/* ==========================================================================
   GRUMLAW DESIGN SYSTEM — TOKENS
   --------------------------------------------------------------------------
   Two deliberate layers:

   1. PRIMITIVES  (--gl-*)  Raw brand values. Fixed. Never theme-dependent.
                            These are the values that map 1:1 into Elementor
                            Site Settings -> Global Colors. See docs/elementor-port.md
   2. SEMANTICS   (--surface-*, --text-*, --border-*)  Role-based. These flip
                            between light and dark. Elementor cannot express
                            these, so they live here and are the reason the
                            theme switch is a single-file change.

   RULE: components consume SEMANTIC tokens only. Never a raw --gl-* value,
   and never a literal hex. If a component needs a colour that has no semantic
   token, the token is missing — add it here rather than hardcoding.
   ========================================================================== */

:root {
  /* ---------------------------------------------------------------
     PRIMITIVES — BRAND
     Sourced from the live grumlaw.com Elementor global kit.
     --------------------------------------------------------------- */
  --gl-sage:            #76C596;  /* Elementor: Primary + Accent            */
  --gl-ink:             #282827;  /* Elementor: taken from the logo artwork */
  --gl-paper:           #FAFAF9;  /* Elementor: page ground                 */
  --gl-cream:           #E2D2BF;  /* Elementor: warm secondary surface      */
  --gl-slate:           #333333;  /* Elementor: Secondary                   */

  /* Sage ramp — derived from --gl-sage at fixed hue 144.3 / sat 40.5%.
     Only 400 and 700 are used as *text*; both are contrast-verified.  */
  --gl-sage-50:  #EDF7F1;
  --gl-sage-100: #DBF0E4;
  --gl-sage-200: #BFE4CE;
  --gl-sage-300: #9BD5B2;
  --gl-sage-400: #7AC799;  /* accent text on dark  — 8.71:1 on stone-900 */
  --gl-sage-500: #61BC86;
  --gl-sage-600: #439E68;
  /* Darkened sage. NOT the brand colour and never used as one — its only job
     is the light-mode focus ring, where 3:1 against paper is a hard
     requirement and brand sage manages 1.97:1. An invisible focus ring is
     not a trade worth making for hue fidelity on a 2px outline. */
  --gl-sage-700: #357D52;  /* focus ring only — 4.78:1 on paper          */
  --gl-sage-800: #275D3D;
  --gl-sage-900: #1B402A;
  --gl-sage-950: #11271A;

  /* Warm neutral ramp. Warm (not blue-grey) is a brand decision — it is what
     makes the off-white read as paper rather than as default UI grey.       */
  --gl-stone-50:  #FAFAF9;
  --gl-stone-100: #F5F5F4;
  --gl-stone-200: #E7E5E4;
  --gl-stone-300: #D6D3D1;
  --gl-stone-400: #A8A29E;
  --gl-stone-500: #78716C;
  --gl-stone-600: #57534E;
  --gl-stone-700: #44403C;
  --gl-stone-800: #292524;
  --gl-stone-900: #1C1917;
  --gl-stone-950: #0C0A09;

  --gl-white: #FFFFFF;
  --gl-black: #000000;

  /* Support — used only for form validation + status, never decoratively. */
  --gl-danger:  #B4442F;
  --gl-warning: #B07A2B;

  /* ---------------------------------------------------------------
     PRIMITIVES — TYPE

     Helvetica is the primary face and carries everything: headings, body,
     UI. Roboto Condensed is the secondary face and is used deliberately
     sparingly — see the usage rule below.

     Neither is a licensed webfont. Helvetica is a system face; Roboto
     Condensed is free from Google Fonts. There is no domain-locked kit in
     the critical path. See docs/fonts.md
     --------------------------------------------------------------- */

  /* Helvetica is absent on Windows and Android, where Arial and Roboto are
     the metrically compatible stand-ins. The optical stroke below matters
     most on those platforms, since Arial runs slightly lighter. */
  --font-sans: "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;

  /* Compressed structural labels and numerals ONLY — never reading text,
     never headings. See the usage rule in base.css. */
  --font-condensed: "Roboto Condensed", "Helvetica Neue Condensed",
                    "Arial Narrow", sans-serif;

  /* ---------------------------------------------------------------
     OPTICAL STROKE
     A stroke in the text's own colour, which thickens the glyph and softens
     its corners. Two things make this work properly:

     1. The width is in `em`, so it tracks font-size. The live site's fixed
        0.75px is muddy at 16px and invisible on an 80px headline.
     2. It must be re-declared per element, NOT inherited.
        -webkit-text-stroke-width is an inherited property, so an `em` value
        set once on <body> computes to a fixed px there and every descendant
        inherits that px — the exact bug this is meant to avoid. base.css
        therefore declares it on `*`, which re-resolves em per element.

     Display gets proportionally less than body: an em-linear stroke over-
     thickens at large sizes, where the glyph already carries enough weight.
     --------------------------------------------------------------- */
  --stroke-text:    0.012em;
  --stroke-display: 0.008em;

  /* Type scale — retuned for Helvetica. The previous scale was built for a
     condensed display face and ran far too large for a grotesque, which sets
     much wider at the same point size. */
  --text-2xs:  0.6875rem;  /* 11px — eyebrow/label only              */
  --text-xs:   0.75rem;    /* 12px                                   */
  --text-sm:   0.875rem;   /* 14px                                   */
  --text-base: 1rem;       /* 16px — body floor, never go below      */
  --text-lg:   1.125rem;   /* 18px — lead paragraph                  */
  --text-xl:   1.375rem;   /* 22px                                   */

  --display-sm: clamp(1.5rem,  1.25rem + 1.1vw, 2.125rem);
  --display-md: clamp(1.875rem, 1.4rem + 2.1vw, 3rem);
  --display-lg: clamp(2.25rem, 1.6rem + 3.0vw, 4rem);
  --display-xl: clamp(2.75rem, 1.8rem + 4.2vw, 5rem);

  /* Helvetica sets tight. 0.92 suited a condensed face with tall caps and
     would collide ascenders and descenders here. */
  --leading-tight:   1.04;  /* display only              */
  --leading-snug:    1.2;
  --leading-normal:  1.5;
  --leading-relaxed: 1.65;  /* long-form body copy       */

  /* Negative tracking on display type is a brand characteristic, carried over
     from the live site's -0.5px on Helvetica. Long-form prose stays at 0 —
     tracking reading text costs legibility. */
  --tracking-display: -0.022em;
  --tracking-ui:      -0.01em;
  --tracking-label:   0.09em;   /* uppercase labels only */
  --tracking-normal:  0;

  --weight-normal:    400;
  --weight-medium:    500;
  --weight-semibold:  600;
  --weight-bold:      700;  /* Helvetica Bold — display + headings */
  --weight-black:     900;

  /* ---------------------------------------------------------------
     PRIMITIVES — SPACE / SHAPE / MOTION
     --------------------------------------------------------------- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs:  0.75rem;
  --space-sm:  1rem;
  --space-md:  1.5rem;
  --space-lg:  2rem;
  --space-xl:  3rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;

  /* The measure a short list of items is held to — the missions trips and the
     care routes. Wider than this and four facts in a row sit a third of a
     screen apart; narrower and the copy starts breaking badly. */
  --measure-list: 56rem;
  --space-4xl: 8rem;

  /* Measured from the live site: 96px between sections at desktop, easing
     down on smaller screens so mobile does not inherit desktop's air. */
  --section-y:       clamp(3rem, 1.5rem + 4.5vw, 6rem);   /* -> 96px */
  --section-y-tight: clamp(2rem, 1.25rem + 2.5vw, 3.5rem);

  /* Hero card heights. The live site sets these per Elementor breakpoint
     rather than by aspect ratio — the card is 783px tall at both 1280 and
     1600, so it is a fixed height, not a ratio. Mirrored exactly. */
  --hero-h:    33rem;     /* 528px — mobile  */
  --hero-h-md: 35.25rem;  /* 564px — tablet  */
  /* Desktop: the card fills the browser height, less the header above it and a
     margin below so it never runs flush to the fold — the edge of the card has
     to be visible for it to read as a card rather than as the page. svh rather
     than vh so a mobile browser's collapsing toolbar does not crop it. */
  --hero-h-lg: calc(100svh - var(--header-h) - var(--space-lg));

  /* Radii are deliberately NOT uniform. The rule is: the larger the
     surface, the larger the radius. A pill button next to a 28px media
     card is what stops the page reading as a template.               */
  --radius-xs:    4px;   /* tags, swatches            */
  --radius-sm:    8px;   /* inputs, small controls    */
  --radius-md:    14px;  /* content cards             */
  --radius-lg:    20px;  /* panels, grouped blocks    */
  --radius-xl:    32px;  /* hero + media cards        */
  --radius-pill:  999px; /* buttons, chips, avatars   */

  --border-width:       1px;
  --border-width-thick: 2px;

  /* Shadows are warm-tinted and directional, not neutral drop shadows.
     Used ONLY for elements that genuinely float (menus, modals, sticky
     header on scroll). Cards on the page are flat by design.          */
  --shadow-menu:  0 12px 32px -8px rgb(40 40 39 / 0.18),
                  0 2px  6px  -2px rgb(40 40 39 / 0.10);
  --shadow-modal: 0 32px 64px -16px rgb(40 40 39 / 0.28),
                  0 4px  12px -4px  rgb(40 40 39 / 0.12);
  --shadow-sticky: 0 1px 0 0 var(--border-subtle);
  /* A small control floating over live content, not a panel. --shadow-menu is
     built for a menu-sized surface and on a 44px puck it reads as a smudge
     under the button rather than as lift. Same warm tint and same direction,
     shorter throw and about half the alpha. It is the only separation a
     borderless glass control gets, so it has to be soft AND present. */
  --shadow-float: 0 6px 16px -4px rgb(40 40 39 / 0.16),
                  0 2px  4px -2px rgb(40 40 39 / 0.08);

  --ease-out:  cubic-bezier(0.22, 1, 0.36, 1);
  /* easeOutCubic — measured from the live site's menu toggle. */
  --ease-toggle: cubic-bezier(0.33, 1, 0.67, 1);
  --dur-toggle:  260ms;
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast:   140ms;
  /* --- brand mark (wordmark <-> monogram) -------------------------------
     220ms was measured from the live site, which crossfades both marks on one
     curve. That reads badly here for a reason the live site shares but never
     fixed: the two marks are anchored at the same left edge, so the GL sits
     directly on top of "gru" for the whole run. One curve keeps the summed
     opacity at 1.00 throughout, which means the midpoint is not a dissolve —
     it is both marks legible at half strength, a 40ms smear of doubled
     strokes.

     So the outgoing mark leaves on a shorter clock than the incoming one
     arrives on, and the two curves are shaped to pass each other low: the
     summed opacity dips to ~0.47 through the middle. That dip is the whole
     technique — it trades a moment of lightness for a swap that is never
     doubled. The scale settle below covers the dip, so it reads as one
     movement rather than a blink.

     Direction is carried by which rule the timings are declared in, never by
     transition-delay: the exit timing sits with the state a mark leaves and
     the entry timing with the state it enters, so reversing mid-scroll picks
     up the correct curve from wherever the value already is. A delay would
     instead freeze the mark mid-reversal for its length. It also means the
     reduced-motion reset in base.css collapses this to an instant swap with
     nothing left over, since that reset zeroes durations but not delays. */
  --dur-brand-out:  160ms;
  --ease-brand-out: cubic-bezier(0.3, 0.7, 0.6, 1);   /* sheds opacity early */
  --dur-brand-in:   260ms;
  --ease-brand-in:  cubic-bezier(0.5, 0, 0.2, 1);     /* holds low, lands soft */
  /* One scale for both marks, deliberately outlasting the fade so the arriving
     mark is seen to come to rest rather than simply being finished.

     easeOutCubic, and the gentlest curve the settle can use. The site's usual
     --ease-out is easeOutQuint, which puts 75% of the travel in the first
     third — by the time the monogram is legible at all the movement is over,
     and the arriving mark looks stamped rather than settled. Cubic splits the
     travel roughly evenly between the window where the wordmark is still
     legible and the window where the monogram has become legible, so whichever
     mark the eye is on is the one that is visibly moving. Same numbers as
     --ease-toggle by coincidence, not by reference: that one is measured off
     the live menu toggle and must stay free to change. */
  --dur-brand-settle:  300ms;
  --ease-brand-settle: cubic-bezier(0.33, 1, 0.68, 1);
  --dur-normal: 240ms;
  --dur-slow:   420ms;

  /* --- navigation motion (the mobile menu) ------------------------------
     One curve for anything that moves LATERALLY between two states: the
     sub-menu push and the return. It leaves fast and settles long, which is
     what makes a panel read as a physical layer rather than a fade between
     two pictures. Entry and exit are deliberately asymmetric — a menu that
     leaves as slowly as it arrives feels like it is arguing with you.      */
  --ease-nav:      cubic-bezier(0.32, 0.72, 0, 1);
  --dur-pane:      380ms;   /* sub-menu push / back */
  --dur-menu-in:   420ms;   /* panel entrance */
  --dur-menu-out:  240ms;   /* panel exit — always shorter than entry */
  --stagger-menu:   38ms;   /* delay between menu rows on entry */

  /* Fluid — viewport minus a constant 32px gutter (verified 1216px @1280,
     1536px @1600) — until it boxes out on ultrawide displays. */
  --container:        2200px;
  --container-narrow: 780px;  /* reading measure for long-form copy */
  --gutter:           2rem;   /* constant 32px at every breakpoint */

  /* Header height. Referenced by scroll-padding and every sticky offset, so it
     must stay a single source of truth. */
  --header-h: 85px;   /* measured on the live site */

  --z-base: 1;
  --z-sticky: 100;
  --z-menu: 200;
  --z-modal: 300;

/* ==========================================================================
   GRUMLAW KIDS — SUB-BRAND PRIMITIVES
   Not invented: every value here is lifted from artwork that already exists.
   The sky, the grass, the sun and the moon come from the scene SVGs; the clay
   and blossom ramps come from the five characters. That is what makes this a
   sub-brand rather than a second theme — the page is coloured out of its own
   illustrations, so the ground under the copy and the ground under the animals
   are literally the same greens and blues.
   ========================================================================== */
  --kid-sky-50:   #F4FAFB;
  --kid-sky-100:  #E4F2F4;
  --kid-sky-200:  #D6EBEE;
  --kid-sky-300:  #C9E7EB;  /* the sky itself                                */
  --kid-sky-400:  #A9D6DE;
  --kid-sky-700:  #2F5F6D;
  --kid-sky-900:  #16323F;  /* the night sky                                 */
  --kid-sky-950:  #0C1A20;

  --kid-pine-300: #93C4A8;  /* night hover                                   */
  --kid-pine-400: #83B99C;  /* the grass, lifted for night                   */
  --kid-pine-500: #517361;  /* the grass                                     */
  --kid-pine-600: #406552;  /* day hover                                     */
  --kid-pine-700: #194430;  /* the Kids mark                                 */
  --kid-pine-night: #334B3E;  /* the grass after dark — see .theme-kids     */

  --kid-sun-300:  #FCDE80;  /* moon and stars                                */
  --kid-sun-500:  #E8CF24;  /* the sun                                       */

  --kid-clay-300: #EFBE76;  /* fox                                           */
  --kid-clay-500: #BD6E29;  /* deer, wolverine                               */
  --kid-clay-700: #8A481F;
  --kid-blossom-400: #EB9CC4;
}

/* ==========================================================================
   SEMANTIC LAYER — LIGHT (default)
   ========================================================================== */
:root,
[data-theme="light"] {
  color-scheme: light;

  --surface-page:     var(--gl-stone-50);
  --surface-raised:   var(--gl-white);
  --surface-sunken:   var(--gl-stone-100);
  --surface-hover:    var(--gl-stone-100);
  --surface-active:   var(--gl-stone-200);
  --surface-cream:    var(--gl-cream);
  --surface-accent:   var(--gl-sage);
  --surface-accent-hover: var(--gl-sage-500);
  --surface-accent-subtle: var(--gl-sage-50);
  --surface-inverse:  var(--gl-ink);

  /* Deep surfaces — the message card and the footer. These are dark slabs in
     BOTH themes, so they cannot use --surface-inverse, which flips. In light
     mode stone-950 already reads as a dark slab against paper. In dark mode the
     page ground IS stone-950, so the slab has to go deeper than the page or its
     edge disappears — true black is what makes it read as a surface rather than
     as the page continuing. */
  --surface-deep: var(--gl-stone-950);

  /* A step off the page ground. The live site uses stone-200 behind its
     "Coming Up" section; this goes one further, because at stone-200 the band
     was only 1.20:1 against paper and read as a rendering artefact rather than
     a deliberate surface. stone-300 lifts that to 1.43:1 — clearly present,
     still nowhere near a slab. Light steps DOWN from paper, dark steps UP from
     near-black, so the relationship to the page reads the same either way. */
  --surface-tint: var(--gl-stone-300);

  /* The tint fades over a longer beat than ordinary UI motion. A full-screen
     ground changing colour is a large event in the corner of the eye, and at
     normal transition speeds it registers as a flicker rather than a shift. */
  --dur-tint: 900ms;

  /* Entrance motion. The number that matters is not how long the entrance
     feels in isolation — it is how far the page has scrolled before it ends.
     At 700ms a phone flicking at ~1200px/s carries the element most of a
     screen while it is still arriving, so the content is permanently a beat
     behind the thumb. 480ms lands it inside a quarter-screen of travel, which
     is the difference between motion that accompanies the scroll and motion
     that trails it. Read at rest it is still unhurried; read in motion it is
     finally on time. */
  --dur-reveal: 480ms;
  --stagger:     50ms;
  /* Decelerate only. Entrances have no reason to ease IN: the element is not
     coming from anywhere the viewer watched it leave, so acceleration at the
     start just delays the arrival.

     easeOutQuart, not easeOutExpo. The two are the same idea at different
     severities, and the severity has to be read against the duration: expo
     spends 90% of its travel in the first third, which over 700ms reads as a
     graceful settle and over 480ms reads as a snap followed by a long crawl
     to the finish. Quart keeps the deceleration and drops the spike, so the
     shorter beat still has a shape. */
  --ease-reveal: cubic-bezier(0.25, 1, 0.5, 1);

  /* The full-bleed black media card is a fixed brand element. In dark mode
     it cannot stay pure ink or it disappears into the page — so it gets its
     own token rather than reusing --surface-inverse.                      */
  --surface-media:        var(--gl-ink);
  --surface-media-border: transparent;

  --text-primary:    var(--gl-ink);
  --text-secondary:  var(--gl-stone-600);
  --text-muted:      var(--gl-stone-500);
  /* Sage splits in two, by ROLE rather than by shade.

     --accent-mark is the brand colour and never changes value to suit a
     background: fills, underlines, rules, icons, large decorative numerals. It
     was previously darkened to sage-700 (#357D52) for text, which is a
     different green — beside the Give button it read as its own colour rather
     than as the same one.

     --text-accent is what reading text may take. Sage is a light-value green:
     on the light page ground it measures 1.97:1, so text there stays ink and
     lets --accent-mark carry the emphasis as an underline or rule instead. In
     dark mode sage clears 9.84:1 and text takes it directly. This is the only
     way the brand colour appears at full strength everywhere it is seen and
     the words next to it stay readable. */
  --accent-mark:     var(--gl-sage);
  --text-accent:     var(--gl-ink);
  --text-on-accent:  var(--gl-ink);
  --text-on-inverse: var(--gl-stone-50);
  --text-on-media:   var(--gl-stone-50);
  --text-danger:     var(--gl-danger);

  --border-subtle:  var(--gl-stone-200);
  --border-default: var(--gl-stone-300);
  /* The edge of an interactive control, which WCAG 1.4.11 holds to 3:1 against
     its background — a rule that does NOT apply to the hairlines and dividers
     --border-default draws. Outline buttons were using that hairline colour and
     came out at 1.49:1 on white: visible if you knew it was there, invisible if
     you did not. Kept as its own token so raising it never thickens every rule
     on the site. */
  --border-control: var(--gl-stone-500);
  --border-strong:  var(--gl-stone-400);
  --border-accent:  var(--gl-sage);
  --border-inverse: rgb(250 250 249 / 0.18);

  --focus-ring: var(--gl-sage-700);

  --logo-src: url("../img/grumlaw-logo.svg");

  --scrim: rgb(40 40 39 / 0.55);

  /* Header scrim as bare channels, so alpha can be varied per gradient stop.
     Stops must never interpolate toward the `transparent` keyword — that is
     transparent BLACK, and ramping a near-white to it drags grey through the
     midtones, which reads as a dull colour cast over the hero. */
  --header-scrim: 250 250 249;
  /* Peak scrim alpha. Light mode needs more than dark: it sets DARK text over
     whatever passes beneath, so the material has to lift an arbitrary — often
     bright, saturated — backdrop far enough for that text to hold. Dark mode
     sets light text on a dark scrim, which is already the easier direction. */
  --header-scrim-a: 0.86;
}

/* ==========================================================================
   SEMANTIC LAYER — DARK
   Warm dark, built from the stone ramp. Deliberately NOT a blue-black:
   the brand ground is warm paper, so its inverse is warm too.
   ========================================================================== */
[data-theme="dark"] {
  color-scheme: dark;

  --surface-page:     var(--gl-stone-950);
  --surface-raised:   var(--gl-stone-900);
  --surface-sunken:   var(--gl-black);
  --surface-hover:    var(--gl-stone-800);
  --surface-active:   var(--gl-stone-700);
  --surface-cream:    #3A3128;              /* cream, held at equal warmth  */
  --surface-accent:   var(--gl-sage);       /* brand sage never shifts      */
  --surface-accent-hover: var(--gl-sage-300);
  --surface-accent-subtle: var(--gl-sage-950);
  --surface-inverse:  var(--gl-stone-50);

  --surface-media:        var(--gl-stone-900);
  --surface-media-border: var(--gl-stone-800);
  --surface-deep:         var(--gl-black);   /* deeper than the page ground */
  --surface-tint:         var(--gl-stone-800);

  --text-primary:    var(--gl-stone-50);
  --text-secondary:  var(--gl-stone-300);
  --text-muted:      var(--gl-stone-400);
  --accent-mark:     var(--gl-sage-400);
  --text-accent:     var(--gl-sage-400);
  --text-on-accent:  var(--gl-ink);         /* sage always carries ink text */
  --text-on-inverse: var(--gl-ink);
  --text-on-media:   var(--gl-stone-50);
  --text-danger:     #E4806B;

  --border-subtle:  var(--gl-stone-800);
  --border-default: var(--gl-stone-700);
  --border-control: var(--gl-stone-400);
  --border-strong:  var(--gl-stone-600);
  --border-accent:  var(--gl-sage);
  --border-inverse: rgb(40 40 39 / 0.18);

  --focus-ring: var(--gl-sage-400);

  /* Light-on-dark reads optically heavier than dark-on-light, so the same em
     stroke looks fatter here. Pull it back to keep the two themes matched. */
  --stroke-text:    0.008em;
  --stroke-display: 0.005em;

  --logo-src: url("../img/grumlaw-logo-light.svg");

  --shadow-menu:  0 12px 32px -8px rgb(0 0 0 / 0.6),
                  0 2px  6px  -2px rgb(0 0 0 / 0.4);
  --shadow-modal: 0 32px 64px -16px rgb(0 0 0 / 0.7),
                  0 4px  12px -4px  rgb(0 0 0 / 0.5);
  --shadow-float: 0 6px 16px -4px rgb(0 0 0 / 0.5),
                  0 2px  4px -2px rgb(0 0 0 / 0.34);

  --scrim: rgb(0 0 0 / 0.7);
  --header-scrim: 12 10 9;
  --header-scrim-a: 0.62;
}

/* Respect the OS when the user has expressed no preference. The [data-theme]
   attribute is always written by theme.js, so this only governs first paint
   before any choice has been stored. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
    color-scheme: dark;
    --surface-page: var(--gl-stone-950);
    --surface-raised: var(--gl-stone-900);
    --surface-sunken: var(--gl-black);
    --surface-hover: var(--gl-stone-800);
    --surface-active: var(--gl-stone-700);
    --surface-cream: #3A3128;
    --surface-accent-hover: var(--gl-sage-300);
    --surface-accent-subtle: var(--gl-sage-950);
    --surface-inverse: var(--gl-stone-50);
    --surface-media: var(--gl-stone-900);
    --surface-media-border: var(--gl-stone-800);
    --surface-deep: var(--gl-black);
    --surface-tint: var(--gl-stone-800);
    --text-primary: var(--gl-stone-50);
    --text-secondary: var(--gl-stone-300);
    --text-muted: var(--gl-stone-400);
    --accent-mark: var(--gl-sage-400);
    --text-accent: var(--gl-sage-400);
    --text-on-inverse: var(--gl-ink);
    --text-danger: #E4806B;
    --border-subtle: var(--gl-stone-800);
    --border-default: var(--gl-stone-700);
    --border-control: var(--gl-stone-400);
    --border-strong: var(--gl-stone-600);
    --border-inverse: rgb(40 40 39 / 0.18);
    --focus-ring: var(--gl-sage-400);
    --stroke-text: 0.008em;
    --stroke-display: 0.005em;
    --logo-src: url("../img/grumlaw-logo-light.svg");
    --scrim: rgb(0 0 0 / 0.7);
    --header-scrim: 12 10 9;
    --header-scrim-a: 0.62;
  }
}

/* ==========================================================================
   GRUMLAW KIDS — SEMANTIC OVERRIDES
   Scoped to the page, not the site. Everything a Kids page paints with is
   re-pointed at the sub-brand ramps; everything structural — the type scale,
   the spacing, the motion, the button shapes — is untouched, so the page is
   recognisably the same site in a different key.

   --text-primary deliberately does NOT move: green body text on a blue ground
   is a costume, not a palette.

   The accent DOES move, and it is the one place this sub-brand overrules a
   site-wide rule. Give and Plan A Visit are sage everywhere else; here they
   are the hill the page is standing on. Because every .surface-accent panel
   derives its own text, borders and hovers from --text-on-accent, re-pointing
   these four tokens carries the whole accent system with it.
   ========================================================================== */
.theme-kids {
  --surface-page:     var(--kid-sky-50);
  --surface-raised:   var(--gl-white);
  --surface-sunken:   var(--kid-sky-200);
  --surface-hover:    var(--kid-sky-100);
  --surface-active:   var(--kid-sky-200);
  /* The tinted band takes the hero's own sky. It measures a slighter step off
     the page than the stone tint it replaces, but it is a step in HUE as well
     as in value, and a coloured band at 1.3:1 reads where a grey one would
     not — and it rhymes with the sky the page opens on. */
  --surface-tint:     var(--kid-sky-300);
  /* The footer becomes the deep pine of the mark. It is the same relationship
     as before — a dark slab under the page — in the sub-brand's own colour,
     and it gives the animals standing on its edge a ground that belongs to
     them rather than a neutral black bar. */
  --surface-deep:     var(--kid-pine-700);

  /* Grass ground, sky type — the two colours of the hero, on the button.
     The literal sky (#C9E7EB) measures 4.05:1 on the grass, which clears the
     3:1 a button SHAPE needs but not the 4.5:1 its LABEL needs. One step up
     the same ramp reaches 4.61:1 and is the same colour to look at; darkening
     the grass instead would have put the button in a different green from the
     hill behind it, which is the one thing this pairing exists to avoid. */
  /* One token for the grass, consumed by BOTH the hill in the hero and the
     band directly under it, so the two cannot drift apart. The hill used to be
     the artwork darkened by a filter, which made its night colour an accident
     of the filter maths — it landed on #252F29, a green measuring 1.03:1
     against the night sky. The horizon had effectively disappeared. Painting
     the silhouette through a mask instead makes the colour a decision. */
  --kid-grass: var(--kid-pine-500);

  --surface-accent:        var(--kid-pine-500);
  --surface-accent-hover:  var(--kid-pine-600);
  --surface-accent-subtle: var(--kid-sky-100);
  --text-on-accent:        var(--kid-sky-100);

  --accent-mark:      var(--kid-sun-500);
  --text-accent:      var(--kid-pine-700);
  --border-accent:    var(--kid-pine-500);

  /* Tinted from the mark rather than the stone ramp, so rules and hairlines
     sit in the same family as everything they separate. */
  --border-subtle:  rgb(25 68 48 / 0.12);
  --border-default: rgb(25 68 48 / 0.20);
  --border-strong:  rgb(25 68 48 / 0.34);
  --border-control: rgb(25 68 48 / 0.46);

  --focus-ring: var(--kid-pine-700);
  --header-scrim: 244 250 251;

  background-color: var(--surface-page);
}

[data-theme="dark"] .theme-kids {
  /* Night. The site's dark mode is a warm stone black because the brand ground
     is warm paper; the Kids ground is a blue-green sky, so its dark is the
     night sky from the hero rather than the same warm black. */
  --surface-page:     var(--kid-sky-950);
  --surface-raised:   #11262E;
  --surface-sunken:   #081419;
  --surface-hover:    var(--kid-sky-900);
  --surface-active:   #1D3F4E;
  --surface-tint:     #122B35;
  --surface-deep:     #081419;

  /* The sun is a light-value yellow: unusable as text on paper, excellent on
     a night sky. It carries both roles here for the same reason sage does on
     the main site's dark mode. */
  /* 1.42:1 against the night sky — a horizon you can see, where the
     filtered version gave 1.03:1 and read as one dark mass. */
  --kid-grass: var(--kid-pine-night);

  /* The primary button does NOT flip. Grass fill and light type in both
     appearances — one button, one colour, wherever you meet it, which is the
     same fixed pairing the Give button holds elsewhere on the site.
  
     Left bare by choice: on the night sky the pill measures 2.54:1 as a
     SHAPE, under the 3:1 a control needs to be identifiable on its own. It
     is fine on the page ground (3.36:1) and the footer (3.53:1) — only the
     hero band is short. A hairline fixed it and was removed on request; the
     label itself stays well clear at 4.61:1. */
  --surface-accent-subtle: #12302A;

  --accent-mark:      var(--kid-sun-300);
  --text-accent:      var(--kid-sun-300);
  --border-accent:    var(--kid-pine-400);

  --border-subtle:  rgb(201 231 235 / 0.12);
  --border-default: rgb(201 231 235 / 0.20);
  --border-strong:  rgb(201 231 235 / 0.34);
  --border-control: rgb(201 231 235 / 0.46);

  --focus-ring: var(--kid-sun-300);
  --header-scrim: 12 26 32;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) .theme-kids {
    /* Night. The site's dark mode is a warm stone black because the brand ground
       is warm paper; the Kids ground is a blue-green sky, so its dark is the
       night sky from the hero rather than the same warm black. */
    --surface-page:     var(--kid-sky-950);
    --surface-raised:   #11262E;
    --surface-sunken:   #081419;
    --surface-hover:    var(--kid-sky-900);
    --surface-active:   #1D3F4E;
    --surface-tint:     #122B35;
    --surface-deep:     #081419;

    /* The sun is a light-value yellow: unusable as text on paper, excellent on
       a night sky. It carries both roles here for the same reason sage does on
       the main site's dark mode. */
    /* 1.42:1 against the night sky — a horizon you can see, where the
       filtered version gave 1.03:1 and read as one dark mass. */
    --kid-grass: var(--kid-pine-night);

    /* The primary button does NOT flip. Grass fill and light type in both
       appearances — one button, one colour, wherever you meet it, which is the
       same fixed pairing the Give button holds elsewhere on the site.
    
       Left bare by choice: on the night sky the pill measures 2.54:1 as a
       SHAPE, under the 3:1 a control needs to be identifiable on its own. It
       is fine on the page ground (3.36:1) and the footer (3.53:1) — only the
       hero band is short. A hairline fixed it and was removed on request; the
       label itself stays well clear at 4.61:1. */
    --surface-accent-subtle: #12302A;

    --accent-mark:      var(--kid-sun-300);
    --text-accent:      var(--kid-sun-300);
    --border-accent:    var(--kid-pine-400);

    --border-subtle:  rgb(201 231 235 / 0.12);
    --border-default: rgb(201 231 235 / 0.20);
    --border-strong:  rgb(201 231 235 / 0.34);
    --border-control: rgb(201 231 235 / 0.46);

    --focus-ring: var(--kid-sun-300);
    --header-scrim: 12 26 32;
  }
}
