:root {
  /* FS 5720.1 section 5 is authoritative for these three.
     Everything else below is derived from them for the site's own use,
     which paragraph 2.1.b leaves to the site. */
  --brand-primary:   #152230;   /* deep slate  */
  --brand-accent:    #2E9E8F;   /* teal - restraint required, see below */
  --brand-secondary: #4A5C6E;   /* supporting text */

  --color-bg: #f7f9fb;
  --color-surface: #ffffff;
  --color-text: var(--brand-primary);
  --color-muted: var(--brand-secondary);
  --color-primary: var(--brand-primary);
  --color-primary-light: #24384c;
  --color-border: #dce4ea;

  /* Text placed ON the primary colour. A token rather than a literal #fff,
     because in the dark scheme the primary becomes a LIGHT teal and white
     text on it measures 2.1:1 - unreadable. The pairing has to flip
     together, and it can only do that if both halves are tokens. */
  --color-on-primary: #ffffff;

  --color-success: #1a7a3c;
  --color-error:   #b3261e;

  /* The footer is dark in BOTH schemes, so its colours are pinned rather
     than derived. It previously took its background from --color-text,
     which was correct only by coincidence: the moment --color-text becomes
     near-white for a dark scheme, the footer turns white and its own
     near-white text vanishes. A surface that is deliberately dark is not
     the same idea as the text colour and no longer borrows from it.
     The footer is also what mark-reverse.svg is drawn for. */
  --color-footer-bg:    var(--brand-primary);
  --color-footer-text:  #c9d4dd;
  --color-footer-brand: #ffffff;
  --color-footer-link:  #9fb6c7;

  --radius: 8px;
  --max-width: 1080px;
  font-size: 16px;

  /* Tell the browser this page renders both schemes.
     Without this declaration the page is treated as light-only, and engines
     with automatic dark mode (Chrome on Android, Samsung Internet, several
     in-app browsers) apply their OWN inversion to it. That inversion darkens
     backgrounds but cannot reach inside an SVG, so a fixed dark-fill logo
     keeps its #152230 while the ground behind it turns dark - 1.14:1,
     measured, which is the logo disappearing. Declaring the schemes hands
     that decision back to this stylesheet, and the block below answers it. */
  color-scheme: light dark;
}

/* ---------------------------------------------------------------------------
   DARK SCHEME

   Only tokens are redefined here - no layout, no component rules. Every rule
   in the rest of this file already reads its colours through a token, so the
   whole page follows from this block alone.

   Every pair below is verified with technical/scripts/fawcett-contrast.sh
   against WCAG 2.2 SC 1.4.3 (4.5:1 text) and SC 1.4.11 (3:1 graphics), not
   chosen by eye. Lowest measured pair in this scheme is the teal accent at
   5.61:1; body text is 15.56:1.
   --------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg:      #0d151d;   /* below the brand slate, so surfaces can sit on it */
    --color-surface: #16232f;   /* the brand slate, lifted just off the background */
    --color-text:    #e6edf3;
    --color-muted:   #9fb3c4;

    /* The primary INVERTS its role: a dark fill carrying light text becomes a
       light fill carrying dark text. Keeping #152230 here would put a nearly
       invisible button on a nearly identical background. */
    --color-primary:       #4fc3b1;
    --color-primary-light: #6fd4c4;
    --color-on-primary:    #0d151d;

    --color-border: #24384c;

    /* The light-scheme greens and reds are chosen for white and are too dark
       to read here: #1a7a3c on #0d151d is 2.6:1. */
    --color-success: #4ade80;
    --color-error:   #ff6b6b;

    /* Footer tokens are NOT redefined. The footer is dark in both schemes by
       design, and mark-reverse.svg is drawn for that background. */
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.55;
}

.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}

h1, h2, h3 { line-height: 1.2; margin-top: 0; }
h1 { font-size: 2.5rem; }
h2 { font-size: 1.9rem; margin-bottom: 8px; }
h3 { font-size: 1.2rem; }

a { color: var(--color-primary-light); }

/* Header */
.site-header {
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 18px;
  padding-bottom: 18px;
}
.brand {
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--color-primary);
  text-decoration: none;
}
.nav a {
  margin-left: 28px;
  text-decoration: none;
  color: var(--color-text);
  font-weight: 500;
}
.nav a:hover { color: var(--color-primary-light); }

