/*

base.css -  foundational CSS styles, design tokens, and global utilities

================================================================================
CSS ARCHITECTURE (READ THIS FIRST)
================================================================================

NAMING CONVENTIONS
- l-   = Layout / structural containers (grid, flow, wrappers, regions)
- c-   = Reusable components (cards, buttons, nav items, forms, media objects)
- u-   = Utilities (single-purpose overrides: spacing, display, text, alignment)
- is-/has- = State classes (applied by JS/template): is-open, has-error, etc.

FILE ARCHITECTURE
1) base.css  — FOUNDATION
    - Design tokens (CSS variables): color, type, spacing, radius, shadows, z-index
    - Resets/normalization + base element styles (html/body/a/p/img/button/form)
    - Global utilities + low-level layout primitives used everywhere
    - A11y/motion rules (focus styles, reduced motion, visually-hidden patterns)
    - MUST NOT contain page/template-specific styling

2) shell.css — SITE CHROME + TEMPLATE LAYOUTS (NOT AI-ASSEMBLED CONTENT)
    - Persistent site frame: header, nav, footer, mobile menu, breadcrumbs, etc.
    - Site wrappers: page shells, global grid scaffolds, sidebars, mastheads
    - Template layouts for content types: articles/blog, portfolio/case studies, team
    - System pages: archives, search/results, taxonomy listings
    - OK to rely on WordPress/template context (post meta, archive structure, etc.)
    - SHOULD NOT contain modular “sales/landing” sections intended for assembly

3) blocks.css — MODULAR PAGE SECTIONS (AI/PAGE-BUILDER ASSEMBLABLE)
    - Portable content sections (“blocks”): heroes, feature grids, pricing, FAQ,
        steps/process, testimonials, logo bands, comparison tables, CTA bands, etc.
    - Contact page sections are BLOCKS if they are modular (form section, map, etc.)
    - Blocks must be drop-in reusable and not depend on shell template context
    - Blocks should not style header/footer/nav or other persistent site chrome

DECISION RULES (QUICK TRIAGE)
- If it’s a universal default/token/utility → base.css
- If it’s persistent site frame OR editorial/system templates → shell.css
- If it’s a portable content section an LLM could choose + assemble → blocks.css

DEPENDENCY RULES (KEEP IT CLEAN)
- base.css may be used by everything.
- shell.css may use base utilities/tokens; should not reach into block internals.
- blocks.css may use base utilities/tokens; should not style shell chrome.

*/


/* ========== Tokens / Theme ==========

   ONE THEME — Midnight. Decided 2026-07-31 at the start of the style cutover.

   This file used to carry three palettes: a dark `:root`, a
   `:root[data-theme="light"]` user override and a duplicate of that override
   under `@media (prefers-color-scheme: light)` — 129 + 73 + 72 tokens. All the
   light ones are gone, along with the header toggle that switched them.

   The reason is measured, not aesthetic. The two-theme split generated three
   whole classes of bug on the demo, each of which cost a session:
     · chrome that drifted tone between themes — probe_footer.py sampled six
       chrome surfaces and found 6 of 6 differing, by up to 15/255
     · a green that needed a light-ground AND a dark-ground variant, so every
       accent decision had to be made twice
     · a glass header whose blur had to stay readable over both a light and a
       dark backdrop, which no single alpha can do
   With one dark theme all three are moot. The two-theme version is in git
   history (backup: .plans/style-cutover/backup/base.css.2026-07-31.bak).

   STRUCTURE — read this before adding a token.

   Block 1 is the SOURCE OF TRUTH: Current's Midnight tokens, verbatim from its
   tokens/signature-styles.css. Blocks 2-5 are the rest of the design system —
   radius, motion, space, type.

   Block 6 and 7 are ALIASES. The site has 905 var(--color-*) references and 240
   var(--green) references across 40 templates; renaming them would be a 15,000
   line edit to fix a colour problem. So the legacy names stay and point at the
   new values. There is exactly ONE value behind each — an alias cannot drift
   from its source the way a second hardcoded palette can, which is the failure
   this whole decision is trying to avoid. As components are converted their
   aliases can retire; the alias layer is scaffolding, not architecture.
*/

