/* ============================================================================
   base.css - THE SPARK CHASSIS. Do not restyle. Do not delete. Do not "clean up."
   ----------------------------------------------------------------------------
   This file is the reason a Spark site is accessible, and it is the ONLY part of
   the CSS that is not yours to redesign. It carries no personality: no brand
   colors, no fonts, no shadows, no radii, no layout opinions. Everything in here
   is either an accessibility guarantee or the functional behavior of a component
   the PHP chassis renders (the drawer, the consent banner, the form).

   theme.css is where you design. base.css is what keeps the design legal.

   WHY THIS FILE EXISTS
   -------------------
   These rules used to live inside theme.css, mixed in with the hero and the card
   styles. That meant "I don't want the template's look" and "I don't want the
   template's safeguards" were the same action - reject one, lose the other. A
   2026-07 fleet audit found exactly that: a site built without the template had
   no focus rings, and a site built FROM the template had dropped the
   reduced-motion guard while being customized. Neither was carelessness. It was
   a packaging bug, and this file is the fix.

   Loaded BEFORE theme.css, always. theme.css may override the look of anything
   here (a focus ring in your brand color is fine, and welcome). It may NOT
   remove the guarantee (`outline: none` with nothing in its place is not fine).

   Chassis integrity is a QA gate: skills/8-quality-assurance.md.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   1. TOKEN CONTRACT
   The names every Spark site must define. theme.css supplies the VALUES.
   These fallbacks exist only so a half-built page is never unstyled - they are
   not a design, and they are not verified against any client's background.
   ---------------------------------------------------------------------------- */
:root {
  /* Accessibility tokens (Hard Rule 20 / WCAG 2.1 AA).

     --color-accent-text is NOT optional and NOT decorative. A brand accent is
     usually a NON-TEXT color: fine on a rule, a dot or an icon stroke (3:1), and
     illegal the moment it carries text (4.5:1). It fails in two places that look
     like separate bugs and share one threshold:
        - the accent used AS TEXT      (eyebrow, link, small label, stat caption)
        - the accent as a SURFACE under a white label (primary button, consent Accept)
     One darkened variant fixes both. COMPUTE IT, never eyeball it:
        python "G:\Shared drives\Lingo\In-House Software\Accessibility Check\contrast.py" fix "#ACCENT"
     and paste the measured ratios into the comment in theme.css.

     On a DARK surface the correction runs the OTHER WAY - the accent must get
     LIGHTER, not darker. Rebind the token on dark panels. Getting this backwards
     took one site from 147 errors to 196. */
  --color-accent-text: currentColor;

  /* The focus ring is a UI component: it owes 3:1 against whatever it lands on
     (WCAG 1.4.11). A pale ring on a pale card is the same bug as pale text. */
  --focus-ring: currentColor;
  --focus-ring-width: 3px;
  --focus-ring-offset: 3px;
}

/* ----------------------------------------------------------------------------
   2. RESET
   ---------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; }

img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }

/* Long words and URLs must not force a horizontal scrollbar at 320px (1.4.10). */
p, h1, h2, h3, h4, h5, h6, li, td, th, dd, dt { overflow-wrap: break-word; }

/* ----------------------------------------------------------------------------
   3. SKIP LINK (WCAG 2.4.1 Bypass Blocks)
   The first thing a keyboard user reaches. It must be invisible until focused,
   then unmissable. It targets <main id="main">, which header.php opens.
   ---------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: 12px 16px;
  font-weight: 600;
  text-decoration: none;
  /* Restyle these two in theme.css to match the brand. Do not make it invisible. */
  background: #000;
  color: #fff;
}
.skip-link:focus { left: 16px; top: 16px; }