/* Hero */
.hero {
  padding: 88px 0 72px;
  text-align: center;
}
.hero h1 { color: var(--color-primary); max-width: 760px; margin-left: auto; margin-right: auto; }
.lede {
  max-width: 620px;
  margin: 20px auto 32px;
  color: var(--color-muted);
  font-size: 1.1rem;
}

.btn {
  display: inline-block;
  padding: 13px 26px;
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  font-size: 1rem;
}
.btn-primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.btn-primary:hover { background: var(--color-primary-light); }

/* Services */
.services { padding: 64px 0; }
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 24px;
  margin-top: 32px;
}
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 24px;
}
.card h3 { color: var(--color-primary); }
.card p { color: var(--color-muted); margin-bottom: 0; }

/* Approach */
.approach {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  padding: 64px 0;
}
.approach p { max-width: 680px; color: var(--color-muted); font-size: 1.05rem; }

/* Contact */
.contact { padding: 64px 0 88px; }
.contact-form {
  max-width: 520px;
  margin-top: 24px;
}
.field { margin-bottom: 18px; }
.field label {
  display: block;
  font-weight: 600;
  margin-bottom: 6px;
  font-size: 0.95rem;
}
.field input,
.field textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  background: var(--color-surface);
  color: var(--color-text);
}
.field input:focus,
.field textarea:focus {
  outline: 2px solid var(--color-primary-light);
  outline-offset: 1px;
}

/* Honeypot field — hidden from sighted users and screen readers alike.
   A real visitor never sees or interacts with this; only an automated
   form-filler that ignores CSS/ARIA state would populate it, which is
   exactly the signal server/routes/contact.js uses to reject the submission. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  margin-top: 14px;
  font-weight: 600;
  min-height: 1.2em;
}
.form-status[data-state="success"] { color: var(--color-success); }
.form-status[data-state="error"] { color: var(--color-error); }

/* Footer */
.site-footer {
  border-top: 1px solid var(--color-border);
  padding: 28px 0;
  color: var(--color-muted);
  font-size: 0.9rem;
}

@media (max-width: 620px) {
  h1 { font-size: 1.9rem; }
  .hero { padding: 56px 0 48px; }
  .nav a { margin-left: 16px; }
}

/* ---------------------------------------------------------------
   Multi-page structure: skip link, active nav, page heads, footer.
   Appended rather than rewritten so the existing hero, card, and
   form rules stay exactly as they were.
   --------------------------------------------------------------- */

.skip-link {
  position: absolute; left: -9999px;
  background: var(--color-primary); color: var(--color-on-primary);
  padding: .6rem 1rem; z-index: 100; border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; top: 0; }

.nav a.is-active { color: var(--color-primary); font-weight: 600; }

.page-head { padding: 3rem 0 1rem; background: var(--color-surface); border-bottom: 1px solid var(--color-border); }
.page-head h1 { margin: 0 0 .5rem; }
.page-head .meta { color: var(--color-muted); font-size: .9rem; margin: .25rem 0 0; }

.wrap.narrow { max-width: 720px; }

.service-detail { padding: 2.5rem 0; }
.service-detail.alt { background: var(--color-surface); }
.service-detail h2 { margin-top: 0; }
.service-detail + .service-detail { border-top: 1px solid var(--color-border); }

.ticks { list-style: none; padding: 0; margin: 1rem 0; }
.ticks li { position: relative; padding-left: 1.6rem; margin: .5rem 0; }
.ticks li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: .5rem; height: .5rem; border-radius: 50%; background: var(--color-primary);
}

.aside { color: var(--color-muted); font-size: .95rem; border-left: 3px solid var(--color-border); padding-left: 1rem; }

.hero-actions { display: flex; gap: .75rem; flex-wrap: wrap; justify-content: center; }
.btn-ghost { background: transparent; color: var(--color-primary); border: 1px solid var(--color-primary); }
.btn-ghost:hover { background: var(--color-primary); color: var(--color-on-primary); }

.card-link { font-weight: 600; text-decoration: none; }
.card-link:hover { text-decoration: underline; }