:root {
    /* One theme, so one scheme. Was `dark light`. */
    color-scheme: dark;

    /* ====================================================================
       1. MIDNIGHT — the source of truth. Verbatim from Current.
       ==================================================================== */
    --bg:            #06090c;
    --surface:       #0d1218;
    --surface-2:     #161c25;
    --ink:           #f5f5f6;
    --ink-2:         #b4bdc7;
    --ink-3:         #7a8593;
    --border:        #1a2230;
    --border-strong: #2a3340;
    --accent:        #1b944b;
    --accent-strong: #239e54;
    --accent-soft:   rgba(27, 148, 75, 0.16);
    --hover:         rgba(255, 255, 255, 0.04);
    --active:        rgba(255, 255, 255, 0.07);
    --focus:         rgba(27, 148, 75, 0.55);
    --shadow:        0 4px 12px rgba(0, 0, 0, 0.55);
    --shadow-float:  0 16px 40px rgba(0, 0, 0, 0.6);
    --ink-on-strong: #ffffff;

    /* The hero takes the footer's ground, on request ("note the tonal
       differences between the top and the bottom, I prefer the bottom").
       The glass header composites over the hero at rest, so the bar reads as
       one surface with what is behind it. */
    --surface-hero:  #06090c;
    --ink-hero:      #f5f5f6;

    /* Cards lift a FULL step off the page ground. --surface (#0d1218) is only
       7/255 above --bg (#06090c) and a dark page built on that step reads as
       one flat black field; #161c25 is 16/255 above it and the cards separate.
       Hover steps to #1d2530, Current's own nav-rail --surface-2. */
    --surface-card:       #161c25;
    --surface-card-hover: #1d2530;

    /* ── THE PLATE — a card lit from above-left, like a machined panel.
          Bright bevel top-left, dark bevel bottom-right, and a light raking
          down the face. Tokenised because it now dresses two families and a
          treatment copied by hand into a second place is a treatment that
          drifts. One definition, one look.

          ⚠ Do NOT add a texture pseudo-element to anything wearing this. An
          inset box-shadow paints above the element's background but BELOW
          positioned children, so any pseudo covering the card covers both
          bevels and flattens the 3D read — at any z-index that keeps it
          visible. The plate's material IS its light model. */
    /* The BACKDROP weave — the same 6px tile as the section bands, at DOUBLE
       the ink. A backdrop exists to be looked at THROUGH glass, and blur eats
       contrast: the band's 0.05 survives a pane as an even grey with no
       structure left, which is what made an earlier attempt read "bloaty and
       dull". 0.10 is the value the card lab used, and the lab is the version
       that got signed off. */
    --tex-hatch-strong: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Cpath d='M-1 1 L1 -1 M0 6 L6 0 M5 7 L7 5' stroke='%23f5f5f6' stroke-opacity='0.10' stroke-width='1'/%3E%3C/svg%3E");

    --plate-face:       linear-gradient(168deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.02) 30%, transparent 55%, rgba(0, 0, 0, 0.3) 100%); /* lint-ok: directional light model, not a decorative fill */
    --plate-face-hover: linear-gradient(168deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.03) 30%, transparent 55%, rgba(0, 0, 0, 0.28) 100%); /* lint-ok: directional light model, not a decorative fill */
    --plate-edge:       rgba(255, 255, 255, 0.1);
    --plate-bevel:       inset 1px 1px 0 rgba(255, 255, 255, 0.13), inset -1px -1px 0 rgba(0, 0, 0, 0.55);
    --plate-bevel-hover: inset 1px 1px 0 rgba(83, 212, 137, 0.3), inset -1px -1px 0 rgba(0, 0, 0, 0.6);

    /* ── AA. --accent #1b944b measures 3.44:1 as text on --bg and carries white
          at only 3.90:1: it is a SURFACE colour, not a text colour and not a
          white-text surface. These three are the live site's own values, settled
          in commit af4ebf3 "a11y: WCAG AA green-text contrast", reused verbatim
          rather than reinvented. Large green headings stay on --accent — they
          clear the 3:1 large-text bar. Do not "fix" those. */
    --accent-text:        #53d489;   /* green TEXT on dark      9.08 – 10.59 */
    --accent-solid:       #07792f;   /* green SURFACE + white text     5.55 */
    --accent-solid-hover: #0a6a2a;   /*                                6.76 */
    --accent-on-hero:     #53d489;
    /* The dark end of the green surfaces that carry a gradient — the quote
       button's chassis and the primary button's hover bloom. Derived from
       --accent-solid rather than written as a literal so it cannot drift away
       from the surface it bookends. White on it measures ~10:1.
       ⚠ Deleting this once took the quote button's fill to `none`: an undefined
       token makes the whole linear-gradient() invalid at computed-value time,
       so the declaration is dropped and the background falls through to
       transparent. The lint passed, verify_hover passed and audit_contrast
       passed — the button had simply stopped being filled, and only a
       screenshot showed it. Same failure as the 11 undeclared --color-* tokens
       found in step 1. */
    --accent-solid-deep:  color-mix(in srgb, var(--accent-solid) 55%, #000);
    --accent-solid-deeper: color-mix(in srgb, var(--accent-solid) 38%, #000);

    /* ── Glass. Blur is for surfaces that genuinely OVERLAP something else —
          the sticky bar, the dropdown, the mobile sheet. Page content stays
          opaque, because glass over nothing is just a lighter fill.

          The fill is the hero's own colour and the alpha is solved, not copied.
          Staging's rgba(0,25,33,0.05) was ported verbatim to the demo and was
          wrong for two measured reasons:
            1. Wrong hue — that teal is 8-15/255 off #06090c at EVERY alpha, so
               the bar could never match what it sits on. Filling with the
               ground's own colour matches at delta 0 for any alpha.
            2. Wrong alpha — at 0.05 the bar is 95% its backdrop, so when a white
               thumbnail scrolled under it chrome text measured 2.01:1: a white
               bar carrying white text. Caught by audit_glass_readability.py.
          Solved against the worst case (pure white): 0.74 is the lowest alpha
          where every chrome token still clears AA. 26% shows through, so content
          visibly moves behind the bar — it just cannot be washed out.
          Worst measured after: 7.41. Panels that must hold TEXT are near-opaque. */
    --glass:       rgba(6, 9, 12, 0.74);
    --glass-panel: rgba(6, 9, 12, 0.97);
    --glass-blur:  blur(10px);

    /* ====================================================================
       2. RADIUS — the scale AND the role rule.
       ====================================================================
       Sharp is the default. Radius means "this floats above the page", so it
       survives only on genuine floaters and on controls.

       These are ADDITIVE. The legacy --radius-* scale below is deliberately
       left alone: the do-not-touch rules keep radius on the mobile panel, the
       lightbox and — load-bearing — the quote button's 999px pill, whose
       travelling disc only ever worked BECAUSE the chassis is a pill. Blanket-
       zeroing --radius-* would flatten those 53 call sites at once and break the
       best interaction on the site. Radius is retired per component, in step 3. */
    --r-none: 0;
    --r-sm:   4px;      /* chips, tags, thumbnail-adjacent bits */
    --r:      6px;      /* buttons, inputs */
    --r-full: 999px;    /* genuinely circular: avatars, icon discs */

    /* ====================================================================
       3. MOTION — curves have ROLES, and the roles are enforced.
       ====================================================================
       Measured on the demo before this existed: 580 of 582 transitions ran on
       `ease`, the browser default. A card lifting, a colour changing, a menu
       opening and a tile crossing a button all moved identically — which is why
       the page read as competent rather than authored. design-lint's
       motion/no-bare-ease and motion/no-adhoc-duration enforce this table.

         state   nothing travels (colour, fill, border). Symmetric, because
                 there is no motion to accelerate or land.
         move    something covers a distance (a lift, a wipe, a rotate). Leaves
                 decisively, settles long — the shape of a thing with mass.
         enter   a surface arrives. Expo-out: fast to nearly there, then eases
                 the last few percent. No overshoot.
         exit    a surface leaves. Accelerates away, and is FASTER than enter —
                 waiting for a thing to go is the most irritating motion there
                 is, while watching a thing arrive is the point.

       Note: a panel's BASE rule is its closed state, so its transition is the
       EXIT and the :hover/:focus-within rule is the ENTER. Backwards gives you
       a menu that is slow to leave and abrupt to arrive. */
    --ease-state: cubic-bezier(.4, 0, .2, 1);
    --ease-move:  cubic-bezier(.2, 0, 0, 1);
    --ease-enter: cubic-bezier(.16, 1, .3, 1);
    --ease-exit:  cubic-bezier(.4, 0, .9, 1);

    --d-state: 120ms;
    --d-move:  220ms;
    --d-enter: 260ms;
    --d-exit:  150ms;
    --d-long:  420ms;

    --t-fast:  var(--d-state) var(--ease-state);
    --t:       var(--d-move)  var(--ease-move);
    --t-slow:  var(--d-long)  var(--ease-move);
    --t-enter: var(--d-enter) var(--ease-enter);
    --t-exit:  var(--d-exit)  var(--ease-exit);

    /* ====================================================================
       4. SPACE
       ==================================================================== */
    --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
    --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

    --wrap: 1200px;
    /* The widest content container on the site — .l-content's own max-width.
       The header bands cap their content at THIS, not at --wrap, so the navbar
       is never narrower than the page beneath it. It was: page content ran
       240..1680 at 1920 while the bar ran 328..1592, an 88px overhang on both
       sides on every single route. One token, both consumers, so they cannot
       drift apart again. */
    --wrap-content: 1440px;

    /* ── The weave ───────────────────────────────────────────────────────────
       A tight 45-degree hatch for the lighter SECTION grounds only.

       ⚠ Drawn as an SVG TILE, not a repeating-linear-gradient. A 1px line at
       45 degrees lands on a different subpixel offset in every repeat, so the
       gradient version rendered some lines noticeably stronger than others —
       the browser is antialiasing each one differently. A tile is rasterised
       once and repeated, so every line is identical.

       6px period: tight enough to read as a woven ground rather than as
       stripes. Section grounds only — on cards this reads as stripes ON the
       object, and a full-page version textures every template. */
    --tex-hatch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Cpath d='M-1 1 L1 -1 M0 6 L6 0 M5 7 L7 5' stroke='%23f5f5f6' stroke-opacity='0.05' stroke-width='1'/%3E%3C/svg%3E");

    /* The CARD weave — the same angle, tighter and cut DARK instead of light.
       On a card the ground hatch's light line reads as stripes laid on top of
       the object; a dark line at a 4px period reads as grain in the material
       instead. Owner's call: "tightly spaced and relatively dark".
       The band hatch above is deliberately left alone — it was signed off. */
    --tex-hatch-card: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Cpath d='M-1 1 L1 -1 M0 4 L4 0 M3 5 L5 3' stroke='%23000000' stroke-opacity='0.30' stroke-width='1'/%3E%3C/svg%3E");
    /* The chrome's side gutter. The header bands are full-bleed but their
       CONTENT is capped at --wrap PLUS this gutter on each side, so the brand
       and the utility strip land on exactly the same column as every section
       below. Capping at --wrap alone leaves them inset by one gutter (measured:
       brand at 152 against a content column starting at 120). */
    --header-gutter: clamp(1.5rem, 4vw, 2rem);

    /* Fallback only — the real value is measured onto :root by JS, because the
       bar is ~116px at 1440 and ~62px at 390 and a hardcoded token silently
       rots. Set in step 2 with the chrome. */
    --header-h: 116px;

    /* ====================================================================
       5. TYPE
       ====================================================================
       The DISPLAY tier is the site's own and is deliberately untouched — it was
       named in the brief as a thing to leave alone ("things we could leave
       alone are nice, large typography"). What changes is below it: the demo's
       first attempt imposed Current's TOOL ladder (12/13/14/15/16px), which is
       a 14px body on a marketing page and produced two competing type systems
       on one page. Body is 17px and weight above 600 is reserved for display. */
    --font-sans: "Onest", system-ui, -apple-system, BlinkMacSystemFont,
        "Segoe UI", sans-serif;
    --font-mono: "Fira Mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
        Consolas, "Liberation Mono", "Courier New", monospace;

    /* Typography: weights */
    --fw-thin: 100;
    --fw-light: 300;
    --fw-regular: 400;
    --fw-medium: 500;
    --fw-semibold: 600;
    --fw-bold: 700;
    --fw-extrabold: 800;
    --fw-black: 900;
    --fw-mono-regular: 400;
    --fw-mono-medium: 500;
    --fw-mono-bold: 700;
    /* Current's names for the same ladder, so ported component CSS reads right. */
    --fw-display: var(--fw-extrabold);
    --fw-semi:    var(--fw-semibold);

    /* Line-heights */
    --lh-tight: 1.1;
    --lh-default: 1.45;
    --lh-relaxed: 1.6;

    /* Type sizes. --fs-body 1.025rem -> 1.0625rem is the 17px marketing body;
       everything else is the live site's own scale, unchanged. */
    --fs-body: 1.0625rem;
    --fs-sm: 0.9rem;
    --fs-xs: 0.78rem;
    --fs-meta: 0.8125rem;                          /* 13px */
    --fs-label: 0.75rem;                           /* 12px mono caps */
    --fs-lead: clamp(1.0625rem, 1.35vw, 1.25rem);
    --fs-h1: clamp(2.5rem, 5vw, 3.6rem);
    --fs-h2: clamp(2rem, 3.4vw, 2.6rem);
    --fs-h3: clamp(1.45rem, 3vw, 1.9rem);
    --fs-h4: 1.25rem;
    --fs-display-lg: clamp(2.6rem, 4.2vw, 3.6rem);
    --fs-display: clamp(2.2rem, 3.6vw, 3rem);
    --fs-display-sm: clamp(1.6rem, 3.6vw, 2.4rem);
    --fs-brand: clamp(1rem, 2vw, 1.4rem);

    /* Uppercase tracking: 0.18em was decorative-wide. */
    --track-caps: 0.1em;

    /* ====================================================================
       6. BRAND ALIASES — legacy names, 240 direct references.
       ====================================================================
       The greens already MATCH Current exactly (#1b944b / #53d489 / #07792f);
       Current's accent came from SilverServers' green in the first place. So
       the only real palette move in this whole step is the GROUND: the teal
       #001921 / #041b25 / #06232f family becomes the neutral #06090c / #0d1218
       / #161c25 family, and the near-whites converge. */
    --primary: var(--green);
    --green: var(--accent);              /* #1b944b — unchanged */
    --lightgreen: var(--accent-text);    /* #53d489 — unchanged */
    --darkgreen: var(--accent-solid);    /* #07792f — unchanged */
    --brightgreen: var(--accent-strong);
    --white: var(--ink);                 /* #f9f9f9 -> #f5f5f6 */
    --black: var(--bg);                  /* #001921 -> #06090c */
    --verydark: var(--bg);               /* was the teal ground */
    --trueblack: #080a0a;
    --dark: var(--surface-2);
    --mid: var(--ink-3);
    --light: var(--ink-2);
    --blue: #377c9d;
    --lightblue: #5e9db7;
    --darkblue: #104464;
    --green-gradient: linear-gradient(135deg, var(--green), var(--darkgreen));
    --blue-gradient: linear-gradient(135deg, var(--blue), var(--darkblue));

    /* Legacy radius scale — RETIRED 2026-08-04, all consumers converted.
       --radius-sm (6.4px) folded into --r; --radius-md (12px) and --radius-lg
       (16px) had 24 and 4 consumers respectively and every one was a page card,
       panel or media figure, so they all went sharp. Both tokens are DELETED
       rather than redefined to 0 — a token named "md" that means "no radius" is
       a trap for whoever reaches for it next.

       --radius-pill survives for exactly one consumer: .c-mega-cta__btn, the
       Get-a-quote button. Its 999px chassis is load-bearing — the travelling
       disc only ever worked BECAUSE the button is a pill; that was rebuilt three
       times and got it wrong twice. Keeping it behind a token also hides it from
       design-lint's literal `999px` pill rule, so a future sweep cannot flatten
       it by accident. Do not "finish the job" by removing this. */
    --radius-pill: 999px;
    --shadow-soft: var(--shadow);
    --shadow-strong: var(--shadow-float);

    /* ── Status. The Midnight palette is green-only, so warn/danger/ok had no
          home and were scattered as raw hex across the monitor panels, the
          domain lifecycle, the pulse dots and the 404. They carry MEANING, not
          decoration, and a palette edit has to be able to find them.
          Values are the ones already in use, not reinvented. */
    --ok:         #27c93f;
    --warn:       #ffbd2e;
    --warn-deep:  #d7a10a;
    --danger:     #c54848;
    /* #c54848 is a SURFACE red: as text it measures 4.18:1 on --bg and 3.58:1
       on a card. Same split as --accent / --accent-text. */
    --danger-text: #e8807f;

    /* Highlighter colors */
    --yellow: #ffe066;
    --highlight-yellow: rgba(255, 224, 102, 0.55);
    --highlight-green: rgba(83, 212, 137, 0.45);
    --highlight-blue: rgba(94, 157, 183, 0.45);

    /* ====================================================================
       7. --color-* ALIASES — 905 references, 40 templates.
       ==================================================================== */
    --color-bg: var(--bg);
    --color-surface: var(--surface);
    --color-surface-alt: var(--surface-2);
    --color-text: var(--ink);
    --color-text-muted: var(--ink-2);
    --color-border-subtle: var(--border);
    --color-band-bg: var(--surface);
    --color-input-bg: var(--surface);
    --color-link: var(--accent-text);
    /* Links hover toward white. This was `var(--green)` for BOTH in the old dark
       theme, so `a:hover { color: var(--color-link-hover) }` had been resolving
       to the colour the link already was — a dead hover on every link on the
       site, pre-dating the cutover. Found by audit_dead_hover.py. --ink is the
       conventional dark-ground answer and it costs no new colour. */
    --color-link-hover: var(--ink);
    --color-accent-text: var(--accent-text);
    --color-accent-border: var(--accent-text);
    --color-accent-blue: var(--lightblue);

    --color-header-bg: var(--glass);
    --color-header-text: var(--ink);
    --color-header-icon: var(--ink);
    /* was --green: 3.44:1 as text. The accent-is-a-surface rule. */
    --color-header-icon-hover: var(--accent-text);

    --color-post-thumb-bg: var(--surface-2);
    --color-post-thumb-border: var(--border);
    --color-post-title: var(--ink);
    /* A category doesn't act, so it stops looking like a button: saturated
       green fill -> tint chip. */
    --color-post-category-bg: var(--accent-soft);
    --color-post-category-text: var(--accent-text);
    --color-post-date: var(--ink-3);

    --color-submenu-bg: var(--glass-panel);

    /* Opaque, not rgba(16,48,59,0.15). A translucent card over a near-black
       ground is a card you cannot see; --surface-card is the 16/255 step. */
    --color-card-bg: var(--surface-card);
    --color-card-bg-opaque: var(--surface-card);
    --color-card-border: var(--border);

    /* Footer + prefooter gradients -> flat theme surfaces. */
    --color-footer-bg: var(--bg);
    --color-footer-card-bg: transparent;
    --color-footer-border: var(--border);
    --color-prefooter-bg: var(--bg);
    --color-prefooter-text: var(--ink);
    --color-prefooter-text-muted: var(--ink-2);
    --color-prefooter-heading: var(--ink);
    --color-prefooter-link: var(--ink-2);
    --color-prefooter-link-hover: var(--accent-text);
    --color-footer-bg-gradient: var(--bg);
    --color-footer-heading: var(--ink);
    --color-footer-link: var(--ink-2);
    --color-footer-link-hover: var(--accent-text);
    --color-copyright-text: var(--ink-3);

    --color-btn-secondary-bg: var(--surface);
    --color-btn-secondary-border: var(--border-strong);
    --color-btn-secondary-text: var(--ink);
    --color-btn-outline-border: var(--border-strong);
    --color-btn-outline-text: var(--ink);
    --color-btn-outline-hover-bg: var(--hover);

    --color-tag-bg: var(--surface-2);
    --color-tag-text: var(--ink-2);
    --color-tag-border: transparent;
    --color-chip-bg: var(--surface-2);
    --color-chip-border: var(--border);

    --color-social-bg: var(--surface-2);
    --color-social-border: var(--border);
    --color-social-text: var(--ink-2);
    --color-social-hover-border: var(--accent);
    --color-social-hover-text: var(--accent-text);

    --color-placeholder: var(--ink-3);
    --color-placeholder-focus: var(--border-strong);

    --color-decorative-line-primary: var(--lightblue);
    --color-decorative-line-secondary: var(--ink-2);
    --color-decorative-line-hover-primary: var(--accent);
    --color-decorative-line-hover-secondary: var(--accent);
    --color-dust: var(--lightblue);

    --color-menu-panel-bg: var(--glass-panel);
    --color-menu-panel-border: var(--border);
    --color-menu-panel-text: var(--ink);
    --color-menu-panel-hover: var(--hover);

    /* ── Previously UNDEFINED. Measured 2026-07-31: these 11 names carried 52
          references across shell.css and windows.css and were declared nowhere,
          so every one of those declarations was invalid-at-computed-value-time
          and silently dropped by the browser — `.c-sidebar-section__title`
          rendered body-white instead of a heading colour, article body links
          and topic-bar links had no link colour at all. Verified with
          getComputedStyle: all 11 resolved to ''. Defining them here is the
          smallest fix; the ones belonging to dead families
          (.c-article-content, .c-category-nav, .c-category-filter, .c-post-list
          — 0 templates each) go away with those families in step 5.

          ⚠ --color-primary is used BOTH as a text colour and, on
          .c-topic-bar__link--active, as a background under var(--white). One
          token cannot be both — that is the accent-is-a-surface trap. Pointed
          at --accent-text here; audit_contrast.py settles the --active case. */
    --color-primary: var(--accent-text);
    --color-primary-light: var(--ink);   /* hover brightens toward white */
    --color-primary-dark: var(--ink);    /* the old name assumed a light ground */
    --color-accent: var(--accent-text);  /* focus rings */
    --color-heading: var(--ink);
    --color-text-base: var(--ink-2);
    --color-text-dark: var(--ink);
    --color-text-secondary: var(--ink-3);
    --color-border: var(--border);
    --color-bg-light: var(--surface);
    --color-bg-white: var(--surface-2);

    /* Background images */
    --bg-blob: url('/images/bg/blob.svg');

    /* Ambient radial glows. Not component gradients — these are the page's
       atmosphere and are kept as-is. */
    --gradient-overlay-1: rgba(83, 212, 137, 0.22);
    --gradient-overlay-2: rgba(83, 212, 137, 0.06);
    --gradient-overlay-3: rgba(94, 157, 183, 0.18);
    --gradient-overlay-4: rgba(94, 157, 183, 0.05);
    --gradient-overlay-5: rgba(104, 255, 210, 0.25);
    --gradient-overlay-6: rgba(104, 255, 210, 0.04);
}

/* ========== Base Reset ========== */

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

html {
    scroll-behavior: smooth;
    scroll-padding: 125px;
}

html,
body {
    margin: 0;
    padding: 0;
    overflow-x: clip;
}

body {
    font-family: var(--font-sans);
    font-size: var(--fs-body);
    font-weight: var(--fw-regular);
    line-height: var(--lh-default);
    min-height: 100vh;
    color: var(--color-text);
    background-color: var(--color-bg);
}

/* ========== Typography ========== */

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans);
    font-weight: var(--fw-regular);
    line-height: 1.15;
    margin: 0 0 0.35em;
    letter-spacing: -0.01em;
    color: var(--color-text);
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

