/* ============================================================================
   Backup Logistics - design tokens
   ----------------------------------------------------------------------------
   ONE accent, ONE radius scale, ONE type family. Every value the site uses
   lives here, so a brand change is one file and not a search across ten pages.

   WHY THIS PALETTE. The previous site was charcoal + muted gold + a Playfair
   serif. Three problems: gold on near-black is hard to read in daylight on a
   phone, which is where an owner-operator actually opens it; a serif display
   face reads "boutique" to an audience that wants "reliable"; and the whole
   category is blue-and-white, so nothing about it was memorable.

   This is a light, high-contrast base with a single signal orange. Orange is
   the freight and roadwork colour, it survives sunlight on a cracked phone
   screen, it prints legibly when a carrier prints the agreement, and no other
   dispatch company in the category is using it.
   ========================================================================= */

:root {
  /* ---- surface + ink ---------------------------------------------------- */
  --paper:        #f7f7f6;   /* page */
  --surface:      #ffffff;   /* raised: cards, forms, nav */
  --surface-sunk: #efefed;   /* wells, table stripes, code */
  --ink:          #16161a;   /* body copy, headlines */
  /* The ratios below were MEASURED in the browser against --paper, not
     estimated. Two of them used to be wrong in this comment: --ink-soft was
     claimed at 7.0:1 and --ink-faint at "4.6:1, AA" when the real figures
     were 6.87:1 and 3.96:1. 3.96 is a FAIL, and --ink-faint carries form
     hints, captions and document dates, so it was darkened to clear AA. */
  --ink-soft:     #55555f;   /* secondary copy. 6.87:1 on paper */
  /* Tuned against --surface-sunk, the DARKEST light surface, not against
     --paper. Tuning it on paper alone left it at 4.31:1 on a sunk panel,
     which is where the legal pages put their date line. Measured now:
     4.98:1 on --paper, 5.34:1 on --surface, 4.64:1 on --surface-sunk. */
  --ink-faint:    #6a6a75;   /* captions, hints, meta */
  --rule:         #dededa;   /* hairlines */
  --rule-strong:  #c4c4bf;

  /* ---- text that sits on a dark field ----------------------------------- */
  /* Photo captions and the dark bands are dark in BOTH themes, so their text
     must NOT follow --ink: it is white either way. These are declared once
     here and deliberately absent from the dark-mode blocks below, which is
     what keeps them from flipping. Before they existed, five raw #fff and
     rgba(255,255,255,...) values sat in the home page style block. */
  /* The footer is a dark slab in BOTH themes, and everything painted on it
     comes from the fixed --on-dark scale below. Its background must therefore
     be FIXED too.
     It used to be var(--ink). In light mode that is #16161a and correct; in
     DARK mode --ink flips to #f2f2f0 and the footer turned near-white with
     white text on it - 1.12:1, every link, heading and label invisible. A
     surface and the text on it must come from the same system: either both
     follow the theme, or neither does. */
  --footer-bg: #16161a;

  --on-dark:       #ffffff;
  --on-dark-soft:  rgba(255, 255, 255, .84);
  --on-dark-muted: rgba(255, 255, 255, .72);
  --on-dark-faint: rgba(255, 255, 255, .68);
  --on-dark-rule:        rgba(255, 255, 255, .12);
  --on-dark-rule-strong: rgba(255, 255, 255, .32);
  --on-dark-wash:        rgba(255, 255, 255, .08);
  --on-dark-sheen:       rgba(255, 255, 255, .22);

  /* ---- photography ------------------------------------------------------
     Every photograph on this site is the company's own, shot on a phone, on
     different days, in different light. That is the whole competitive
     advantage and also the whole problem: eight frames with eight white
     balances read as a snapshot dump. This block is the treatment that makes
     them one body of work. It is applied in site.css, not here.

     THE GRADE. One filter on every fleet photograph: a little contrast, a
     little saturation, and a 12% sepia that pulls every frame onto a common
     warm axis. Sepia is doing the real work - it is the cheapest way to drag
     a cool overcast frame and a sodium-lit night frame toward each other
     without touching the files. Kept at 12% so the trucks stay the colour
     they actually are. This is a treatment, not an effect: the photographs
     have to stay legible as proof.

     Values are MEASURED, not chosen by eye. tools/photo-probe.html draws each
     real file into a canvas with this exact filter string, reads the pixels
     back and reports luminance percentiles. Re-run it if any of this changes. */
  --photo-grade: contrast(1.06) saturate(1.06) sepia(.12);
  /* Per-photo exposure trim, multiplied on top of the grade. Default 1, and
     the table of exceptions lives in site.css beside the selectors. */
  --photo-exposure: 1;
  --photo-radius: var(--r-lg);
  --photo-frame:  color-mix(in srgb, var(--ink) 12%, transparent);
  /* A whisper of flat dim under any photograph that carries text. Its job is
     exposure unification across the set, not contrast; the scrim below does
     the contrast work. */
  --photo-veil: .12;

  /* ---- scrims, measured -------------------------------------------------
     A scrim has to clear AA against the BRIGHTEST pixel the text sits over,
     not the average, because the average is not where the reader's eye lands.
     Guessing produces exactly the failure this replaces: the old --scrim faded
     to 35% alpha at 45% height, and the home page bento tile puts an h3 right
     there. Measured over shot 09 that was 4.35:1, a fail on the one photo tile
     the eye goes to first.

     Two decisions carry the whole system.

     FIRST, the scrim is FLAT across the bottom 60% and only fades above that.
     A caption is bottom-anchored, so its text is always inside the flat
     region, and the guarantee stops depending on how tall a tile happened to
     grow. A smooth gradient cannot make that promise, which is why the old one
     could not keep it.

     SECOND, there is ONE number, --scrim-total, and it is the total darkening
     under the text however the layers get there. A bare caption uses it
     directly. .shot-cap and .photo-band also lay down a flat veil across the
     whole photograph for exposure unification, so their scrim alpha is derived
     BELOW to compensate. Change --photo-veil and the scrim follows it; the
     measured floor survives either way, because it is the thing being held
     constant rather than a number someone typed twice.

     .64 IS MEASURED, not chosen. tools/photo-probe.html draws all eight real
     photographs under their real grade and trim, composites this alpha, and
     reads the worst pixel in the bottom 40% of each. Against --on-dark-soft,
     84% white, the weakest white tier anywhere in this system:

       total .62  ->  4.61:1     total .64  ->  4.93:1     total .66  ->  5.22:1

     .64 in both themes. The floor is set against 84% and not against pure
     white on purpose: a page can legitimately reach for any tier on the
     on-dark scale, and a floor that only holds for the colour I happened to
     test is not a floor. */
  --scrim-rgb: 10, 10, 12;
  --scrim-total: .64;
  --scrim-flat:  60%;  /* text lives below this line; above it the scrim fades */
  /* The scrim alpha for a component that ALSO lays down --photo-veil. Solved
     from the compositing itself: veil + scrim must equal --scrim-total. */
  --scrim-a: calc((var(--scrim-total) - var(--photo-veil)) / (1 - var(--photo-veil)));
  /* How far a content-hugging scrim fades out over its own top edge. A caption
     box sets padding-top to at least this, so no line of text can land in the
     fade. That turns the contrast floor from a hope about layout into a
     property of the box rather than a hope about where the copy landed.

     Fluid, because it is a share of the frame and not a fixed distance: 80px
     of fade above a 240px caption on a phone is most of the tile, while the
     same 80px on a desktop band is a soft edge. Measured: 48px at 375, 80px
     at 1440. */
  --scrim-fade: clamp(3rem, 7vw, 5rem);

  /* Text ON a scrimmed photograph. White in both themes, because the scrim is
     dark in both. The soft tier is 90% where --on-dark-soft is 84%: the floor
     above already clears AA at 84%, so this is not rescuing anything, it is
     margin. A photograph is a hostile background - it has specular highlights,
     it changes when a page agent swaps the file - and six points of alpha is a
     cheap way to stop being one bad frame away from a failure. Measured at the
     floor: 5.35:1 worst case. */
  --on-photo:      #ffffff;
  --on-photo-soft: rgba(255, 255, 255, .90);

  /* The general-purpose caption scrim, for a layer painted over a photograph
     with no veil beneath it. Carries the full total on its own. */
  --scrim: linear-gradient(to top,
             rgba(var(--scrim-rgb), var(--scrim-total)) 0%,
             rgba(var(--scrim-rgb), var(--scrim-total)) var(--scrim-flat),
             rgba(var(--scrim-rgb), 0) 100%);
  /* Decoration only. NO TEXT ON THIS ONE - it carries no contrast guarantee,
     it exists to seat a photograph against the band below it. */
  --scrim-soft: linear-gradient(to top,
             rgba(var(--scrim-rgb), .42) 0%,
             rgba(var(--scrim-rgb), 0) 62%);

  /* ---- the one accent --------------------------------------------------- */
  /* MEASURED, not estimated: #d24413 carries #ffffff at 4.59:1. That clears
     WCAG AA for normal text (4.5:1) with very little margin, so do not darken
     the text or lighten this value without re-measuring. An earlier comment
     here claimed 5.0:1, which was wrong. Locked: this is the only accent on
     the site. No second colour appears in any section. */
  --accent:       #d24413;
  --accent-press: #a9350c;   /* :active, and hover on light surfaces */
  /* The accent as TEXT rather than as a fill. #d24413 carries white at 4.59:1
     when it is the background, but as a foreground on --paper it is only
     4.28:1, which fails AA for the in-body links and eyebrows that used it.
     Same hue, darkened until it clears: 6.12:1 on paper. Dark mode lifts it
     back to the bright accent, which measures 6.56:1 on the dark paper. */
  --accent-ink:   #a9350c;
  --accent-wash:  #fdf0eb;   /* tinted backgrounds, selected rows */
  --on-accent:    #ffffff;

  /* ---- state ------------------------------------------------------------ */
  --ok:      #1c6b3f;
  --ok-wash: #e8f4ed;
  --warn:    #8a5a00;
  --warn-wash:#fdf3e0;
  --bad:     #a01c1c;
  --bad-wash:#fbecec;

  /* ---- type ------------------------------------------------------------- */
  /* Archivo across the board, hierarchy from weight and size rather than from
     mixing families. JetBrains Mono only for figures that want to line up:
     MC/DOT numbers, phone, percentages. */
  --font:      "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Fluid scale. clamp() means no breakpoint jumps between 360 and 1440. */
  /* Capped at 4rem, not 5rem. Measured: at 5rem the six-word hero headline
     wrapped to THREE lines in a 595px column, and the rule is two. Font size
     and column width get planned together, not separately. */
  --t-hero:  clamp(2.35rem, 1.5rem + 3.1vw, 4rem);
  --t-h1:    clamp(2rem,   1.4rem + 2.6vw, 3.25rem);
  /* Raised from 2.25rem. Section headings on this site are short - "Onboarding
     takes about ten minutes" is the longest - and at 2.25 against a 1rem body
     the step down read as timid rather than as hierarchy. The floor is
     untouched, so nothing changes on a phone. */
  --t-h2:    clamp(1.5rem, 1.15rem + 1.7vw, 2.5rem);
  /* Floor raised from 1.15rem, which is 18.4px. WCAG counts bold text as
     "large" from 18.66px, and h3 is 700 weight, so 1.2rem/19.2px moves every
     h3 on the site from the 4.5:1 threshold to the 3:1 one. That is a real
     accessibility gain from a typography decision, and it matters most where
     an h3 sits on a photograph. */
  --t-h3:    clamp(1.2rem, 1.08rem + 0.5vw, 1.45rem);
  --t-lead:  clamp(1.05rem,1rem + 0.35vw, 1.25rem);
  --t-body:  1rem;
  --t-small: 0.875rem;
  --t-micro: 0.78rem;
  /* One tier above the hero, for a short headline over a full-bleed
     photograph where there is no column to fight with. Anything using this
     gets a word budget, not a paragraph. */
  --t-display: clamp(2.5rem, 1.55rem + 4.2vw, 5rem);

  /* ---- tracking and leading ---------------------------------------------
     Archivo is loaded at 400/500/600/700 only, so hierarchy cannot come from
     a heavier display cut. It comes from size, tracking and leading instead,
     and those get tokens so the relationship is deliberate rather than typed
     out per component.

     Optical rule: the bigger the type, the tighter it sets. The reverse is
     also true and matters more here - the eyebrow is 12.5px in a cab in
     daylight, so it opens right up. */
  --track-display: -.038em;
  --track-tight:   -.022em;
  --track-snug:    -.012em;
  --track-wide:     .16em;
  --lh-display: 1.0;
  --lh-heading: 1.08;
  --lh-lead:    1.5;
  --lh-body:    1.6;

  /* ---- space ------------------------------------------------------------ */
  --s1: 0.25rem;  --s2: 0.5rem;   --s3: 0.75rem;  --s4: 1rem;
  --s5: 1.5rem;   --s6: 2rem;     --s7: 3rem;     --s8: 4rem;
  --s9: 6rem;     --s10: 8rem;
  --section-y: clamp(3.5rem, 2rem + 6vw, 7rem);

  /* ---- shape ------------------------------------------------------------ */
  /* One radius everywhere, including buttons. Crisp reads as equipment-grade;
     pills would read as consumer app. */
  --r:    4px;
  --r-lg: 8px;

  /* ---- depth ------------------------------------------------------------ */
  /* Shadows tinted toward the ink hue, never pure black. */
  --lift-1: 0 1px 2px rgba(22,22,26,.06), 0 1px 1px rgba(22,22,26,.04);
  --lift-2: 0 4px 12px rgba(22,22,26,.07), 0 1px 3px rgba(22,22,26,.05);
  --lift-3: 0 12px 32px rgba(22,22,26,.10), 0 2px 8px rgba(22,22,26,.05);

  /* ---- motion ----------------------------------------------------------- */
  /* MOTION_INTENSITY 4: transitions and scroll reveals, no parallax, no
     hijacking. The audience opens this on mobile data in a cab. */
  --ease: cubic-bezier(.16, 1, .3, 1);
  --fast: .16s;
  --med:  .28s;

  /* ---- layout ----------------------------------------------------------- */
  --wrap:     1200px;
  --wrap-wide:1400px;
  --measure:  62ch;
  --gutter:   clamp(1.15rem, 4vw, 2.5rem);
  --nav-h:    68px;

  --z-nav: 100;
  --z-drawer: 200;
  --z-modal: 300;
}