.cta { padding: 3rem 0; background: var(--color-primary); color: var(--color-on-primary); text-align: center; }
.cta h2 { margin-top: 0; color: var(--color-on-primary); }
.cta .btn-primary { background: var(--color-on-primary); color: var(--color-primary); }

.hint { display: block; color: var(--color-muted); font-size: .85rem; margin-top: .25rem; }

.site-footer { background: var(--color-footer-bg); color: var(--color-footer-text); padding: 2rem 0; margin-top: 0; }
.footer-inner { display: flex; flex-direction: column; gap: .75rem; align-items: center; text-align: center; }
.footer-brand { font-weight: 700; color: var(--color-footer-brand); margin: 0; }
.footer-nav { display: flex; gap: 1.25rem; flex-wrap: wrap; justify-content: center; }
.footer-nav a, .footer-note a { color: var(--color-footer-link); }
.footer-note { font-size: .9rem; margin: 0; }

@media (max-width: 600px) {
  .header-inner { flex-direction: column; gap: .5rem; }
  .nav { gap: .9rem; }
}

/* ===================================================================
   VERTICAL RHYTHM AND RESPONSIVE LAYOUT
   ===================================================================
   Spacing was set per-section, so every section chose its own padding
   and the gaps between them were whatever two neighbours happened to
   add up to. Below the hero buttons that came to 152px of empty space
   — 72px hero padding, 16px button margin, 64px section padding.

   These rules replace that with one scale. Every section uses the same
   token, so the rhythm is a property of the page rather than an
   accident of which two sections meet.

   clamp() makes the scale fluid: it shrinks on a phone and grows on a
   wide display without a breakpoint for each size. The middle value is
   viewport-relative, the outer two are the floor and ceiling.
   =================================================================== */

:root {
  --space-section: clamp(2.25rem, 4.5vw, 3.5rem);  /* between major bands   */
  --space-block:   clamp(1.25rem, 2.5vw, 2rem);  /* between blocks within */
  --space-tight:   clamp(.75rem, 1.5vw, 1rem);   /* label-to-control      */
}

/* One padding rule for every band, so no two neighbours can disagree. */
.hero,
.services,
.approach,
.contact,
.cta,
.service-detail { padding: var(--space-section) 0; }

/* The hero is asymmetric on purpose.
   More above, because nothing precedes it and the page needs a top edge.
   LESS below, because the next section contributes its own padding and the
   two add together - a symmetric hero makes the space under the buttons the
   largest gap on the page, which is where the eye is least served by it. */
.hero {
  padding-top:    calc(var(--space-section) * 1.5);
  padding-bottom: calc(var(--space-section) * 0.7);
}

/* The buttons are the last thing in the hero; the section padding is the
   space below them. A margin here would be added to that, not instead of it. */
.hero-actions { margin: var(--space-block) 0 0; }
.hero .lede   { margin: var(--space-tight) auto var(--space-block); }

/* Consecutive bands of the same colour need a divider, not double padding. */
.service-detail + .service-detail { border-top: 1px solid var(--color-border); }

.page-head { padding: calc(var(--space-section) * .9) 0 var(--space-block); }

/* --- mobile ----------------------------------------------------------
   Below 620px the header stacks, the nav becomes a wrapped row, and every
   interactive target is at least 44px on its shortest side, which is the
   minimum WCAG 2.2 Target Size (Minimum) 2.5.8 accepts and the size a
   thumb actually hits.
   -------------------------------------------------------------------- */
@media (max-width: 620px) {
  .header-inner { flex-direction: column; align-items: center; gap: .5rem; }
  .nav { display: flex; flex-wrap: wrap; justify-content: center; gap: .25rem 1.1rem; }
  .nav a { margin-left: 0; padding: .55rem .25rem; display: inline-block; min-height: 44px; line-height: 1.9; }

  .hero h1 { font-size: clamp(1.6rem, 7vw, 2.1rem); }
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { width: 100%; }
  .btn { padding: .85rem 1.25rem; min-height: 44px; }

  .cards { grid-template-columns: 1fr; gap: var(--space-block); }
  .two-col { grid-template-columns: 1fr; }

  /* Inputs at 16px or larger stop iOS zooming the page on focus. */
  input, textarea, select { font-size: 16px; min-height: 44px; }
  textarea { min-height: 8rem; }

  .footer-nav { gap: .25rem 1rem; }
  .footer-nav a { padding: .4rem 0; display: inline-block; }
}