p {
    margin: 0 0 0.75rem;
    line-height: var(--lh-relaxed);
    color: var(--color-text);
}

/* ========== Links ========== */

a {
    color: var(--color-link);
    text-decoration: none;
    text-decoration-skip-ink: auto;
}

a:hover {
    color: var(--color-link-hover);
}

a:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--green) 70%, transparent);
    outline-offset: 3px;
    border-radius: 0.35rem;
}

/* Utility: consistent underline link treatment (shared across shell + blocks) */
.u-link-underline {
    color: var(--color-link);
    text-decoration: none;
    /* Not gradient FILLS: `linear-gradient(<colour> 0 0)` is a solid one-colour
       layer, the standard technique for an underline that animates via
       background-size. These two layers ARE the underline. */
    background-image: linear-gradient(var(--color-link-hover) 0 0), linear-gradient(var(--color-link) 0 0); /* lint-ok: solid colour layers drawing an underline, not a gradient fill */
    background-size: 0 2px, 100% 1px;
    background-position: 0 100%, 0 100%;
    background-repeat: no-repeat;
    padding-bottom: 1px;
    transition: background-size var(--t);
}

.u-link-underline:hover {
    background-size: 100% 2px, 100% 1px;
}

@media (prefers-reduced-motion: reduce) {
    .u-link-underline { transition: none; }
}

