/* ⛔ THE CANONICAL HOUSE STYLESHEET. Edit it HERE; the sibling sites VENDOR it byte-for-byte.
 *
 * Design audit 2026-09-07, DA-12 — decided in §0: "daliapps is canonical; siblings vendor the shared
 * block and a CI drift test fails on any byte difference." The three sites had hand-copied the same
 * tokens and components for months and had already drifted four ways (`--shadow-lift` two values,
 * the nav collapse at 720/860/720, `.wrap-narrow` at 760/760/780, `--green` and `.visually-hidden`
 * in driverprep only). Nothing anywhere compared them, which is the same failure the brand toolkit
 * had before `brand/toolkit/` became its canonical home.
 *
 * So: this file is the origin, `mrdriversed.com` and `bleacherbrag.com` carry byte-identical copies
 * at their own `<web>/shared.css`, each pinned by sha256 in `brand/_site-css/STYLESHEET-MANIFEST.json`
 * and re-hashed on every CI run. The vendoring commands, and what each consumer must delete from its
 * own `styles.css`, are in `brand/site-css/README.md`.
 *
 * WHAT BELONGS HERE: the palette, the resets, the layout primitives, and the components all three
 * sites draw the same way — nav, hero, buttons, section heads, prose columns, panels, the facts
 * list, the footer, the legal-document page and the 404. WHAT DOES NOT: anything whose SELECTOR or
 * geometry is a product's own (daliapps' app cards and its two-card hero stage, driverprep's
 * `--green` "correct answer" semantic, sports-graphics' `svg.mark` brand element). Those live in
 * each site's `styles.css`, which is loaded AFTER this file.
 *
 * ⚠️ Every site loads this first and its own sheet second:
 *     <link rel="stylesheet" href="/shared.css">
 *     <link rel="stylesheet" href="/styles.css">
 *
 * The CSP served by CloudFront is
 *   default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'
 * so: no CDN anything. The font below is self-hosted, every image is same-origin, and there is no
 * JavaScript on any of these sites at all — daliapps.dev's privacy notice promises visitors exactly
 * that in writing, so every interaction here is CSS-only.
 */

/* Geist — SIL Open Font License 1.1, (c) 2023 Vercel in collaboration with basement.studio.
 * Full license text ships alongside the font at /fonts/OFL.txt, as the OFL requires.
 * One variable file covers 100-900, which is smaller than the two static weights it replaces. */
