/* tokens.css — the global custom properties. LOADED FIRST, before every other
 * sheet, so everything below can read them and anything after can override them.
 *
 * WHAT BELONGS HERE: values used by more than one section — the reds, the
 * charcoal register, the greys, the type stack, the easings.
 *
 * WHAT DOES NOT: properties scoped to one section or one state. Three sets live
 * elsewhere on purpose and must stay there —
 *
 *   hero.css / hero.small.css   the contain-scale set (--inset, --gap, --col-w
 *                               and the rest) is written in cqw/cqh against an
 *                               element with `container-type: size`. In :root
 *                               there is no query container and every one of
 *                               them resolves to nothing.
 *   chrome.small.css            :root { --banner-h: 53px } — the mobile value.
 *   main.css                    body.banner-closed { --banner-h: 0px } — state.
 *
 * --banner-h is therefore declared three times deliberately: the base is here,
 * mobile in chrome.small.css, dismissed in main.css. That only works while this
 * sheet loads first — see the $base array in functions.php.
 *
 * Do not alter existing values. The contrast notes below are measurements, not
 * preferences, and several of them record a colour that already failed once. */

:root {
  /* --- the red, SPLIT BY SURFACE ------------------------------------------
     There is one brand red with two renderings, chosen by what it sits on.
     The design system calls this out as a hard rule and records that a blanket
     find-and-replace is what broke it before: a light-surface red landed on a
     dark block at 3.74:1 and on the ink header at 4.01:1.

     NEVER swap one of these for another without checking the ground first.
       --red        on #f4f6f7 #cdd1d5 #f4f6f8 #e6e9eb   (4.41:1 on #f4f6f8)
       --red-dark   on #12181e #16161d #0e0f12 #1f2026   (4.55:1 on #12181e)

     Two reds are retired rather than merely unused, and are recorded here so
     they are not reintroduced from an old sheet or an old handoff:

       #ff5a72  was --red-ink, the dark-ground red for where --red-dark's
                4.55:1 was not enough (5.92:1 on #12181e). No consumer.
       #c00020  was --red-deep, the AA-passing red for small type on light
                cards under M19's "red on a light card is #c00020". It measured
                5.94:1 on #f4f6f8 against #ff0a32's 3.63:1, and it carried the
                Services card accents and the Contact submit button until
                RED-CONTACT-SERVICES.md moved both to the accent red. That is a
                deliberate loss of contrast, logged on --red-label below. */
  --red:          #e6002a;   /* light grounds */
  --red-dark:     #ff0a32;   /* dark grounds */

  /* RED TYPE — BRIGHTNESS CHOSEN OVER CONTRAST, DELIBERATELY.
     Zsolt's decision on 2026-08-12, made against measured alternatives: the
     labels on the blog list, the article view, the price list and the page
     template take the footer's bright red rather than a darker AA-passing one.
     RED-CONTACT-SERVICES.md then extended the same choice to the front page —
     this token now carries the Contact and Services accents too, on their own
     grounds (#e6e9eb ~3.4:1, #f4f6f8 3.63:1). It is the site's only red for
     type, and it is no longer secondary-page-only.

     THIS DOES NOT MEET WCAG AA AND THAT IS THE ACCEPTED TRADE:

       foreground   on #f4f6f8 light band   on #cdd1d5 steel
       #ff0a32      3.63  fails             2.56  fails   <- this token
       #e6002a      4.41  fails             3.11  fails
       #b70017      6.41  passes            4.52  passes
       #a80017      7.24  passes            5.11  passes

     4.5:1 is the bar; these labels are 9.5-12px so none take the large-text
     exemption, and 2.56 is under even the 3:1 non-text floor. #b70017 was the
     brightest value that still passed and was rejected as too close to the
     current one to be worth it. #a80017 was the AA-passing red this replaced;
     it had a token of its own until its last consumer moved here, and the row
     is kept because it is the value to return to if the trade is ever reversed.

     DO NOT "FIX" THIS BACK. It is not an oversight and it is not a copy of
     --red-dark by accident — it is the same value chosen for the same reason
     the footer uses it, brightness, on grounds where the footer's contrast does
     not follow. */
  --red-label:    #ff0a32;

  /* THE ACCENT RED'S CHANNELS — FOR ALPHA DERIVATIONS ONLY, never as a colour.
     Same #ff0a32 as --red-label and --red-dark, written as channels because a
     hex token cannot carry an alpha: rgba(var(--red-accent-rgb), .5).

     It is a triplet rather than one token per shade because six alphas are in
     use across four sections — .06 and .4 and .5 in contact, .5 in the footer
     divider and glow, .10 and .12 in the services washes, .7 on the eyebrow
     dot. Naming each would be nine tokens describing one colour.

     RED-CONTACT-SERVICES.md §3 is the rule this enforces: "Both derived reds
     come from rgb(255,10,50)... Never derive an alpha from 230,0,42 inside
     these two sections." Deriving from the surface red is the bug it names, and
     a literal rgba() is how that bug gets in. Use this token instead. */
  --red-accent-rgb: 255, 10, 50;

  /* --- charcoal register (M12) ---------------------------------------------
     The dark sections are no longer pure black. These are the register's five
     steps, shipped as a scale rather than as ad-hoc values. */
  --char-lo:      #131316;   /* services intro stage, footer, contact headline box */
  --char-base:    #17171b;   /* service panel stage, testimonials section */
  --char-hi:      #1b1c20;   /* footer row and channel hover fills */
  --char-hi2:     #202126;
  --char-ink:     #1f2026;   /* idle testimonial cards */

  /* Hero body text, the default link, and the H1 — M4: ink black, not pure
     black. The H1 had its own --ink token at the same #12181e; it was merged
     here because one value under two names is how a colour drifts apart. */
  --near-black:   #12181e;
  --banner-black: #0e1216;   /* the availability banner — already blue-ink, M12 leaves it */
  --grey-accent:  #7b838c;   /* the word "solid" in the headline */
  --grey-marker:  #2b333c;   /* "/ Signal" */
  --grey-body:    #3f474f;   /* subhead, proof, motto — body tier, 6.15:1 on #cdd1d5 */
  /* Dim tier. Must stay visibly darker than nothing and visibly LIGHTER than
     --grey-body: the Prices page's struck-through original price depends on
     that step. #565e67 was here and measures 4.28:1 on #cdd1d5, which fails. */
  --grey-stat:    #4d545b;
  --lang-off:     rgba(18, 28, 40, .58);
  --lang-sep:     rgba(18, 28, 40, .28);

  /* Text on dark grounds. */
  --on-dark:      #f4f2ed;
  --on-dark-2:    #9a9aa2;
  --on-dark-3:    #5c5c63;   /* 2.84:1 — inherited from the approved footer, flagged, unresolved */

  --steel-light:  #f2f3f4;
  --steel-mid:    #e3e6e8;
  --steel-base:   #cdd1d5;

  --hair-on-red:   rgba(255, 255, 255, .28);
  --hair-on-light: rgba(18, 28, 40, .14);
  /* THE HEAVIER RULE ON LIGHT, one step up from --hair-on-light. The mobile
     handoff names the pair outright — "Hairlines: rgba(18,28,40,.13) on light
     ... Stronger divider on light: rgba(18,28,40,.22)" — and this is the second
     of the two. It closes the page template's hero band and outlines the media
     figure on mobile; those are the only two users today.

     NOT the same as contact.css's .contact__rule, which is rgba(18,28,42,.22).
     Two, not zero, in the blue channel — a different value that happens to
     share an alpha. Do not fold them together. */
  --hair-strong:   rgba(18, 28, 40, .22);

  /* THE SAME PAIR ON A DARK GROUND. The mobile handoff names all four together
     — "Hairlines: rgba(18,28,40,.13) light, rgba(244,242,237,.10) dark
     (.14/.22 for stronger)" — and only the light two had tokens. The dark value
     is already written as a literal in services.css, testimonials.css,
     footer.css and chrome.css; those are left alone, but nothing new should
     restate it. */
  --hair-on-dark:   rgba(244, 242, 237, .10);
  --hair-on-dark-2: rgba(244, 242, 237, .22);

  /* --- Testimonials ink -----------------------------------------------------
     TESTIMONIALS-ONLY, and here rather than in testimonials.css only because
     the sections 08 / 09 block below already sets that precedent. Both are
     warm greys — the section's greys were neutral-to-cool before and the
     desktop handoff's colour table is explicit that they are not.

     Measured on the two grounds this section actually has, the charcoal field
     and the idle card, because both carry type in both colours:

                    on #17171b field   on #1f2026 card
       --ts-ink-2   12.91:1            11.47:1
       --ts-ink-3    5.90:1             5.24:1

     --ts-ink-3 REPLACES a hardcoded #6c6c74 that measured 3.47:1 on the field
     and carried 11px mono — the meta labels, the pager index and the caption
     row. That failed AA outright; this is the fix as much as it is the design.
     Do not darken it back toward the old value. */
  --ts-ink-2:  #ddd9d2;   /* intro paragraph, count figure, idle role, LinkedIn value */
  --ts-ink-3:  #96938d;   /* H2 second clause, meta labels, pager index, caption */

  /* --- sections 08 / 09 ---------------------------------------------------
     The Achievements/About handoff specified its accent as #DE0026. Unified
     onto --red (#E6002A) instead: the red curtain that opens section 08 is the
     Clients field receding, so two near-identical reds would otherwise meet on
     screen and read as a mistake rather than a choice. */
  --bg-ach:    #d3d6d9;   /* section 08 panel */
  --bg-about:  #c6c9cd;   /* section 09 panel — deliberately one step darker */
  --ink-head:  #1b232b;   /* h2/h3, large numbers */
  --ink-body:  #3f474f;   /* paragraphs */
  --muted:     #4f575f;   /* mono labels, metric captions */
  --muted-2:   #5f676e;   /* grid sublabels */
  --muted-3:   #6f777f;   /* secondary heading half */
  --dim-num:   #454d55;   /* the large 16+ figure */
  /* Named for 08/09, but it is the site's ordinary 1px divider on a light
     ground and the secondary pages' mobile sheets take it too — it is the
     handoff's "Hairlines: rgba(18,28,40,.13) on light". Kept under this name
     rather than duplicated under a better one: the value is what matters and a
     second token holding the same rgba is how a palette drifts. */
  --hair-2:    rgba(18, 28, 40, .13);   /* 1px dividers, 08/09 and mobile */
  --guide:     rgba(18, 28, 40, .035);  /* vertical column guides */
  --ghost:     rgba(18, 28, 40, .04);   /* oversized RESULTS wordmark */

  /* The availability banner is fixed over everything. The 100vh panels in 08/09
     inset their content by this much so the banner cannot cover the signature
     footer; main.js zeroes it when the banner is dismissed. */
  --banner-h: 52px;

  /* --- the fixed top bars, at one size ------------------------------------
     The hero's own top bar and the secondary pages' .pgbar are the same
     furniture in the same place on screen, so they have to read as one bar.
     They did not: on a phone the wordmark was 12px in .pgbar against 9.96px in
     the hero, and the controls 10.5px against 9.96px — about 20% apart, which
     is plainly visible when you move between the front page and Prices.

     PX HERE, NOT THE HERO'S OWN UNITS, and that is a constraint rather than a
     preference. The hero writes its type as min(cqw, cqh) against a size
     container; in :root there is no query container and those resolve to
     nothing — see the note at the top of this file. So these are the values the
     hero RESOLVES to at the 390px mobile reference, which lets .pgbar match it
     with a plain length. If hero.small.css's --fs-brand / --fs-hud ever move,
     move these with them. */
  --fs-bar-brand: 10px;
  --fs-bar-hud:   10px;

  /* --- type ------------------------------------------------------------- */
  --display: Archivo, system-ui, sans-serif;          /* H1, H2, brands, stats, watermark */
  --body:    "Bai Jamjuree", system-ui, sans-serif;   /* body copy */
  --hud:     "JetBrains Mono", ui-monospace, monospace; /* labels, markers, chips, banner */

  /* --- easing ------------------------------------------------------------
     expo-out for entrances and reveals; in-out for UI state changes.
     Scroll mapping and the bar use JS easing, not these. */
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-io:  cubic-bezier(.65, 0, .35, 1);
}