/* ========== Utility Classes ========== */

/* Typography utilities */
.u-heading-display-lg {
    font-family: var(--font-sans);
    font-size: var(--fs-display-lg);
    line-height: var(--lh-tight);
    font-weight: var(--fw-regular);
}

.u-heading-display {
    font-family: var(--font-sans);
    font-size: var(--fs-display);
    line-height: var(--lh-tight);
    font-weight: var(--fw-black);
}

.u-heading-small {
    font-family: var(--font-sans);
    font-size: var(--fs-h3);
    line-height: var(--lh-tight);
    font-weight: var(--fw-bold);
}

.u-heading-mono {
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    line-height: var(--lh-tight);
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.u-heading-subtitle {
    font-family: var(--font-sans);
    font-size: var(--fs-sm);
    line-height: var(--lh-relaxed);
}

.u-text-muted {
    color: var(--color-text-muted);
}

/* Font weight utilities - data attribute pattern (preferred) */
[data-weight="thin"] { font-weight: var(--fw-thin); }
[data-weight="light"] { font-weight: var(--fw-light); }
[data-weight="regular"] { font-weight: var(--fw-regular); }
[data-weight="medium"] { font-weight: var(--fw-medium); }
[data-weight="semibold"] { font-weight: var(--fw-semibold); }
[data-weight="bold"] { font-weight: var(--fw-bold); }
[data-weight="extrabold"] { font-weight: var(--fw-extrabold); }
[data-weight="black"] { font-weight: var(--fw-black); }

/* u-weight-* class scale + u-font-mono--medium/--bold + u-text-blue + u-text-gradient--blue
   deleted 2026-04-28 — zero callsites. Active utilities below: u-font-mono (base + --small),
   u-text-green, u-text-gradient. Pages use --fw-* token in component CSS instead. */

/* Monospace utilities */
.u-font-mono {
    font-family: var(--font-mono);
    font-weight: var(--fw-mono-medium);
    letter-spacing: 0.03em;
}

.u-font-mono--small {
    font-size: var(--fs-xs);
    font-weight: var(--fw-mono-medium);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* Color utilities */
.u-text-green { color: var(--green); }

/* Gradient text utilities */
.u-text-gradient {
    background: var(--green-gradient);
    background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

/* Accessibility utilities */
.u-skip-link {
    position: absolute;
    top: -100%;
    left: 1rem;
    z-index: 10000;
    padding: 0.75em 1.5em;
    /* --accent-solid, not --accent-text. Measured 2026-07-31: white on
       --accent-text #53d489 is 1.88:1. --accent-solid #07792f is the token that
       exists for exactly this — a green SURFACE carrying white text, 5.55:1.
       (This read `var(--color-accent, #0057ff)` and --color-accent was declared
       nowhere, so the skip link had quietly been rendering in the fallback
       BLUE. Defining the token is what surfaced it.) */
    background: var(--accent-solid);
    color: var(--ink-on-strong);
    font-weight: 700;
    text-decoration: none;
    border-radius: 0 0 0.25rem 0.25rem;
}

.u-skip-link:focus {
    top: 0;
}

/* The site's focus ring is green (a:focus-visible above) and this element sits
   ON green, so it needs its own. */
.u-skip-link:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -5px;
}

.u-visually-hidden {
    position: absolute !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    width: 1px !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    height: 1px !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    padding: 0 !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    margin: -1px !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    overflow: hidden !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    clip: rect(0, 0, 0, 0) !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    white-space: nowrap !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    border: 0 !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
}

.u-sr-only {
    position: absolute !important;   /* lint-ok — the sr-only pattern REQUIRES !important — a component rule must not be able to un-hide content from assistive tech */
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

/* u-stack/--lg/u-cluster Every-Layout primitives deleted 2026-04-28 — zero callsites.
   Pages use component-scoped flex/grid (c-card-grid, c-feature-list, etc.). */

/* Eyebrow/label text utility */
.u-eyebrow {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-text-muted);
}

/* ========== Shared Component Primitives (used by shell + blocks) ========== */

/* Shared control shape. Was `--radius-pill` (999px) on all three — the single
   highest-leverage radius in the codebase: .c-btn alone appears on 32 templates.
   Buttons are CONTROLS, so they keep a radius; they just stop being pills.
   Tags and chips are smaller and take the smaller step. */
.c-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r);
    text-decoration: none;
    gap: 0.5em;
}