/* ---------------------------------------------------------------------------
   Dark mode. Same hierarchy, same accent identity, lifted for contrast on a
   dark field. Follows the system by default; the toggle in the header writes
   data-theme, which wins over the media query in both directions.
   ------------------------------------------------------------------------ */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:        #121215;
    --surface:      #1a1a1f;
    --surface-sunk: #0d0d10;
    --ink:          #f2f2f0;
    --ink-soft:     #a8a8b2;
    --ink-faint:    #83838d;
    --rule:         #2b2b33;
    --rule-strong:  #3d3d47;

    --accent:       #ff6a35;   /* lifted: measured 6.56:1 on --paper */
    --accent-press: #ff8a5e;
    --accent-ink:   #ff6a35;   /* 6.56:1 on the dark paper, so no darkening */
    --accent-wash:  #2a1710;
    --on-accent:    #14100e;   /* dark ink on bright orange reads better here */

    --ok:      #5fd191;  --ok-wash:  #10241a;
    --warn:    #e0aa4f;  --warn-wash:#251c0d;
    --bad:     #ff7b7b;  --bad-wash: #2a1213;

    /* Photography is not inverted for dark mode, it is re-graded for it. A
       daylight frame at mean luminance .34 glares against a #121215 page in a
       dark cab, so the grade loses 6% brightness and a little contrast, and
       the sepia goes up 2 points because the warm axis has to work harder
       against a cool dark field. The frame hairline flips from a dark line to
       a light one: on paper a photograph needs containing, on a dark page it
       needs separating. And because the photograph is already dimmer, the
       scrim can come DOWN four points and still measure better than the light
       theme did. Re-measured, not assumed: worst 5.48:1 across the eight. */
    --photo-grade: contrast(1.04) saturate(1.02) sepia(.14) brightness(.94);
    --photo-frame: var(--on-dark-rule);
    --photo-veil:  .16;

    --lift-1: 0 1px 2px rgba(0,0,0,.4);
    --lift-2: 0 4px 12px rgba(0,0,0,.45);
    --lift-3: 0 12px 32px rgba(0,0,0,.55);
  }
}

:root[data-theme="dark"] {
  --paper:        #121215;
  --surface:      #1a1a1f;
  --surface-sunk: #0d0d10;
  --ink:          #f2f2f0;
  --ink-soft:     #a8a8b2;
  --ink-faint:    #83838d;
  --rule:         #2b2b33;
  --rule-strong:  #3d3d47;

  --accent:       #ff6a35;
  --accent-press: #ff8a5e;
  --accent-ink:   #ff6a35;
  --accent-wash:  #2a1710;
  --on-accent:    #14100e;

  --ok:      #5fd191;  --ok-wash:  #10241a;
  --warn:    #e0aa4f;  --warn-wash:#251c0d;
  --bad:     #ff7b7b;  --bad-wash: #2a1213;

  /* See the note in the media-query block above: the photography is re-graded
     for the dark field, not inverted into it. */
  --photo-grade: contrast(1.04) saturate(1.02) sepia(.14) brightness(.94);
  --photo-frame: var(--on-dark-rule);
  --photo-veil:  .16;

  --lift-1: 0 1px 2px rgba(0,0,0,.4);
  --lift-2: 0 4px 12px rgba(0,0,0,.45);
  --lift-3: 0 12px 32px rgba(0,0,0,.55);
}