@font-face {
  font-family: 'Geist';
  src: url('/fonts/Geist-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------- palette
 *
 * "Petrol & Brass" — see docs/brand/house-identity-rollout.md. Aggie maroon #500000 is RETIRED
 * along with the per-product coral and gold; do not restore them.
 *
 * Two layers, and the split matters:
 *   - BRAND CONSTANTS are fixed hexes. They never change with the colour scheme.
 *   - ROLE TOKENS are what every rule below actually references, and they are the only names the
 *     dark block overrides. It overrides VALUES only and introduces no new name, so a rule cannot
 *     silently reference a token that exists in one scheme and not the other. (`tokens()` in
 *     infra/scripts/site-contrast.mjs enforces that rather than trusting this paragraph.)
 *
 * A deep brand colour cannot keep the same job on a dark canvas: petrol as a filled button on
 * #10171A measures 1.46:1, i.e. invisible. So --fill and --heading invert to the lifted values in
 * dark rather than staying petrol. Every ratio quoted here was computed with the WCAG 2.x
 * relative-luminance formula, checked against the black-on-white = 21.00 pin.
 */
:root {
  /* Brand constants. */
  --petrol: #0e3a40;      /* 11.57 on cream · white on it 12.37 */
  --petrol-lift: #5fb8c4; /* 7.88 on the dark canvas */
  --brass: #9a6410;       /* 4.67 on cream · 4.99 on white */
  --brass-lift: #e0aa4e;  /* 8.65 on the dark canvas */

  /* Roles — light. Canvas is a warm cream rather than a blue-grey white; that is most of the
   * site's character, and it is the reason the accent had to be a mid-tone brass. */
  --canvas: #f9f7f4;
  --canvas-2: #f1eeea;
  --surface: #ffffff;
  --ink: #1e1e24;         /* 15.51 on canvas */
  --muted: #6d6a67;       /* 5.03 on canvas · 4.65 on canvas-2 */
  --line: #e2ded7;

  --heading: var(--petrol);
  --accent: var(--brass);
  --fill: var(--petrol);
  --fill-hover: #155a64;  /* white on it 7.84 */
  --on-fill: #ffffff;
  /* The card badge is a PILL, and a pill nobody can see is just text. DA-02: it used to be
   * --canvas-2 on --surface — 1.15 in light and 1.00 in dark, i.e. the category tag rendered as
   * bare text on the card with no pill at all. A pale tint cannot be fixed by tinting it harder:
   * 3.0 against white caps the fill's relative luminance at 0.30, which is a mid-tone, not a wash.
   * So the badge takes the same inversion --fill does, and its label inverts with it. */
  --badge-fill: var(--petrol); /* 12.36 on --surface */
  --badge-ink: #ffffff;        /* 12.36 on the fill */
  /* A dark drop shadow does nothing on a dark canvas, so this is a role token too, not a literal. */
  --shadow: 0 12px 32px -18px rgba(30, 30, 36, 0.45);
  /* DA-12 drift 1, reconciled. driverprep had `0 24px 60px -40px` / .55, sports-graphics
   * `0 18px 44px -28px` / .5, daliapps nothing at all. driverprep's wins: --shadow is 12/32/-18 and
   * --shadow-lift should read as the SAME shadow one step up, which 24/60/-40 nearly doubles and
   * 18/44/-28 does not. ⚠️ daliapps.dev references it nowhere today — an unused role in a shared
   * block is the cost of the block being shared, and is cheaper than a fourth value appearing the
   * first time this site wants a raised card. */
  --shadow-lift: 0 24px 60px -40px rgba(30, 30, 36, 0.55);
  --mark-keyline: transparent;

  --r-card: 14px;
  --r-lg: 20px;
  --r-pill: 999px;

  --max: 1120px;
  --gutter: clamp(20px, 5vw, 40px);
  --sec: clamp(64px, 9vw, 128px);

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --canvas: #10171a;
    --canvas-2: #161f23;
    --surface: #162024;   /* 1.09 lift off the canvas — enough to read as a raised card */
    --ink: #eaf0f1;       /* 15.72 on canvas · 14.40 on surface */
    --muted: #9aa8ab;     /* 7.39 on canvas · 6.76 on surface */
    --line: #2a3b41;

    /* The inversion. */
    --heading: var(--petrol-lift);
    --accent: var(--brass-lift);
    --fill: var(--petrol-lift);
    --fill-hover: #8ed2dc; /* canvas-coloured label on it 10.70 */
    --on-fill: #10171a;    /* 7.88 on the fill */
    --badge-fill: var(--petrol-lift); /* 7.21 on --surface */
    --badge-ink: #10171a;             /* 7.88 on the fill */
    --shadow: 0 12px 32px -18px rgba(0, 0, 0, 0.85);
    --shadow-lift: 0 24px 60px -40px rgba(0, 0, 0, 0.9);
    /* SG-13 — lifted from #4a6b73 on 2026-09-07. This outline exists for ONE reason: a mark carries
     * its own petrol field, which is 1.34 against the dark card surface, so without a keyline the
     * tile vanishes and the white glyph appears to float. An indicator that exists to be seen and
     * measures under 3.0 against the thing it is meant to separate from is not doing its job, and
     * #4a6b73 missed on two of the three dark grounds this site actually draws marks on:
     *
     *              on --surface   on --canvas-2   on --canvas
     *   #4a6b73        2.87           2.90           3.14      (2 of 3 under the 1.4.11 floor)
     *   #52747c        3.27           3.30           3.57      (3 of 3 clear)
     *
     * Computed by infra/scripts/site-contrast.mjs, pinned to black-on-white 21.00 and
     * #767676-on-white 4.54, and asserted pair-by-pair in infra/test/site-contrast.test.mjs — the
     * keyline was previously in NO suite in any of the three repos, which is why it drifted under
     * the floor unnoticed. Against the petrol field itself it goes 2.14 -> 2.44; that side is
     * decorative (the field is not a component boundary, the SURFACE side is). */
    --mark-keyline: #52747c;
  }
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  background: var(--canvas);
  color: var(--ink);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
}

a { color: inherit; }

/* DA-07 — none of the three sites had ANY focus styling, on any element, and whether the browser's
 * own ring reads against the petrol `.btn-primary` fill was never checked. One rule, everywhere.
 * `outline-offset` is what makes it work on a filled pill: the ring is drawn OUTSIDE the button, on
 * the page ground, where the accent measures 4.67 on --canvas / 4.99 on --surface in light and
 * 8.64 / 7.92 in dark — all well past the 3.0 floor a focus indicator has to clear. Drawn ON the
 * petrol fill it would be 2.53 and would fail. `:focus-visible`, not `:focus`, so a mouse click on
 * a button does not leave a ring behind it. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* padding-INLINE, not the `padding` shorthand: .wrap sits on the same elements as <section>
 * (`<section id="apps" class="wrap">`), so a class shorthand beats the `section` rule below on
 * specificity and silently zeroes the vertical rhythm between every section. */
.wrap { max-width: var(--max); margin: 0 auto; padding-inline: var(--gutter); }
/* DA-12 drift 3, reconciled: 760px. daliapps and driverprep already agreed; sports-graphics' 780 is
 * the odd one out and is the measure its legal pages happen to have been typeset at. */
.wrap-narrow { max-width: 760px; }

/* Anchored sections must clear the sticky nav when jumped to. */
[id] { scroll-margin-top: 96px; }

/* Present to assistive tech, absent on screen. DA-12 drift 4a, reconciled: it existed only in
 * driverprep (for the empty corner cell of the privacy table, which needs a name for screen readers
 * but must stay blank visually). It is a generic accessibility utility with no product semantics,
 * so it is shared — it costs nothing on a site that does not use it, and the alternative is each
 * site inventing its own clip geometry the first time it needs one. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------------------------------------------------------------- nav */

/* The gutter lives on the outer element: .nav sets its own padding for the pill's interior, which
 * would otherwise override the horizontal padding .wrap is trying to supply. */
.nav-outer { position: sticky; top: 0; z-index: 20; padding: 12px var(--gutter); }
.nav {
  max-width: var(--max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 10px 8px 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  /* Opaque, not translucent: with no JS we cannot add a scrolled state, and a blurred bar over
   * cream reads as muddy. A solid pill is cleaner and costs nothing. */
  background: var(--surface);
}
.nav-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; font-weight: 600; letter-spacing: -0.02em; }
.nav-links { display: flex; gap: 4px; margin-left: auto; }
.nav-links a {
  padding: 6px 12px;
  border-radius: var(--r-pill);
  text-decoration: none;
  font-size: 15px;
  color: var(--muted);
  transition: color 0.15s ease, background-color 0.15s ease;
}
.nav-links a:hover { color: var(--ink); background: var(--canvas-2); }
.nav-cta {
  padding: 9px 18px;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--on-fill);
  text-decoration: none;
  font-size: 15px;
  font-weight: 550;
  white-space: nowrap;
  transition: background-color 0.15s ease;
}
.nav-cta:hover { background: var(--fill-hover); }