.c-tag,
.c-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r-sm);
    text-decoration: none;
    gap: 0.5em;
}
/* Common smooth transitions (no box-shadow) */
.c-btn {
    transition:
        border-color var(--t-fast),
        background-color var(--t-fast),
        color var(--t-fast),
        transform var(--t);
}

/* ========== Component: Tags & Chips ========== */

.c-tag {
    padding: 0.35rem 0.95rem;
    border: 1px solid var(--color-tag-border);
    background: var(--color-tag-bg);
    color: var(--color-tag-text);
}

/* c-tag color variants (--green/--blue/--light/--dark/--black) deleted 2026-04-28 — zero callsites. Active variants below. */

.c-tag--small {
    padding: 0.25rem 0.625rem;
    font-size: var(--fs-xs);
}

.c-tag--outline {
    background: transparent;
    border-color: var(--green);
    color: var(--green);
}

.c-chip {
    padding: 0.35rem 0.85rem;
    font-size: var(--fs-xs);
    background: var(--color-chip-bg);
    border: 1px solid var(--color-chip-border);
    color: var(--color-text);
}


/* ========== Component: Buttons ========== */

.c-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.85rem 1.35rem;
    font-size: 0.95rem;
    font-weight: 500;
    border: 0;
    cursor: pointer;
    white-space: nowrap;
}