/* ----------------------------------------------------------------------------
   4. FOCUS RING (WCAG 2.4.7 Focus Visible)
   On EVERY interactive element, not just links - a keyboard user tabs through
   buttons, inputs, selects and <summary> too, and an unringed stop is a stop
   they cannot see.

   NEVER `outline: none` without a replacement. Designers delete this constantly
   because they think it is untidy. Deleting it blinds every keyboard user, and
   it is the single most common self-inflicted accessibility bug in web design.
   If it clashes with your design: restyle it, do not remove it.
   ---------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
details:focus-visible,
[tabindex]:focus-visible,
[contenteditable]:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: 2px;
}

/* ----------------------------------------------------------------------------
   5. SCREEN-READER-ONLY TEXT
   For text the accessibility tree needs and sighted users do not. Do NOT use
   display:none or visibility:hidden for this - those remove it from the
   accessibility tree too, which is the opposite of what you want.
   ---------------------------------------------------------------------------- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ----------------------------------------------------------------------------
   6. REDUCED MOTION (WCAG 2.3.3 / respect the user's stated preference)
   A global guard, so a hover-zoom or a reveal added later cannot escape it.
   This is exactly the block Complete Wheel's fork lost while being customized -
   which is why it lives here now, where it is not yours to delete.

   0.01ms rather than 0s so `transitionend` still fires and any JS waiting on it
   does not hang forever.
   ---------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ----------------------------------------------------------------------------
   7. MOBILE DRAWER - functional behavior (not its look)
   The drawer is a native <dialog popover>. This block is what makes it open,
   close, animate and stay keyboard-operable. Style its colors, width, type and
   spacing in theme.css; do not reimplement this machinery.

   The popover snaps open with no animation unless `display`/`overlay` are
   transitioned with allow-discrete AND an @starting-style is given for the entry.
   Reduced-motion users get the instant version for free via section 6.
   ---------------------------------------------------------------------------- */
body.is-locked { overflow: hidden; }   /* set by footer.php while the drawer is open */

.nav-mobile {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(360px, 90vw);
  height: 100vh;
  height: 100dvh;
  margin: 0;
  border: none;
  transform: translateX(100%);
  opacity: 0;
  transition:
    transform var(--dur-base, 250ms) var(--ease-out, ease),
    opacity   var(--dur-base, 250ms) var(--ease-out, ease),
    overlay   var(--dur-base, 250ms) var(--ease-out, ease) allow-discrete,
    display   var(--dur-base, 250ms) var(--ease-out, ease) allow-discrete;
}
.nav-mobile:popover-open { transform: translateX(0); opacity: 1; }
@starting-style {
  .nav-mobile:popover-open { transform: translateX(100%); opacity: 0; }
}
.nav-mobile::backdrop {
  background: rgba(0, 0, 0, 0);
  transition:
    background var(--dur-base, 250ms) var(--ease, ease),
    overlay    var(--dur-base, 250ms) var(--ease, ease) allow-discrete,
    display    var(--dur-base, 250ms) var(--ease, ease) allow-discrete;
}
.nav-mobile:popover-open::backdrop { background: rgba(0, 0, 0, 0.4); }
@starting-style {
  .nav-mobile:popover-open::backdrop { background: rgba(0, 0, 0, 0); }
}

/* ----------------------------------------------------------------------------
   8. FORM ERROR MESSAGING (WCAG 3.3.1 Error Identification)
   A red border is not an error message - a colorblind user cannot see red. The
   contact form ships `novalidate`, which means OUR JS owes the error text the
   browser would otherwise have supplied.

   .field-error is the per-field message; [role=status][aria-live=polite] is the
   region a screen reader announces. Both are hidden until they have something
   to say. Style them in theme.css; keep them announcing.
   ---------------------------------------------------------------------------- */
.field-error[hidden] { display: none; }
.field-error {
  display: block;
  font-size: 0.875rem;
  /* Restyle in theme.css. Whatever color you choose must clear 4.5:1. */
  color: #b3261e;
}

/* ----------------------------------------------------------------------------
   9. TARGET SIZE
   Icon-only controls (hamburger, close, social) need a real hit area. 44px is
   the pointer-target floor, and it keeps the focus ring from cropping the icon.
   ---------------------------------------------------------------------------- */
.nav-toggle,
.nav-mobile__close {
  min-width: 44px;
  min-height: 44px;
}