/* DA-08 — below this width the links were `display: none` WITH NO REPLACEMENT: three of the four
 * navigation targets simply vanished and only the CTA survived. These sites ship zero JavaScript
 * (the CSP has no `script-src` at all), so a toggle would have to be a `<details>` disclosure or a
 * checkbox hack, either of which needs new markup inside every page's `<nav>`. A wrapping row needs
 * none, keeps every target reachable and tappable, and is the alternative the audit's own fix line
 * offers. The links take `order: 3` so they drop to a full-width second row UNDER the brand and the
 * CTA, which is why `margin-left: auto` has to move to the CTA — it lives on `.nav-links` above and
 * would otherwise push the whole second row right.
 *
 * DA-12 drift 2, reconciled: 720px. daliapps and sports-graphics already agreed; driverprep
 * collapsed at 860 because its nav carries more links and they collided sooner — a reason that
 * dissolves once the links WRAP instead of disappearing. */
@media (max-width: 720px) {
  .nav { flex-wrap: wrap; row-gap: 8px; padding: 8px 10px 10px 14px; }
  .nav-cta { margin-left: auto; }
  .nav-links {
    order: 3;
    width: 100%;
    margin-left: 0;
    flex-wrap: wrap;
    justify-content: center;
    gap: 2px 4px;
  }
}