/* Nothing may force a horizontal scrollbar. A single wide element does it
   to the whole document, and it is the most common phone layout fault. */
html, body { overflow-x: hidden; }
img, svg, table, pre { max-width: 100%; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* Standalone links need a target, not just a text run.
   WCAG 2.2 SC 2.5.8 Target Size (Minimum), Level AA, requires 24x24 CSS px.
   Its exception is for links INLINE in a sentence, which these are not - they
   sit alone as the call to action for a card or a section, so the exception
   does not apply and the bar does. 44px is used rather than the 24px minimum
   because that is what a thumb actually hits, and it costs nothing here. */
.card-link {
  display: inline-block;
  min-height: 44px;
  line-height: 44px;
}

/* Footer navigation is a row of standalone links, same reasoning. */
.footer-nav a {
  display: inline-block;
  min-height: 44px;
  line-height: 44px;
  padding: 0 .15rem;
}

/* Brand mark. The logo is decorative in the footer (alt="") because the word
   "Fawcett Systems" sits beside it — announcing it twice helps nobody using a
   screen reader. In the header it carries the alt text, because there it IS
   the link's accessible name. */
.brand img { display: block; height: 44px; width: auto; }
.footer-mark { vertical-align: middle; margin-right: .4rem; opacity: .9; }
.footer-brand { display: flex; align-items: center; justify-content: center; gap: .1rem; }
@media (max-width: 620px) { .brand img { height: 38px; } }

/* ---- accent -------------------------------------------------------------
   FS 5720.1 section 5: the accent SHALL NOT be used for large fills,
   backgrounds, or body text. It is also too light for text on white -
   #2E9E8F against #ffffff is roughly 2.9:1, below the 4.5:1 WCAG 2.2 SC 1.4.3
   requires. So it appears only as a small non-text mark: the rule under a
   heading and the focus ring, where SC 1.4.11 asks 3:1 against the adjacent
   colour and it comfortably passes.
   ------------------------------------------------------------------------- */
.hero h1::after {
  content: ""; display: block; width: 56px; height: 4px; border-radius: 2px;
  background: var(--brand-accent); margin: 1rem auto 0;
}
.page-head h1::after {
  content: ""; display: block; width: 44px; height: 4px; border-radius: 2px;
  background: var(--brand-accent); margin: .75rem 0 0;
}
:focus-visible { outline: 3px solid var(--brand-accent); outline-offset: 2px; }

/* The three rules above reach for --brand-accent DIRECTLY, and that is
   correct: the teal is a brand constant that does not move between schemes,
   and it measures 5.61:1 against the dark background - comfortably past the
   3:1 SC 1.4.11 asks of a non-text mark.

   A `.cta { background: var(--brand-primary); }` rule used to sit here, and
   it was not correct. It restated a background the .cta rule further up
   already sets, but sourced it from the raw BRAND token instead of the
   scheme-aware one - so in the dark scheme the panel kept the light slate
   while its text followed --color-on-primary to near-black. Dark text on a
   dark panel, at 1.4:1.

   The bug was not the colour, it was declaring one property twice from two
   different tokens: whichever rule happens to come last silently wins, and
   only one of them knows about the scheme. The rule is deleted rather than
   corrected - .cta already has its background, and a single declaration
   cannot disagree with itself. */

/* ---------------------------------------------------------------------------
   FIELD VALIDATION

   Colour is not the only signal. A red border alone fails WCAG 2.2 SC 1.4.1
   (Use of Colour) and is invisible to a colour-blind visitor; the message text
   below the field carries the same information, script.js sets aria-invalid so
   assistive technology reports the state, and the first invalid field receives
   focus so it can actually be found.

   --color-error is already scheme-aware: #b3261e on light, #ff6b6b on dark,
   both verified against their backgrounds with fawcett-contrast.sh.
   --------------------------------------------------------------------------- */
.field-error {
  display: block;
  margin-top: .35rem;
  font-size: .85rem;
  color: var(--color-error);
}
.field-error:empty { display: none; }

.contact-form [aria-invalid="true"] {
  border-color: var(--color-error);
  /* A second ring rather than a thicker border, so the control does not change
     size when it becomes invalid and shift everything below it. */
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-error) 25%, transparent);
}