/* ── The primary button: rest quiet, hover BLOOMS ───────────────────────────
   The mechanism is the one the header's external-destination chips used to
   have and lost: a calm surface at rest that, on hover, blooms into a rich
   three-stop diagonal gradient, takes a bright accent border, lifts 1px and
   throws a colour-keyed glow. Four moves arriving together. That styling was
   flattened during the cutover for two REAL reasons — it hovered to each
   destination's own brand gradient (a second and third palette inside our
   chrome) and white on #3cc1ca measured 1.9:1 — but flattening threw out the
   gesture along with its two defects. The gesture is good. Here it is in our
   own greens, where neither objection applies.

   Note where the vividness lives: in the BORDER and the GLOW, not the fill.
   That is how the original worked too — its fill was mostly dark and the
   bright brand colour was the 1px edge. Keeping the fill dark is also what
   keeps the white label readable: measured on real pixels, worst stop 5.09:1.

   What this replaced: a "kinetic gradient" — a 200%-wide green sweep that slid
   on hover — reading `var(--c1, #02784b), var(--c2, #0f9455) 51%,
   var(--c1, #044d37)`. --c1/--c2 were declared NOWHERE, so all three fallbacks
   always applied: three off-palette greens on the primary button across 21
   templates, including the home hero. Measured on real pixels the middle stop
   carried white at 3.90:1, under the 4.5 AA bar — the same 3.90 already
   documented for raw --accent. Every run of audit_contrast.py missed it,
   because a gradient leaves background-color transparent, so the audit climbed
   past the button and scored its white label against the page ground. */
.c-btn--primary {
    color: var(--white);
    background: var(--accent-solid);
    /* transparent, so the bloom's border-color has something to animate FROM
       without the button changing size on hover */
    border: 1px solid transparent;
    border-radius: var(--r);
    transition:
        background var(--t-fast),
        border-color var(--t-fast),
        box-shadow var(--t-fast),
        transform var(--t);
}