/* ---------------------------------------------------------------- hero */

.hero { padding-block: clamp(48px, 8vw, 104px) var(--sec); }
.hero h1 {
  color: var(--heading);
  font-size: clamp(38px, 7.2vw, 88px);
  line-height: 0.98;
  letter-spacing: -0.04em;
  font-weight: 600;
  max-width: 15ch;
  margin-top: 18px;
  text-wrap: balance;
}
.hero .lead {
  margin-top: 24px;
  max-width: 56ch;
  font-size: clamp(18px, 1.6vw, 21px);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--muted);
}
.hero .lead strong { color: var(--ink); font-weight: 550; }

.cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; }
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 24px;
  border-radius: var(--r-pill);
  text-decoration: none;
  font-size: 16px;
  font-weight: 550;
  letter-spacing: -0.01em;
  border: 1px solid transparent;
  /* DA-11: `transform 0.15s ease` was in this list and NOTHING sets a transform on a `.btn` — not
   * here, not in any state, not in any site sheet. A transition on a property that never changes is
   * dead weight that reads as intent: the next person adds a hover lift because the animation
   * "already exists", and gets a different one from the `.card` lift beside it. `.stage-card` and
   * `.card` do animate a transform and each declares its own transition for it.
   * ⚠️ CANONICAL file: a consumer that wants a transform on `.btn` declares the transition in its
   * own `styles.css`, which loads after this one. */
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.btn-primary { background: var(--fill); color: var(--on-fill); }
.btn-primary:hover { background: var(--fill-hover); }
.btn-secondary { background: var(--surface); border-color: var(--line); color: var(--ink); }
.btn-secondary:hover { border-color: var(--ink); }

/* ---------------------------------------------------------------- sections */

section { padding-bottom: var(--sec); }

h2 {
  color: var(--heading);
  font-size: clamp(28px, 3.6vw, 46px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-weight: 600;
  text-wrap: balance;
}
.section-head { margin-bottom: clamp(28px, 4vw, 48px); }
.section-head h2 { margin-top: 12px; }
.section-head p { margin-top: 14px; max-width: 54ch; color: var(--muted); font-size: clamp(17px, 1.4vw, 19px); }

/* --- prose --- */
.statement { max-width: 24ch; }
.prose-cols { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 3vw, 56px); }
.prose-cols p { color: var(--muted); }
.prose-cols p + p { margin-top: 16px; }

/* --- panels: the business-facts list --- */
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: clamp(8px, 1vw, 12px) clamp(20px, 3vw, 32px); }
dl.facts { display: grid; grid-template-columns: 15rem 1fr; }
dl.facts dt, dl.facts dd { padding: 16px 0; border-top: 1px solid var(--line); }
dl.facts dt:first-of-type, dl.facts dt:first-of-type + dd { border-top: 0; }
dl.facts dt { color: var(--muted); font-size: 15px; }
dl.facts a { color: var(--accent); text-decoration: none; }
dl.facts a:hover { text-decoration: underline; }
address { font-style: normal; }