.c-btn--primary:hover {
    /* Stops derived from --accent-solid rather than written as literals, so the
       bloom follows the palette instead of drifting from it. lint-ok: this is
       the restored destination-bloom, a hover REVEAL rather than a resting
       decorative fill — the same standing the quote CTA's gradient has. */
    background: linear-gradient(135deg, var(--accent-solid-deep), var(--accent-solid), var(--accent-solid-deeper)); /* lint-ok: restored hover bloom, see above */
    border-color: var(--accent-text);
    color: var(--white);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--accent-text) 32%, transparent);
}


.c-btn--secondary {
    background: var(--color-btn-secondary-bg);
    color: var(--color-btn-secondary-text);
    border: 1px solid var(--color-btn-secondary-border);
}

.c-btn--outline-light {
    background: transparent;
    color: var(--color-btn-outline-text);
    border: 1px solid var(--color-btn-outline-border);
}

.c-btn--outline-light:hover {
    background: var(--color-btn-outline-hover-bg);
}



/* PayPal's own brand colours, and they have to be exact — this is a payment
   button and it must look like PayPal's. Raw hex is correct here for the same
   reason .ss-platform-card--instagram keeps Instagram's gradient: a third
   party's mark is not ours to re-theme. lint-ok */
.c-btn--paypal {
    background: #ffc439;   /* lint-ok — PayPal brand yellow */
    color: #003087;        /* lint-ok — PayPal brand blue */
    font-weight: 600;
    border: 1px solid #f0b72f;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    transition: background var(--t-fast), box-shadow var(--t-fast), transform var(--t);
}

.c-btn--paypal:hover {
    background: #f5bb30;   /* lint-ok — PayPal brand yellow, hover */
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
    color: #003087;        /* lint-ok — PayPal brand blue */
}

.c-btn--paypal:active {
    transform: translateY(1px);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

.c-btn--full {
    width: 100%;
    justify-content: center;
}


.c-btn--menu,
.c-btn--menu-close {
    /* ≥44×44 touch target (mobile audit 2026-06-03) */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0.5rem;
    background: transparent;
    color: var(--color-header-text);
    border: 0;
    cursor: pointer;
}

.c-btn--menu:hover,
.c-btn--menu-close:hover {
    color: var(--color-header-icon-hover);
}

.c-btn--menu-close {
    font-size: 1.5rem;
    line-height: 1;
}

/* Icon Lists */
.c-list-icon {
    list-style: none;
    padding: 0;
    margin: 0;
}

.c-list-icon li {
    position: relative;
    padding-left: 1.5rem;
    margin-bottom: 0.55rem;
}

.c-list-icon li::before {
    position: absolute;
    left: 0;
    top: 0.35rem;
    font-family: "Font Awesome 6 Free";
    font-weight: 500;
    font-size: 0.65rem;
}

.c-list-icon--arrow li::before {
    content: "\f061";
    color: var(--color-accent-text);
}
/* c-list-icon variants (--address/--phone/--email/--facebook/--instagram/--linkedin/--plain)
   deleted 2026-04-28 — zero callsites. Active variants: c-list-icon (base) + --arrow only. */

/* ========== Component: Decorative Line ========== */

/* ========== Component: Social Icons ========== */


/* ========== Selection ========== */

::selection,
::-moz-selection {
    color: var(--white);
    background: var(--green);
}

/* ========== Forms (scoped component) ========== */

.c-form {
    display: grid;
    gap: 1rem;
    margin-top: 1rem;
}

.c-form__row {
    display: grid;
    gap: 1rem;
}

@media (min-width: 720px) {
    .c-form__row--2up {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.c-form__field {
    display: grid;
    gap: 0.4rem;
}

/* The Last-name input borrows the NAME label sitting over the First input, and
   this spacer keeps the two on one baseline. Once the row stacks, there is no
   baseline to hold and the spacer would just open a blank line between them. */
.c-form__label-spacer {
    display: none;
}

@media (min-width: 720px) {
    .c-form__label-spacer {
        display: block;
    }
}

.c-form label {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-text-muted);
}

.c-form input,
.c-form textarea,
.c-form select {
    width: 100%;
    font: inherit;
    color: var(--color-text);
    background: var(--color-input-bg);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--r);
    padding: 0.75rem 0.9rem;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.08);
    transition:
        border-color var(--t-fast),
        box-shadow var(--t-fast),
        transform var(--t),
        background var(--t-fast);
}

.c-form textarea {
    min-height: 8rem;
    line-height: 1.5;
    resize: vertical;
}

.c-form input::placeholder,
.c-form textarea::placeholder {
    color: var(--color-placeholder);
}

.c-form input:focus,
.c-form textarea:focus,
.c-form select:focus {
    outline: none;
    border-color: var(--green);
    box-shadow:
        0 0 0 3px color-mix(in srgb, var(--lightgreen) 45%, transparent),
        0 18px 45px rgba(0, 0, 0, 0.16);
}

.c-form input:focus::placeholder,
.c-form textarea:focus::placeholder {
    color: var(--color-placeholder-focus);
}

.c-form__hint {
    font-size: var(--fs-sm);
    color: var(--color-text-muted);
    line-height: 1.45;
    margin: 0;
}

.c-form__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
    align-items: center;
    margin-top: 0.25rem;
}


/* ========== Form controls: sensible button default (no class) ========== */
/* If you apply .c-btn classes, they take over. This only catches plain buttons. */
.c-form button:not([class]),
.c-form input[type="submit"]:not([class]),
.c-form input[type="button"]:not([class]),
.c-form input[type="reset"]:not([class]) {
    appearance: none;
    -webkit-appearance: none;

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;

    font: inherit;
    font-family: var(--font-mono);
    font-size: 0.95rem;
    font-weight: 500;
    white-space: nowrap;

    padding: 0.85rem 1.35rem;
    border-radius: var(--r);
    cursor: pointer;

    background: transparent;
    color: var(--color-btn-outline-text);
    border: 1px solid var(--color-btn-outline-border);

    transition:
        border-color var(--t-fast),
        background var(--t-fast),
        color var(--t-fast),
        transform var(--t),
        box-shadow var(--t-fast);
}

.c-form button:not([class]):hover,
.c-form input[type="submit"]:not([class]):hover,
.c-form input[type="button"]:not([class]):hover,
.c-form input[type="reset"]:not([class]):hover {
    background: var(--color-btn-outline-hover-bg);
    box-shadow: var(--shadow-soft);
}

.c-form button:not([class]):focus-visible,
.c-form input[type="submit"]:not([class]):focus-visible,
.c-form input[type="button"]:not([class]):focus-visible,
.c-form input[type="reset"]:not([class]):focus-visible {
    outline: 2px solid color-mix(in srgb, var(--green) 70%, transparent);
    outline-offset: 3px;
}


/* ========== Line breaking ==========
   A runt — a last line carrying one short word — was reported on
   /services/website-hosting/: a paragraph ending "...just rent the space and
   power, that's" / "Colocation." Measuring line boxes across that one page
   found SEVEN of them, and every block reported `text-wrap: wrap`, i.e. the
   browser's plain greedy line breaker with nothing asked of it.

   Two different tools, because the two cases are different:

   `balance` on headings — evens the line lengths across the block, so a
   two-line headline stops stranding its last word. The spec caps it at a few
   lines, which is exactly the size of a headline and why it is wrong for body.

   `pretty` on body copy — keeps the greedy breaker but forbids the bad last
   line, which is the runt/orphan case. It costs nothing where unsupported: the
   value simply does not apply and you get today's behaviour back.

   Set as element defaults so new copy inherits the fix rather than each block
   rediscovering it. audit_typography.py measures the result. */
h1, h2, h3, h4,
.c-hero-title,
.ss-form-intro__headline {
    text-wrap: balance;
}

p, li, dd, blockquote, figcaption {
    text-wrap: pretty;
}

/* SHORT CENTRED prose gets `balance`, not `pretty`.
   `pretty` only protects the last line, and on a narrow centred measure that is
   not enough: the reported paragraph went from "Colocation." alone to
   "that's Colocation." alone — still a stranded line under two full-width ones.
   `balance` evens every line in the block instead, which is what a centred
   pull-quote-shaped paragraph needs. The spec caps balance at a few lines,
   which is exactly this shape and why it stays off body copy generally. */
.c-media-split__body,
.c-block--centered p,
.c-text-intro,
.ss-form-intro__body {
    text-wrap: balance;
}

/* ========== Dialog ==========
   The <dialog> element had NO styling anywhere in this codebase, so it rendered
   on Chromium's UA defaults — and under `color-scheme: dark` those resolve to
   background `Canvas` #121212, colour `CanvasText` #fff and `border: solid`,
   which computes to a 3px WHITE frame. Measured on /team/blake-noble/ and
   confirmed identical to a bare <dialog> in an otherwise empty dark page. It did
   not come from another site; it belonged to no design system at all, which is
   why it read as foreign.

   Styled here as an element default rather than as a component, so any dialog
   added later inherits it instead of repeating the same accident. The chrome
   deliberately echoes the .window idiom used by windows.js on this same page —
   a header strip with the title and controls, then a scrollable body. */
dialog {
    max-width: min(64rem, calc(100vw - 2 * var(--sp-5)));
    max-height: calc(100vh - 2 * var(--sp-6));
    padding: 0;
    color: var(--color-text);
    background: var(--surface-card);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--r);
    box-shadow: var(--shadow-float);
    overflow: hidden;
}

dialog::backdrop {
    background: rgba(6, 9, 12, 0.72);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.c-dialog__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-surface);
}

.c-dialog__title {
    margin: 0;
    font-size: var(--fs-h4);
    font-weight: var(--fw-semibold);
}

/* The reason this exists at all: the dialog shipped with NO close control. Esc
   did work, because the helper opens it with showModal() — but a modal whose
   only exit is a keystroke you have to already know is a trap, and it was
   reported as one. */
.c-dialog__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex: none;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--color-text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: background-color var(--t-fast), color var(--t-fast);
}

.c-dialog__close:hover,
.c-dialog__close:focus-visible {
    background-color: var(--hover);
    color: var(--color-text);
}

.c-dialog__body {
    padding: var(--sp-5);
    overflow-y: auto;
    max-height: calc(100vh - 2 * var(--sp-6) - 68px);
}

.c-dialog__body > :first-child { margin-top: 0; }
.c-dialog__body h4 {
    margin: var(--sp-5) 0 var(--sp-2);
    font-size: var(--fs-body);
    font-weight: var(--fw-semibold);
}
.c-dialog__body p,
.c-dialog__body ul { margin: 0 0 var(--sp-3); }
.c-dialog__body ul { padding-left: var(--sp-5); list-style: disc; }
.c-dialog__body li { margin-bottom: var(--sp-2); }
.c-dialog__body li > p { margin-bottom: var(--sp-2); }
.c-dialog__body code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    padding: 0.1em 0.35em;
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--r-sm);
}

/* ========== Rules / Dividers ========== */

hr {
    border-top: 1px solid var(--color-card-border);
    height: 0px;
    margin: 2rem 0;
    opacity: 0.5;
}

.c-form .c-btn {
    align-self: flex-start;
}

/* iOS auto-zooms the viewport when a focused text field is < 16px. Enforce a
   16px floor on small screens for text-entry controls only — radio/checkbox/
   range/buttons are excluded (they don't trigger zoom), and `1em` preserves any
   field already ≥16px. Mobile audit 2026-06-03. */
@media (max-width: 768px) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]),
    textarea,
    select {
        font-size: max(16px, 1em);
    }
}

/* ==========================================================================
   Reduced motion: disable decorative transitions & animations globally.
   Component-specific overrides live in shell.css and blocks.css.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;   /* lint-ok — the reduced-motion clamp, this rule's own sanctioned exception */
        animation-iteration-count: 1 !important;   /* lint-ok — the reduced-motion clamp, this rule's own sanctioned exception */
        transition-duration: 0.01ms !important;   /* lint-ok — the reduced-motion clamp, this rule's own sanctioned exception */
        scroll-behavior: auto !important;   /* lint-ok — the reduced-motion clamp, this rule's own sanctioned exception */
    }
}