/* ---------------------------------------------------------------- footer */

footer { border-top: 1px solid var(--line); background: var(--canvas-2); }
.footer-main { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: clamp(24px, 4vw, 48px); padding: clamp(48px, 6vw, 72px) 0 clamp(32px, 4vw, 48px); }
.footer-brand { display: flex; align-items: center; gap: 10px; font-weight: 600; letter-spacing: -0.02em; }
.footer-tagline { margin-top: 14px; color: var(--muted); font-size: 15px; max-width: 34ch; }
.footer-col h4 { font-size: 12px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.footer-col ul { list-style: none; margin-top: 14px; }
.footer-col li + li { margin-top: 8px; }
/* DA-06 — at rest these were `--ink` with no underline and no weight change, i.e. visually
 * identical to the body text beside them: nothing said they were links until the pointer was
 * already on one, and a keyboard or touch user got no signal at all. The affordance is an
 * UNDERLINE rather than the accent colour, and that is forced arithmetic, not taste: the footer
 * sits on --canvas-2, where brass measures 4.31 and misses AA. */
.footer-col a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: var(--muted);
  font-size: 15px;
  color: var(--ink);
  transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
/* --heading, NOT --accent — see the ratio above. Petrol is 10.69 on --canvas-2. */
.footer-col a:hover { color: var(--heading); text-decoration-color: var(--heading); }
.footer-bottom { border-top: 1px solid var(--line); padding: 24px 0 40px; color: var(--muted); font-size: 13px; }
/* On the document pages the footer is just the copyright line, with no columns above it to divide. */
.footer-bottom-flush { border-top: 0; }
.footer-bottom p + p { margin-top: 6px; }

/* ---------------------------------------------------------------- legal document pages */

.doc { padding-block: clamp(40px, 6vw, 72px) var(--sec); }
.back { display: inline-block; text-decoration: none; color: var(--muted); font-size: 15px; margin-bottom: 28px; transition: color 0.15s ease; }
.back:hover { color: var(--ink); }
.doc h1 { color: var(--heading); font-size: clamp(34px, 5vw, 60px); line-height: 1.02; letter-spacing: -0.035em; font-weight: 600; }
.doc .meta { margin-top: 16px; color: var(--muted); }
.doc h2 { font-size: clamp(21px, 2.2vw, 27px); margin-top: clamp(40px, 5vw, 56px); }
.doc p { margin-top: 14px; color: var(--muted); max-width: 68ch; }
.doc p strong { color: var(--ink); font-weight: 550; }
.doc a { color: var(--accent); }
.doc ul { margin-top: 14px; padding-left: 1.15em; color: var(--muted); }
.doc li + li { margin-top: 8px; }
.doc .panel { margin-top: 20px; padding: clamp(20px, 3vw, 28px); }
.doc .panel p:first-child { margin-top: 0; }

/* ---------------------------------------------------------------- 404 */

.center-page {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
}
.center-page .code { color: var(--heading); font-size: clamp(56px, 12vw, 96px); line-height: 1; letter-spacing: -0.05em; font-weight: 600; }
.center-page h1 { margin-top: 10px; color: var(--heading); font-size: clamp(20px, 2.4vw, 26px); letter-spacing: -0.02em; font-weight: 600; }
.center-page p { margin-top: 12px; color: var(--muted); max-width: 42ch; }

/* ---------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .prose-cols { grid-template-columns: 1fr; }
  .prose-cols div + div { margin-top: 16px; }
  .footer-main { grid-template-columns: 1fr 1fr; }
  .footer-brand-col { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
  body { font-size: 16px; }
  dl.facts { grid-template-columns: 1fr; }
  dl.facts dt { padding: 14px 0 0; }
  dl.facts dd { padding: 0 0 14px; border-top: 0; }
  dl.facts dt:first-of-type + dd { border-top: 0; }
  .footer-main { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}
