/*
 * member theme — portal-local (legacy generic-named). Owned by /build-theme.
 *
 * ⚠️ READ THIS BEFORE YOU GREP THIS FILE. Promoted here 2026-08-26 from a changelog line at the
 * bottom, because it has now cost three sessions across two days — including one that was
 * measuring the trap itself.
 *
 * NINETEEN of the declarations below have a SPACE BEFORE THE COLON:
 *     --primary-color : #C0222A            --btn-onHover-primary-bg-color : ''
 *     --btn-primary-bg-color :  #C0222A    --input-bg-color :#fff
 *     …and fifteen more, mostly in the brand, button and input groups. Note the last shape:
 *     space BEFORE the colon and none AFTER.
 * The obvious pattern gets this wrong:
 *     grep '--name:'   -> misses all 19, reports 192 where the answer is 211
 * What works — space-tolerant on BOTH sides, anchored to the start of the line:
 *     grep -E -- '^[[:space:]]*--[A-Za-z][A-Za-z0-9_-]*[[:space:]]*:'
 * Note [A-Za-z], not [a-z]: EIGHT tokens are camelCase — --header-onHover-color,
 * --footer-onHover-color, and the SIX --btn-onHover-* slots. A lowercase-only class silently
 * TRUNCATES them mid-name rather than skipping them — so the miss surfaces as a plausible
 * undeclared token like `--header-on` instead of as an absence. That one cost a real gate run.
 *
 * ANCHOR TO LINE START. Dropping the ^ returns MORE than 211. The exact figure is deliberately
 * NOT quoted: it counts prose mentions, so it changes every time anyone edits a comment in this
 * file — including this note, which is how the first draft's figure went stale within minutes.
 * Tokens are named
 * mid-sentence throughout the changelog below and an unanchored pattern reads those prose
 * mentions as declarations — handing back changelog sentences as VALUES. Plausible-looking
 * garbage, not an error, so nothing flags it.
 *
 * THE NUMBERS, so a count can be sanity-checked rather than trusted. All four measured against
 * this file on 2026-08-26 by counting DECLARATIONS with comments stripped — see the units warning
 * below the table, which matters more than the numbers:
 *     >211 matches if you forget the ^ anchor      <- wrong, and drifts with every comment edit
 *     211  declared in code                        <- the file's size
 *       8  of those declared as ''                 (6 --btn-onHover-*, 2 --input-width-*)
 *     203  carrying a real value
 * Three sessions quoted 199, 203 and 211 on 2026-08-26. Two were right about narrower questions;
 * say which question you are answering. Figures of 214 and 194 also circulated that day and do
 * NOT reproduce against this file — they are recorded here only so nobody re-derives them and
 * assumes the file changed. MEASURE, DO NOT INHERIT A NUMBER — and this note is the cautionary
 * case in point. Its first draft stated 214 / 20 / 194, all three taken from a peer session's
 * report rather than measured here; none reproduces. Its second draft then said six camelCase
 * tokens where there are eight, and quoted an unanchored count that its own text invalidated.
 * A warning about the shape of a grep got its own numbers wrong twice before this line existed.
 *
 * ⚠️ AND THE OBVIOUS CROSS-CHECK DOES NOT PROVE WHAT IT LOOKS LIKE IT PROVES. "192 tight + 19
 * spaced = 211 declared" closes exactly, and that feels like validation. It is not. A SYSTEMATIC
 * units error preserves the sum:
 *     192 + 19 = 211   the measured declaration counts        <- correct
 *     194 + 20 = 214   the retracted triple                   <- also closes
 *     195 + 22 = 217   `grep -c` LINE counts over the raw file <- also closes
 * (Those three line counts were accurate when written and will DRIFT with every comment edit,
 * including edits to this note — which is the second reason never to quote one as a fact. They
 * are here to show the shape of the error, not as figures to check against.)
 * All three are internally consistent; only one is in the right units. The retracted triple
 * survived its author's own sanity check for exactly this reason, and was then offered — and
 * briefly accepted — with confidence. `grep -c` counts LINES, not matches, and over a file whose
 * comments quote tokens in prose, a line count is not a declaration count.
 * So: strip comments, count MATCHES not lines (grep -o | wc -l, or parse), and treat the closure
 * as a consistency check only. Both sides of any cross-check must be counted in the same units by
 * the same pass, or it agrees with itself and is wrong twice over.
 *
 * Changelog:
 * 2026-08-27c (IN-PLACE, newest) — ADD --shadow-card-soft. One additive token; nothing renamed, no
 *   value changed. The AXTNJI favourite page draws .axt-pcard with a 0 2px 10px rgba(20,21,42,.07)
 *   rest shadow and this file had no equivalent — --shadow-card (0 1px 2px .06) is flatter and
 *   --shadow-sm (0 2px 6px .08) is both tighter and darker, so "use the nearest" would have been a
 *   visible change, not a rounding.
 *   ITS HOVER PARTNER WAS DELIBERATELY **NOT** MINTED. The design's card hover is --shadow-md
 *   (0 6px 18px rgba(20,21,42,.10)) and this file ALREADY carries that exact value, as
 *   --shadow-dropdown. Two names for one value is the trap this file warns about elsewhere, so the
 *   cards reference --shadow-dropdown and the reason is recorded at the token itself. Owner chose
 *   this over minting both.
 *   ⚠️ Do NOT "correct" a favourite card to --shadow-card-hover: that is 0 16px 40px, the design's
 *   --shadow-lg, roughly three times the lift the card is actually drawn with.
 *   ── CHANGELOG REPAIR, same edit ──────────────────────────────────────────────────────────────
 *   The welcome-reskin entry had been inserted BETWEEN the first line of the --input-text-size
 *   entry and the rest of it, splitting one entry across another and leaving both claiming
 *   "(IN-PLACE, newest)". Both entries are restored whole and in order below, and the marker now
 *   appears exactly once, here. NO WORDING WAS CHANGED in either — only their position and the
 *   duplicated marker. Three sessions wrote this file today; if you are the fourth, add your entry
 *   ABOVE this line and demote this one rather than splicing into an existing block.
 * 2026-08-27d (CORRECTION to the --radius-sm entry below, same author) — THE TOKEN STANDS; ITS
 *   STATED SCOPE WAS TOO WIDE. The entry claimed the design draws "Add To Cart AND the Conditions
 *   variant pills" at 8px. Only the pills were ever 8px. #add-btn is
 *   class="axt-btn axt-btn--accent axt-btn--lg" and .axt-btn's BASE rule is border-radius:10px —
 *   which this file already expressed as --btn-border-radius. So Add To Cart was always a
 *   --btn-border-radius control and member/shop.php shipped it on --radius-sm by mistake; a peer
 *   corrected the page before I got to it. --radius-sm keeps its two REAL consumers, .pdp-color-btn
 *   and #buy-now-btn, so nothing is orphaned and no token is retired.
 *   HOW THE ERROR HAPPENED, because it is a repeatable one: I read the design's STYLESHEET and
 *   stopped at the first matching rule, instead of reading the RENDERED result. When a base rule
 *   (.axt-btn) and a modifier both touch the same property, only the computed value is the truth.
 *   axtnji2-b4 measured the rendered #add-btn at 10px and was right; its fidelity gate agreed
 *   independently. MEASURE THE RENDER, NOT THE RULE — and note b4 had itself written up the
 *   sibling lesson that morning ("search by VALUE, not by the name you expect") after axtnji2-05
 *   lost a round hunting --border-radius-sm for a token that existed as --radius-sm. Both traps
 *   are the same shape: trusting a NAME or a RULE over the measured fact.
 * 2026-08-27 (IN-PLACE) — --input-text-size RESTORED 15px -> 16px. ONE VALUE, no token
 *   added or removed. This is a REGRESSION FIX, not a new design ruling: HEAD (db168ec) carries
 *   --input-text-size: 16px, set by the 2026-08-21 owner-approved standardisation pass below
 *   ("16 is the industry norm the owner approved"), and the uncommitted 2026-08-26 AXTNJI rebrand
 *   dropped it to 15 with NO changelog entry — the only one of that ruling's three input tokens to
 *   move. Found by the member QA audit (AUTH-13, P3, /sign-in.php): at --font-scale 1 the declared
 *   number is the painted one, so every login/form control rendered 15px, under the 16px floor iOS
 *   Safari uses to decide whether to auto-zoom on focus. The viewport meta is
 *   'width=device-width, initial-scale=1' with no maximum-scale, so iOS zoomed the page on every
 *   field focus and the member had to pinch back out mid-login. Deliberately NOT fixed with
 *   maximum-scale=1 (suppresses pinch-zoom entirely — WCAG 1.4.4 failure) and deliberately NOT
 *   fixed with a @media override: this file is :root tokens only, zero @media blocks, and
 *   /build-theme owns that rule. Ripple: every input site-wide renders 1px larger — which is
 *   simply the 2026-08-21 ruling back in force. --input-label-size (14px) and
 *   --input-placeholder-size (15px) ALSO sit below that ruling's 16 and were left alone — neither
 *   affects iOS auto-zoom, so touching them would be outside this fix. Flagged for the owner.
 * 2026-08-27 (IN-PLACE — welcome reskin) — ADD 6 additive non-canon tokens. No value was
 *   changed and nothing was renamed, so every existing consumer resolves exactly as before.
 *   Requested by /page-reskin of member/welcome.php against the AXTNJI Welcome design frozen at
 *   spec/design-src/welcome.design.html. Each one is a LAW 1 'missing' or 'conflict' row the owner
 *   resolved design-wins; the alternative in every case was a raw literal in markup, which the
 *   step-9b gate forbids. Same naming ruling as the --radius-sm entry below: named after the DESIGN
 *   SYSTEM and joined to this file's existing non-canon families, never slotted into locked canon.
 *     --radius-xl 24px        the design's own --radius-xl, the .axt-welcome band. This file's
 *                             radius ladder stopped at 16px (--border-radius-lg / --card-border-radius).
 *     --fs-49 / --fs-31       the design's --text-4xl (band h1) and --text-2xl (Quick Access h2).
 *                             --fs-48 and --fs-30 sit 1px away; the owner declined to absorb the
 *                             difference, so both rungs are real.
 *     --fs-16-5 16.5px        the design's creed line. First half-pixel rung in the ladder; named
 *                             with a hyphen because a dot is not valid in a custom-property name.
 *     --color-tint-red        color-mix 11%. CONFLICTS with --color-brand-blush (12%), which was
 *                             NOT retuned — it has existing consumers and 12% is their measured
 *                             value. A second name for a near value is normally what this file
 *                             warns against; here the 1% is the design's, and blush's is the
 *                             GoTasty-era measurement, so they are two facts, not one.
 *     --color-tint-blue       color-mix 11%. No blue tint existed at all. Pairs with -red on the
 *                             welcome page's Quick Access icon tiles.
 *   FONTS UNTOUCHED. The design's Noto Sans TC / Noto Serif TC / Figtree stack stays deferred to
 *   the single joint pass (owner, 2026-08-26; ledger dv-12 in member/css/input.css).
 * 2026-08-27 (IN-PLACE) — ADD --radius-sm: 8px. One additive non-canon token. No value was
 *   changed and nothing was renamed, so every existing consumer resolves exactly as before.
 *   Requested by /page-reskin of member/shop.php: the AXTNJI design system draws the PDP's
 *   "Conditions" variant pills at its own --radius-sm (8px), and this file had
 *   no 8px rung for that role — canon stops at --border-radius-xs (4px) / -md (12px), and
 *   --btn-border-radius is 10px. Owner chose "add the token" over "use the nearest" (10px), so the
 *   markup can reference the design's real value instead of a near-miss.
 *   NAMED AFTER THE DESIGN SYSTEM, NOT SLOTTED INTO CANON. --border-radius-sm was rejected: the
 *   --border-radius-* family is the LOCKED canonical schema (xs/md/lg/full) and /build-theme may not
 *   grow it outside Refactor mode, which is a major bump plus a lockstep edit of every portal theme.
 *   --radius-sm instead joins the non-canon --radius-* extension family this file already carries
 *   (--radius-panel / --radius-menu-item / --radius-option-row), whose newest member was added for
 *   the identical reason: step-9b forbids the raw literal, so the value must be a token.
 *   Additive, so per the version policy this is in-place — no bump, no head_v*.php edit.
 *   head_v1.0.14's ?v=filemtime() cache-bust self-invalidates on save.
 *   NOTE FOR THE PAGE SIDE: rounded-[var(--radius-sm)] is a Tailwind ARBITRARY value. It does not
 *   exist until member/css/style.css is rebuilt (npm run dev / build:prod in member/). Shipping the
 *   token without the rebuild renders the corners unstyled, not wrong-but-close.
 * 2026-08-26 (IN-PLACE) — AXTNJI REBRAND. Values-only remap + a 6-token --axt-* extension. Owner-approved
 *   (DESIGN WINS) after /build-header hard-halted at the LAW 1 token gate: the AXTNJI design is a
 *   different brand (royal ink-blue #262C78 / vermilion #C0222A / gold #F0C962 / maroon footer
 *   ground) against this file's GoTasty coral palette, with 11 token conflicts and 6 roles that had
 *   no slot at all. Source: "Homepage.html", a Claude Design bundle — the design system is a :root
 *   token block inside a gzipped asset in the bundler manifest, so every value below is READ OFF
 *   that block, not sampled off a screenshot.
 *   NO TOKEN WAS RENAMED OR REMOVED — that is the whole point. 200 names stay, so
 *   homepageHeader_v*.php, homepageFooter_v*.php and ~30 pages keep resolving without an edit;
 *   only what they resolve TO changed. Adopting the canonical 126-token schema was considered and
 *   rejected: it renames tokens those consumers reference by name, i.e. a portal-wide break.
 *   VALUES CHANGED (104). The role mapping, not a hue-for-hue swap:
 *     RED is the action colour, BLUE the ground. The design's token names say --brand-primary is
 *     blue, but every CTA it actually renders is red (.axt-btn--accent on both homepage buttons,
 *     the active nav underline, section headings), and blue is the default button + dark surface.
 *     Owner confirmed this reading before the remap ran; getting it backwards would have painted
 *     every primary button in the portal the wrong colour.
 *       coral #F06353          -> #C0222A  --primary-color, --color-brand-primary,
 *                                          --btn-primary-bg-color, --color-text-brand,
 *                                          --tertiary-color, --signin-btn-bg-color
 *       coral-dark family      -> #9C1A22  the four -dark/-hover/-deep rungs collapse onto one
 *                                          value: the design ships ONE red-deep, not three.
 *       ink #2A2D41            -> #262C78  --secondary-color, --fourth-color, --color-bg-dark
 *       ink-mid #1E2030        -> #1B205C  --color-bg-dark-mid
 *       footer #2A2D41         -> #5C1A1A  --footer-bg-color (gradient START; end is
 *                                          --footer-bg-color-deep, declared right below it)
 *       neutrals               -> the design's ink scale (900 #14152A / 700 #34374F / 600 #4C4F68
 *                                          / 500 #6B6E85 / 400 #9598AC / 300 #C3C5D2 / 200 #E0E1EA
 *                                          / 100 #EEEFF4 / 050 #F6F7FA). Warmer greys out, cool
 *                                          navy-tinted greys in — this is why the whole portal
 *                                          reads bluer even where no brand colour appears.
 *       shadows                -> re-tinted from rgba(42,45,65,*) to the design's rgba(20,21,42,*)
 *                                          and re-shaped to its four elevations. --shadow-dropdown
 *                                          drops from 0 12px 30px/.16 to 0 6px 18px/.10: the
 *                                          design's menus sit much closer to the surface.
 *       --focus-ring-color     -> rgba(38,44,120,0.35). Was coral at 0.13 alpha, which was nearly
 *                                          invisible; the design declares blue at 35%.
 *       two empty slots FILLED: --header-onHover-color '' -> #262C78 and --footer-onHover-color
 *                                          '' -> #F0C962. An empty value is invalid CSS, so those
 *                                          hovers previously did nothing at all. The other seven
 *                                          '' slots (--btn-onHover-*, --input-onfocus-border-color)
 *                                          were LEFT EMPTY deliberately — filling them would switch
 *                                          on hover states across ~30 pages that have never had
 *                                          them, which is a behaviour change, not a recolour, and
 *                                          nothing in the header needs them (its Sign In control is
 *                                          a plain icon link, not a filled button).
 *   GEOMETRY CHANGED, and it ripples past the chrome — owner accepted this explicitly:
 *       --btn-border-radius  30px -> 10px   every button in the portal stops being a pill. The
 *                                           design's .axt-btn is 10px. This is the single most
 *                                           visible change in this entry and it touches every page.
 *       --border-radius-md   10px -> 12px   \
 *       --border-radius-xs    5px ->  4px    | the design's radius ladder (xs 4 / sm 8 / md 12 /
 *       --border-radius-lg   15px -> 16px    | lg 16). Moves cards, panels and menus everywhere.
 *       --radius-menu-item    9px ->  8px    |
 *       --radius-panel       14px -> 12px   /
 *       --js-header-height   80px -> 76px   the design's --header-h.
 *       --button-border  1px #CDCDCD -> 1.5px solid #262C78  the design's secondary-button outline.
 *       inputs: 5px radius -> 12px, 1px #aaa -> 1.5px #E0E1EA, bg #F3F3F3 -> #fff. The design's
 *               fields sit on white with a visible hairline; the grey fill is gone.
 *     ALREADY MATCHED, so untouched: --card-border-radius 16px, --radius-option-row 12px,
 *     --border-radius-full 9999px, --md-text-size 15px (the design's nav/body size), and every
 *     pure-white token.
 *   NOT TOUCHED, and each for a reason — do not "finish the job" without checking these:
 *     --color-whatsapp / --color-facebook / --color-instagram-gradient — third-party brand marks.
 *       Never restyle to brand tokens; that rule predates this entry and still holds.
 *     --badge-*-bg/-text/-border (10 tokens) — SEMANTIC status pairs, not brand. The AXTNJI design
 *       declares --success/--warning/--danger (already applied above) but ships no badge pairs.
 *     --color-star-gold #F5A623 — a RATING colour. The AXTNJI gold went into --axt-gold instead,
 *       precisely so a future star-colour change cannot repaint the footer.
 *     --color-pink-* / --color-chip-* / --color-bg-otp — MrFlower-era one-offs on body pages
 *       (product-detail chips, OTP box). Body-reskin territory; they are now visibly off-brand and
 *       should be re-pointed there, not guessed at here.
 *     --hero-scrim-gradient / -strong / --hero-text-shadow — the AXTNJI design has no photo-hero
 *       scrim (its hero is a full-bleed banner image, no text over it). Left for the body pass.
 *     --toast-warning-* — semantic. Only --toast-error-border moved, #C33427 -> #9C1A22, to stay
 *       inside the brand red family it was already imitating.
 *   TWO VALUES ARE DERIVED, NOT DECLARED — flagged so the next pass can correct them cheaply:
 *     --color-brand-blush      color-mix(in srgb, #C0222A 12%, #fff). The 12% mix IS the design's
 *                              own (.axt-badge--red ground), so the RATIO is declared; what the
 *                              design never declares is a named pale-brand token.
 *     --color-brand-blush-deep color-mix(in srgb, #C0222A 24%, #fff). The 24% is MINE. The AXTNJI
 *                              design ships no second brand tint. Consumers are body-page details
 *                              (product-card image frame, divider rules), so whoever reskins those
 *                              should replace this with a real design value rather than inherit a
 *                              guess. NOT signed off as design-accurate.
 *   NEW TOKENS (11, was 7). Later the same day, owner-approved at the body session's request:
 *     --axt-container-lg (1120px, the design's inner content spine) and a new MOTION category,
 *     --dur-fast / --dur-base / --ease-out. The motion block at the bottom carries its own
 *     rationale, including the header drift that prompted it. Running total 211 tokens.
 *   NEW TOKENS (7, was 6): five in the --axt-* extension block at the bottom of :root, plus
 *     --color-text-heading (#1F2138, ink-800, form field labels) added later the same day at the
 *     auth-reskin session's request and on the owner's approval — see its own comment. Plus
 *     --footer-bg-color-deep declared up in the footer section — see its own comment for why
 *     it is not in the extension block. Count unchanged from the owner's approval; one of the
 *     six simply lives under a better, role-scoped name.
 *   ALSO: the footer session asked for --footer-link-hover-color #F0C962. NOT minted — the
 *     canon slot --footer-onHover-color already carries exactly that role and was one of the
 *     two empty '' slots this rebrand filled, with the design's gold. A second name for one
 *     value is the thing this file's own notes keep warning against.
 *   FONTS ARE NOT IN THIS FILE'S SCOPE and are NOT done: the design's Noto Sans TC / Noto Serif TC
 *     / Figtree stack needs member/css/input.css @theme + new @font-face + font files + a
 *     style.css rebuild. Owner deferred it to one joint pass after the header/footer/body sessions
 *     land. Until then the portal renders AXTNJI colour on Poppins + Nunito Sans — and note the
 *     design is Traditional-Chinese-first while Poppins has no CJK coverage at all.
 * 2026-08-21 (IN-PLACE) — SITE-WIDE STANDARDISATION PASS (owner-approved, 4 rulings:
 *   homepage FAQ form site-wide, unified product card w/ coral price, pill button family h-12,
 *   auth pages scaled to industry size). This file's part:
 *   VALUES CHANGED (3): --input-label-size / --input-text-size / --input-placeholder-size
 *     17px -> 16px. Auth + form inputs were the outlier at 17; 16 is the industry norm the
 *     owner approved. Ripple: every input site-wide renders 1px smaller — intended.
 *   NEW NON-CANON TOKENS (9), extension block at the bottom: --hero-scrim-gradient,
 *     --hero-scrim-gradient-strong, --hero-text-shadow, --shadow-modal, --shadow-panel,
 *     --toast-warning-bg, --toast-warning-border, --toast-error-border, --loader-size.
 *     Each replaces raw rgba()/#hex literals repeated across pages (hero scrims x8, modal
 *     shadows x7, auth card shadow x4, toast palette in head_v*). Colour values are byte-for-byte
 *     what the pages rendered before, EXCEPT --toast-error-border: was Metronic pink #d62a64 on
 *     an #DC3B2E toast (old-template relic), now #C33427 (darker of the brand error red).
 * 2026-08-20 (IN-PLACE, after the checkout entry) — 7 TOKENS RESTORED FROM THE HOME BODY RESKIN.
 *   Not new work: the Home pass ran earlier the same day, its edits to this file sat unmerged
 *   while two other sessions (Blog, then Checkout) rewrote it, and this entry brings back only the
 *   parts that were ADDITIVE — --color-star-gold, --color-whatsapp, --color-whatsapp-bg,
 *   --shadow-lift, --border-on-dark, --text-on-dark-muted, --surface-on-dark-subtle. See the
 *   extension block at the bottom for what each one is for.
 *   WHAT WAS DELIBERATELY NOT RESTORED, because doing so would have undone committed work with no
 *   error anywhere: the Home version of this file predates both later sessions, so replaying it
 *   wholesale would have DELETED 4 tokens now in use (--color-brand-primary-deep,
 *   --color-brand-primary-hover, --color-text-neutral, --shadow-panel-hover — all consumed by
 *   _blog-card.php / blog.php / blog-detail.php, where a missing var() renders as nothing) and
 *   reverted 4 values (--color-text-link back to #F06353, undoing an owner design-wins call;
 *   --color-brand-primary-dark, --shadow-brand, --shadow-card-hover). Committed values were kept
 *   in every case. ONE STILL OPEN for the owner: --color-brand-primary-dark is #D94A3A here and
 *   the Home pass read the same coral as #DC3B2E. Both were documented as design-sourced.
 * 2026-08-20 (IN-PLACE) — CHECKOUT DESIGN ALIGNMENT. Driven by /page-reskin LAW 1 for
 *   member/checkout.php against "GoTasty Checkout (standalone) (1).html". Owner decision on every
 *   row: DESIGN WINS. Path chosen: values-only + inject missing CANONICAL tokens (not adopt-canon,
 *   not a prefixed extension block).
 *
 *   VALUES CHANGED (2):
 *     --shadow-sm            0 2px 6px rgba(42,45,65,0.08) -> 0 1px 3px rgba(42,45,65,0.07)
 *     --color-bg-pink-subtle #FEF3F1 -> #FEF7F5   PORTAL-WIDE: 24 uses across 8 member pages.
 *       Owner approved the ripple. The five OTHER tokens holding #FEF3F1 (--sixth-color,
 *       --secondary-bg-color, --listing-row-child-primary-color,
 *       --listing-row-child-secondary-color, --color-brand-blush) are UNCHANGED — only the
 *       selected-surface role moved.
 *
 *   CANONICAL TOKENS INJECTED (3) — present in templates/theme-template.css, absent here. This
 *   file carries 75 of the 126 canon tokens; these three are now 78. The other 48 stay missing
 *   (adopting the full schema is a Refactor + major bump, deliberately not done today):
 *     --border-radius-full     9999px    canon default, exact design match
 *     --card-border-radius     16px      canon default var(--border-radius-md); design wins
 *     --secondary-border-color #EDEDF1   canon default #F1F2F4; design wins
 *
 *   NEW NON-CANON TOKENS (2) — no canonical slot covers these roles, and the usual
 *   soft-fallback var(--x,#hex) is NOT available: /page-reskin v1.12.0's step-9b gate greps page
 *   markup for raw hex and blocks on it, so an uncovered role must become a real token or the
 *   page cannot be built at all. Named for this file's own conventions, not a new prefix:
 *     --color-text-label   #595960   uppercase field labels
 *     --radius-option-row  12px      selectable option rows
 *
 *   NOT TOUCHED, on purpose:
 *     --border-radius-lg  stays 15px — ZERO consumers in any member page, so the design's 16px card
 *       radius went to the canon --card-border-radius slot instead of retuning a shared step.
 *     --btn-border-radius stays 30px — the design's pills are served by --border-radius-full.
 *     FPX #14498F and GrabPay #00B14F were requested as tokens and are NOT added: the page renders
 *       payment marks as images (/images/payment/fpx.webp), so FPX needs no colour, and GrabPay
 *       needs an ASSET (page-reskin handoff fh-1), not a token.
 * 2026-08-20 (IN-PLACE, latest+1) — 5 MORE TOKENS, same GoTasty Blog handoff, all read off its
 *   own :root and :hover rules rather than derived:
 *     --color-brand-primary-hover NEW #E14A39               <- design --brand-hover (button bg)
 *     --shadow-card               NEW 0 1px 3px   rgba(42,45,65,.06)
 *     --shadow-card-hover         NEW 0 14px 34px rgba(42,45,65,.13)
 *     --shadow-panel-hover        NEW 0 16px 40px rgba(42,45,65,.13)
 *     --shadow-brand              NEW 0 8px 24px  rgba(240,99,83,.34)
 *   and it matches none of the design's four, so _blog-card.php had been carrying a hardcoded
 *   rgba(28,34,50,0.22) hover shadow since v1.0.1. The card/panel hover pair is deliberately NOT
 *   collapsed into one token — the design uses 14/34 on a grid card and 16/40 on the wide hero.
 * 2026-08-20 (IN-PLACE, latest) — 1 TOKEN ADDED, 2 VALUES CORRECTED, from the GoTasty Blog
 *   listing handoff (spec/design-src/blog.design.html). All three are values the design DECLARES
 *   in its own :root, so none is derived:
 *     --color-text-neutral      NEW      #595960   <- design --neutral-600 (card category pill)
 *     --color-text-link         #F06353 -> #E14A39 <- design --coral-600 (owner: design-wins)
 *     --color-brand-blush-deep  #FDE0DB -> #F8C3B9 <- design --coral-200 (was derived)
 *   NOT added: #BE3A2B. It arrived minutes earlier in this same file as
 *   --color-brand-primary-deep, from the Blog ARTICLE handoff (blog-detail.php) — same design
 *   --coral-700, same pale-pill-on-blush role. A second name for one hex is how a catalog rots,
 *   so the blog LISTING's featured pill reuses that token. Worth knowing: two sessions were
 *   editing this file at once; this entry was written against md5 93ca7c60 and reuses rather
 *   than re-declares.
 * 2026-08-20 (IN-PLACE, later) — 4 RUNGS ADDED TO THE --fs-* LADDER: --fs-12, --fs-32, --fs-36,
 *   --fs-42. Not a new category — the ladder is by construction "raw px x scale", named for its
 *   base, and these are the steps the pages needed once their hardcoded sizes became tokens.
 *   --fs-19 and --fs-48 were added in the same pass and REMOVED again in the one below: the
 *   nearest-RENDERED remap left them with zero consumers, and a token nothing reads is a trap for
 *   the next person deciding what the ladder means.
 *
 * 2026-08-20 (IN-PLACE, last) — --font-scale 1.12 -> 1, AND THE LADDER IS NOW HONEST.
 *   Owner call, and it settles a problem the two previous attempts at this only moved around.
 *   Every size token is calc(base * --font-scale). With the scale at 1.12 no token could render an
 *   integer: --md-text-size read "15px" in the source and painted 16.8px. Mapping a page's
 *   hardcoded 15px to it grew the type 12%; mapping instead by nearest RENDERED size kept the
 *   pixels but made every name lie about its own value (a 14px site pointing at --fs-12). Both are
 *   symptoms of the same thing — a multiplier between the declared number and the painted one.
 *   At --font-scale: 1 the declared number IS the painted one. --md-text-size is 15px, full stop.
 *   All 26 distinct sizes the pages use now hit a rung exactly, with --fs-19 and --fs-48 restored
 *   (they were deleted an hour earlier when the nearest-rendered map left them unused).
 *   WHAT MOVES: ~373 references that were ALREADY tokens before this rebrand pass render 12%
 *   smaller than they did yesterday — a --sm-text-size site goes 14.56px -> 13px. That is the
 *   whole cost, it is portal-wide, and it is worth eyeballing on the form-dense pages (checkout,
 *   cart, sign-up). Nothing that was a hardcoded px changes at all: those now paint the exact
 *   number their author wrote.
 *   The scale mechanism is KEPT, not removed — it is still the one knob for resizing the portal.
 *   If the portal should be bigger, raise the BASE values so the numbers stay honest; do not
 *   reintroduce a multiplier.
 * 2026-08-20 (IN-PLACE) — 13 TOKENS ADDED, 6 VALUES CHANGED. Driven by the owner rule stated this
 *   day: values come from the theme, 100% no hardcoded colour/font at page level. The GoTasty
 *   chrome rebuild (homepageHeader_v1.0.18 / homepageFooter_v1.0.15) had 17 rendered literals in
 *   its markup — raw rgba() inside Tailwind arbitrary values, bg-white/NN opacity utilities, and
 *   var(--name,#hex) soft-fallbacks. A fallback hex is still a hardcoded colour sitting in a page
 *   file; it just hides behind a token name. Every one of them is now a token here and the markup
 *   references it with NO fallback. Filename kept: additive + value-only, so head_v1.0.11's <link>
 *   and its filemtime cache-bust need no edit — same reasoning as the 2026-08-19 entry below.
 *
 *   SOURCE. Values are not invented. The 2026-08-20 design handoff ("GoTasty Home (standalone)
 *   .html") ships a full :root design system, and every value below is one of its declared tokens:
 *       --color-bg-subtle        #F8F8FA          <- design --neutral-50  / --surface-page
 *       --color-text-faint       #A6A6AE          <- design --neutral-400
 *       --error-color            #DC3B2E          <- design --red-500     ("error / sold out")
 *       --warning-color          #F2A33C          <- design --amber-500
 *       --success-color          #2E9E5B          <- design --green-500
 *       --input-placeholder-color #A6A6AE         <- design --neutral-400 (its placeholder colour)
 *       --surface-on-dark(-hover)                 <- design --border-on-dark family
 *       --overlay-bg-color                        <- the design drawer's own scrim
 *       --shadow-sm / -dropdown / -drawer         <- SiteHeader's own box-shadow values
 *       --radius-panel / --radius-menu-item       <- SiteHeader's dropdown + menu-item radii
 *
 *   ADDED (13): --color-bg-subtle, --color-text-faint, --radius-panel, --radius-menu-item,
 *     --footer-border-color, --surface-on-dark, --surface-on-dark-hover, --overlay-bg-color,
 *     --overlay-strong-bg-color, --focus-ring-color, --shadow-sm, --shadow-dropdown, --shadow-drawer.
 *
 *   NO --color-text-danger, deliberately. The chrome's Sign Out needs the design's red and so does
 *   validation; the design has ONE --red-500 for both. Minting a danger token would have put the
 *   same value in two slots, and the first person to re-shade one would leave the other behind.
 *   Sign Out uses --color-text-error.
 *
 *   CHANGED (6) — VALUES ONLY, no token renamed or removed, so every existing consumer keeps
 *   working and simply picks the new colour up:
 *     --error-color            #FD397A -> #DC3B2E   (11 live consumers)
 *     --warning-color          #E0A800 -> #F2A33C   (4)
 *     --success-color          #00B900 -> #2E9E5B   (4)
 *     --color-text-error       #FD397A -> #DC3B2E   (3)
 *     --color-text-success     #00B900 -> #2E9E5B   (2)
 *     --input-placeholder-color #d6d6d6 -> #A6A6AE  (contact-us.php x3 + the header search)
 *   ⚠️ These repaint validation messages, toasts and status badges across the member portal.
 *   #FD397A was a pink and #DC3B2E is a red — the biggest visible shift of the six. The old
 *   placeholder #d6d6d6 also failed contrast against white; #A6A6AE is the design's own value.
 *   Worth eyeballing a failed form submit and a toast after deploying.
 *
 *   NOT DONE, on purpose: --badge-*-bg/text (11 tokens) still hold their 2026-07-03 values. They
 *   are a self-consistent Tailwind-palette set with their own bg/text/border trios, and the design
 *   ships no badge system to map them onto. Re-colouring them from the four semantic-500 values
 *   would be invention, not translation.
 * 2026-08-19 (later, IN-PLACE) — MRFLOWER FACES RETIRED. The type system is now exactly two
 *   families, both GoTasty: Poppins (display) and Nunito Sans (body).
 *     - DELETED 5 @font-face blocks: Montserrat 400/500/600/700 and Quinstory 100-900. The entry
 *       below says they were "DELIBERATELY KEPT" because 57 the Quinstory family literal references depended
 *       on them; that dependency is gone. Every hardcoded family reference across the portal was
 *       converted first (73 of them: 29 Quinstory -> font-display, 40 a hardcoded Poppins family literal ->
 *       font-display, 4 font-['Nunito_Sans',...] -> font-sans), verified to zero with comments
 *       stripped, and ONLY THEN were the faces removed. That order matters: dropping the faces
 *       while a reference survived would not have shown the old font, it would have fallen through
 *       to Georgia/serif — a silent, uglier regression than the one being fixed.
 *     - DELETED the files too: css/fonts/Montserrat/ (864KB, 8 files) and css/fonts/Quinstory/
 *       (48KB, 2 files). ~912KB off every deploy. Keeping the files without the faces would have
 *       cost nothing on the wire (a face is what triggers a download) but would have left the old
 *       brand one @font-face block away from returning.
 *     - THREE REFERENCES SURVIVE, in files nothing includes: head_v1.0.6.php,
 *       accountLayout_v1.0.1.php, accountLayout_v1.0.2.php. Verified dead — every mention of them
 *       is prose inside their own docblocks, no page includes any of them. Left untouched on
 *       purpose (older versions of an include are not this change's business). If one is ever
 *       resurrected its heading renders Georgia, not Quinstory, so fix the class then.
 *     - WEIGHT CHANGED with the family, owner's call: every Quinstory site was font-normal (31 of
 *       31 — correct for a script face whose natural weight is 400) and is now font-bold. Poppins
 *       at 400 reads visibly thin at the 20-52px these headings use, and the GoTasty design sets
 *       --type-h1/h2 to --fw-bold. Weight is the one thing here that is a judgement, not a fact.
 * 2026-08-19 — GoTasty rebrand, IN-PLACE (values + two new faces; NO token added, renamed or
 *   removed, so all 38 member pages that consume the non-canon --color-* vocabulary keep working
 *   and pick the new palette up for free). Filename kept on purpose: head_v1.0.11's <link> and its
 *   filemtime cache-bust need no edit, which keeps this out of chrome's scope.
 *     - 44 token VALUES swapped MrFlower sage/cream -> GoTasty coral/navy. Anchors:
 *       #68796B sage -> #F06353 coral (primary, brand, links, primary button, sign-in button);
 *       #2C2A22 / #2A2820 / #1C2232 -> #2A2D41 navy (body text, dark surfaces, footer);
 *       #E6E6E6 -> #E5E5EA borders; #767470 -> #727272 muted text; #FFF9F9 / #F9E9E2 -> #FEF3F1
 *       blush; #F6F6F4 -> #F1F1F4 and #FAFAF7 -> #EFEFF2 subtle surfaces; #474747 -> #1E2030.
 *       Derived shades not in the handoff: --color-brand-primary-dark #D94A3A and
 *       --color-brand-primary-light #F58C80 (coral darkened/lightened; the design ships only the
 *       one coral, and these two slots had to hold SOMETHING coherent).
 *     - --header-bg-color #fff3e3 -> #FFFFFF. The design header is plain white. Applied here rather
 *       than deferred to /build-header because the token is theme-owned; the markup is not.
 *     - NEW faces: Poppins (display/headings/buttons, static 400-800) and Nunito Sans (body/UI,
 *       variable 400-800). Latin + latin-ext subsets only — see the note above the blocks for the
 *       285KB of Devanagari/Cyrillic/Vietnamese deliberately left out of the tree.
 *     - Montserrat and Quinstory @font-face blocks were kept at the time of this entry; they were
 *       REMOVED later the same day — see the 2026-08-19 (later) entry at the top.
 *     - NOT TOUCHED, on purpose: --color-bg-otp, --color-pink-soft / -deep / -dark / -vivid and
 *       --color-chip-* . Those are page accents for reset-password, verify-otp, product-detail and
 *       the about/privacy/terms callouts — pages outside this reskin. They still carry MrFlower
 *       pink and WILL look wrong next to the coral once those pages are visited. Revisit each with
 *       its own page reskin.
 *     - ACCESSIBILITY, KNOWN + ACCEPTED (owner decision 2026-08-19: match the design exactly).
 *       Coral #F06353 is design-correct but measures 3.17:1 against white — it FAILS WCAG AA for
 *       normal-size text and passes only AA Large (>=18.66px, or >=14px bold). The sage it replaced
 *       measured 4.63:1 and PASSED, so this is a real regression, not a pre-existing state.
 *         Affected as TEXT: --color-text-link, --color-text-brand, --primary-color where used for
 *         type. Buttons are defensible (labels are >=14px semibold, so AA Large applies).
 *         Measured (WCAG 2.1 relative luminance), for anyone re-checking:
 *           navy #2A2D41 on white .......... 13.55:1  PASS
 *           white on navy .................. 13.55:1  PASS
 *           muted #727272 on white .......... 4.81:1  PASS
 *           coral #F06353 on white .......... 3.17:1  FAIL normal / PASS large
 *           white on coral #F06353 .......... 3.17:1  FAIL normal / PASS large
 *           coral #F06353 on navy ........... 4.27:1  FAIL normal / PASS large
 *         The AA-passing alternative was #B33A2D (5.90:1) for the two TEXT tokens only, keeping
 *         #F06353 for fills. It was rejected in favour of design fidelity. Re-open this if an
 *         accessibility audit or Lighthouse a11y score becomes a release gate.
 *     - Paradigm note: this file predates the /build-theme canonical schema (126 tokens). Only 74
 *       of its 143 names overlap; 69 are non-canon and 52 canonical slots are absent. Adopting canon
 *       is a major bump plus a 38-page rewrite, deliberately NOT done here.
 * 2026-08-17 — v1.0.2 (dead-code removal only — ZERO token/value changes vs v1.0.1; 130 tokens on
 *   both sides, same values, same selector blocks). Deleted 13 @font-face declarations that nothing
 *   applies: MainFont/Heebo (7 weights, .ttf) and HelveticaNeue (6 weights, .otf). Montserrat and
 *   Quinstory are untouched — they are the two faces this portal actually renders.
 *     - A @font-face only downloads when a rule matches an element, so these cost visitors nothing
 *       and the removal changes no byte on the wire. What they cost is 2.8MB of .ttf/.otf that
 *       shipped on every deploy, which is why they are going.
 *     - The only rules naming those families live in css/general_v1.0.0.css (six of them: .datepicker
 *       table tr th, .daterangepicker, .btnPrimaryModal, and a bare `header` element selector). That
 *       stylesheet is NOT LOADED by anything — head_v1.0.11's own changelog says "Not using bootstrap,
 *       custom_v1.0.0.css and general_v1.0.0.css", and the four CSS links a page actually emits are
 *       style.css, main_v1.0.0.css, main_v1.0.0.css (noscript) and this file.
 *     - Verified in the browser, not by reading: across index/blog/track-order/shop/contact-us/sign-up,
 *       zero elements compute to MainFont/HelveticaNeue/Konnect and zero Heebo or HelveticaNeue files
 *       are requested. The four selectors above were then INJECTED into a live page — the datepicker
 *       and modal ones matter because that markup is built by JS and a static scan cannot see it —
 *       and all four resolved to Montserrat, confirming the general_v1.0.0 rules are unreachable
 *       rather than merely unused on the pages sampled.
 *     - The .ttf/.otf files themselves are deleted in the same commit. theme_v1.0.0.css still names
 *       them, and its src URLs are now dangling — inert, because no head has referenced v1.0.0 since
 *       404_v1.0.8 moved off it and every legacy head has zero page callers.
 * 2026-08-13 — ASSET-ONLY note, no edit to any rule below: Quinstory.woff2 re-subset in place,
 *   finishing what the Montserrat entry below started. Same filename, same URL, so the @font-face
 *   block for it is untouched. 20,704 -> 12,036 bytes (-42%).
 *     - This is NOT the same operation as Montserrat, and the difference is the point. Montserrat
 *       had two levers: 969 codepoints to trim (Cyrillic + Greek) and a pile of OpenType features
 *       to drop. Quinstory has NEITHER. Its coverage was already Latin-only (218 codepoints, no
 *       Cyrillic, no Greek, no CJK), and its ONLY features are kern/liga/clig/calt — proven, not
 *       assumed: subsetting with `--layout-features=kern,liga,clig,calt` produces a file BYTE-EQUAL
 *       to subsetting with the features left alone. There was nothing extra in there to remove.
 *     - So the only lever was glyph coverage: 218 -> 104 codepoints, 219 -> 115 glyphs.
 *     - KERNING IS DELIBERATELY KEPT. Dropping GPOS as well would reach 8,588 bytes, another 3.4KB.
 *       Refused: GPOS is 12.4KB of this font's uncompressed weight because it IS the kerning, and
 *       Quinstory is a display face that only ever renders headings — the h1 here is 34-46px, which
 *       is exactly the size where bad letter-fit is visible. Wrong 3.4KB to take.
 *     - THE KEEP-LIST WAS SCANNED, NOT GUESSED, because the failure mode is silent: any codepoint
 *       that renders in Quinstory but is missing from the subset falls back to Georgia mid-heading.
 *       Every codepoint appearing anywhere in the portal's page and lang PHP files was intersected
 *       with the font's cmap; outside printable ASCII that yielded exactly six — À, ÿ, ’, “, ” and •
 *       (glob paths are spelled out here on purpose: a slash-star inside a CSS comment is inert but
 *       makes every comment-balance check and minifier misread the file)
 *       and all six are kept, along with the neighbouring quote range and NBSP as cheap insurance.
 *       Re-run that intersection before trimming further, and note it says nothing about zh-cn:
 *       Quinstory has no CJK, so Chinese headings already fell back before any of this.
 *     - Verified by rendering, not by inspection: the four elements this portal actually draws in
 *       Quinstory (the sign-in h1 and the three footer headings) were screenshotted at 2x before
 *       and after and pixel-compared.
 *   Untouched next door: Quinstory.otf (36KB) is still listed as the second src below. It is never
 *   downloaded — WOFF2 comes first and support is >98% — but it does still ship on every deploy.
 * 2026-08-13 — ASSET-ONLY note, no edit to any rule below: the four Montserrat .woff2 files these
 *   @font-face blocks point at were re-subset in place. Same filenames, same URLs, so nothing in
 *   this file (or in the 17 consumers of it) changed — but the FILES are no longer the stock
 *   Google builds, and that matters to whoever touches them next:
 *     - 61.5-62.5KB each -> 19.0-19.3KB each. 248KB -> 76KB across the four weights.
 *     - Coverage kept: Latin + Latin-Ext + Vietnamese (969 -> 645 codepoints). What was dropped is
 *       Cyrillic and Greek, which this portal cannot produce (locales are en + zh-cn, and Montserrat
 *       never carried CJK — Chinese already falls back to a system face).
 *     - The saving is NOT mostly glyph coverage. Coverage-only subsetting gets 61.5KB -> 38.8KB;
 *       the other half is OpenType layout features. Kept kern/liga/clig/calt, dropped the rest after
 *       checking that nothing in the portal asks for them (no tabular-nums, no small-caps, no
 *       stylistic sets; the only font-feature-settings in the repo is 'liga', on the icon font).
 *     - Verified before install: every codepoint appearing in this portal's own sources AND drawable
 *       by the old font is still drawable by the new one (0 missing), and hhea ascender/descender/
 *       lineGap + unitsPerEm are byte-identical, so nothing reflows.
 *     - RE-SUBSET FROM THE .ttf SIDECARS, never from a .woff2 — Montserrat-<weight>.ttf sits next to
 *       each .woff2, is already in git, and carries the identical 969-codepoint cmap (checked, not
 *       assumed). No separate backup copy is kept precisely because those .ttf ARE the backup;
 *       adding one would have put 262KB of duplicate binaries in git history and on every deploy.
 *       If you ever replace a face with a fresh build, re-run the subset or this portal quietly
 *       goes back to paying 248KB. Recipe:
 *         python3 -m fontTools.subset Montserrat-<w>.ttf \
 *           --unicodes=<latin,latin-ext,vietnamese + this portal's own character census> \
 *           --layout-features=kern,liga,clig,calt --flavor=woff2 \
 *           --output-file=Montserrat-<w>.woff2
 * 2026-08-06 — v1.0.1 (perf, font delivery only — ZERO token/value changes vs v1.0.0).
 *   Lighthouse on the production homepage scored Performance 56 with FCP 8.0s / LCP 13.6s
 *   while TBT was 100ms and CLS was 0 — i.e. not a script or layout problem, a bytes-on-the-
 *   critical-path problem. Fonts were 1.08MB of a 3.33MB page.
 *
 *   (1) font-display: swap added to the 4 Montserrat faces and to Quinstory. They previously
 *       declared NO font-display at all, which means `auto` — browsers treat that as `block`,
 *       so body text stayed INVISIBLE until the webfont arrived. Waiting on a 193KB TTF over
 *       throttled mobile is the direct cause of the 8s first paint. `swap` paints immediately
 *       in the fallback face and re-renders when the webfont lands.
 *       NOT applied to the Material Symbols icon font — that one is font-display: block by a
 *       deliberate, documented decision (main_v1.0.0.css: swap flashes the ligature text, e.g.
 *       the literal word "check_circle", before the glyph resolves).
 *
 *   (2) Montserrat 400/500/600/700 + Quinstory now load WOFF2, with the original TTF/OTF kept
 *       as a second src for any client that cannot take WOFF2 (support is >98%, so in practice
 *       nothing fetches it — browsers pick the first format they understand and never request
 *       the other). Measured on these exact files:
 *         Montserrat-Regular   193.0KB -> 60.1KB   (69% smaller)
 *         Montserrat-Medium    193.1KB -> 60.3KB   (69%)
 *         Montserrat-SemiBold  193.3KB -> 60.5KB   (69%)
 *         Montserrat-Bold      193.5KB -> 61.1KB   (68%)
 *         Quinstory             35.3KB -> 20.2KB   (43%)
 *         TOTAL                808.2KB -> 262.2KB  = 546KB off the critical path
 *       Same outlines, same rendering — WOFF2 is a compression container, not a re-draw.
 *
 *   Heebo / HelveticaNeue / LineAwesome faces are left on TTF/OTF untouched: the homepage does
 *   not request them (a @font-face only downloads when something uses it), so converting them
 *   would be churn with no measured win. Convert if a page that does use them is optimised.
 *
 *   Referenced by head_v1.0.11.php (the homepage head) only — every other head_v* still points
 *   at v1.0.0 by design, per the repo's gradual-rollout rule.
 *
 * 2026-07-08 — Update (non-canon, values-only): BRAND RE-ALIGN to the official
 *   "brand logo-MrFlower-Mar2025.docx" brand book (operator decision — reverses the
 *   2026-06-08 "handoff bundle is authoritative" call; brand book now wins). 4 brand
 *   colours restored/mapped onto brand-bearing slots — functional palette (error/
 *   warning/success, pink product accents, neutrals — not covered by the brand book)
 *   left untouched:
 *     PRIMARY sage #68796B  -> --primary-color, --tertiary-color, --tertiary-text-color,
 *       --fifth-text-color, --btn-primary-bg-color (was #4A5740), --color-brand-primary,
 *       --color-text-brand, --color-text-link. Derived shades: --color-brand-primary-dark
 *       #4E4A28->#4E5B51, --color-brand-primary-light #8C8450->#8B978C, --signin-btn-bg
 *       #4A5740->#4E5B51 + hover #3A4732->#3C4740 (sage-dark). White-on-sage passes AA (~4.65:1).
 *     SECONDARY yellow-green #F7FAE8 -> --secondary-bg-color (was #F6F6F4); also already
 *       at --sixth-color.
 *     TERTIARY grayish-yellow #B0AF9C -> --color-border-warm (was #C7C4A4, nearest slot).
 *     ACCENT strong-green #37B14C -> --color-brand-accent (was #FFC154 gold). NOTE: white
 *       text on this accent is only ~2.3:1 — do NOT place text directly on --color-brand-accent.
 *   Live prod (www.mrflower.com.my) still shows the June handoff design; portal now
 *   diverges from live until the live site is re-synced.
 * 2026-07-03 — Correction: removed a duplicate --btn-primary-bg-color/--btn-primary-text-color
 *   declaration that had been added earlier the same day under a mistaken belief the token was
 *   undefined (a grep search missed the pre-existing `--btn-primary-bg-color :` — space before
 *   the colon — at its real declaration further up this :root block, alongside every other
 *   --btn-* token). Since CSS uses the LAST declaration when a custom property repeats in the
 *   same scope, the duplicate had been silently overriding the real, intentionally-set value
 *   (#4A5740, "unified to match --signin-btn-bg-color" per that declaration's own comment) with
 *   #6B6437 for every consumer (homepageHeader avatar circle + cart badge, products.php,
 *   product-detail.php, verify-link.php, the account-page Save buttons). Reverted; single
 *   source of truth restored.
 * 2026-06-23 — Update (non-canon, values-only): --warning-color #ffe139 -> #E0A800
 *   (vivid yellow -> warm gold/amber). Reads as amber on warning UI (cart "cart updated"
 *   notice border/icon + showMessage warning modal icon) per the design. Token-only;
 *   no page touched, no hardcoded value on the banner.
 * 2026-06-08 — Extend (non-canon, prefix --signin-*): added --signin-btn-bg-color
 *   #4A5740 + --signin-btn-hover-bg-color #3A4732 (Mr Flower "Sign In.html" auth
 *   button — a darker green than the brand --btn-primary-bg-color #6B6437; sign-in
 *   page scope only). Additive; no existing token changed. For /reskin-page sign-in.php.
 * 2026-06-08 — Brand sync to the Mr Flower design handoff bundle (latest link
 *   _6CSNL6Ra... ; operator: bundle is now authoritative, overwrite prior live-synced
 *   values). Values-only, 11 tokens: --primary-color #5E5A34->#6B6437, --tertiary-color
 *   #68796B->#6B6437, --btn-primary-bg-color #68796B->#6B6437 (sage->olive/bronze),
 *   --btn-border-radius 0px->30px (square->pill), --primary-text-color #5E5A34->#2C2A22
 *   (olive->warm-dark body text), --default-secondary-bg-color #fff->#FAFAF7 (warm page bg),
 *   --secondary-bg-color #f7f7f7->#F6F6F4, --fourth-text-color #545454->#767470 (muted),
 *   --error-color #fb403a->#FD397A, --success-color #2CAA0D->#00B900, --input-bg-color
 *   #FFFFFF->#F3F3F3. Text-size scale kept (member uses 17px; bundle 14px would regress).
 *   Footer #2A2820 already synced (2026-06-08 prior entry). Portal-wide.
 * 2026-06-08 — Follow-exactly extension (operator: match the bundle's :root precisely).
 *   Text scale: xs 12->11, sm 15->13, md 17->15, lg 24->18, xl 37->24. Font-weights:
 *   medium 400->500, thick 500->600. Buttons: secondary-bg #FFF->#F6F6F4, secondary-text
 *   #fff->#2C2A22, disabled-bg #d3d3d3->#CDCDCD, width-short 100->120px, font-size 17->14px,
 *   button-border 2px #C1BFA9 -> 1px #CDCDCD. Text: tertiary-text #000->#6B6437, fifth-text
 *   #89855D->#6B6437. Borders: --default-border #ddd->#E6E6E6, --primary-border-color
 *   #DEDEDE->#E6E6E6. Listing: header-text-size 17->13px, header-child-color #000->#767470.
 *   NOT in bundle (left as-is, flagged): --input-label/text/placeholder-size (17px),
 *   --footer-text-size (15px), --header-bg/text-color, --warning-color, --secondary-color.
 * 2026-06-08 — Values-only: --footer-bg-color #68796B (sage) -> #2A2820 (dark warm
 *   charcoal). Matches the Mr Flower Homepage.html design (Claude Design handoff
 *   bundle: --color-bg-dark, used for footer + announcement bar). Footer bg now dark
 *   portal-wide; --footer-text-color stays #FFFFFF. Upstream of /build-footer
 *   homepageFooter_v1.0.3.
 * 2026-06-04 — Values-only: --btn-primary-text-color #5E5A34 -> #ffffff. The sage
 *   primary button (--btn-primary-bg-color #68796B) needs white text to match live
 *   (white-on-sage) and pass WCAG AA (olive-on-sage was ~1.4:1; white-on-sage ~4.65:1).
 *   Surfaced by wf-ai-review-runner color-contrast on member/sign-in.php. Portal-wide.
 * 2026-06-04 — Brand refresh to production www.mrflower.com.my (source: live
 *   :root tokens via Playwright). Values-only sync of 16 tokens: red (#E21E25)
 *   -> sage/olive florist palette (primary-text + btn-primary -> #5E5A34 /
 *   #68796B), square buttons (btn-border-radius 0px), sage footer (#68796B),
 *   fixed text sizes (clamp() -> px), input label/text/placeholder 15 -> 17px,
 *   input-border #DEDEDE -> #aaa. No tokens added/removed; filename kept.
 */
/* ── GoTasty faces (2026-08-19). Poppins = display/headings + buttons, Nunito Sans = body/UI.
 * Latin + latin-ext subsets ONLY. The handoff also shipped Devanagari (5 x 39KB), Cyrillic
 * (2 x ~37KB) and Vietnamese (16KB) subsets — 285KB of glyphs this portal (en + zh-cn) can
 * never render, so they are not in the tree. Poppins is static-per-weight; Nunito Sans is a
 * variable font, so ONE file covers 400-800 per subset.
 * Montserrat + Quinstory are GONE (2026-08-19): every reference across the portal was converted
 * to font-display / font-sans, so the faces had nothing left to resolve and both they and
 * their files were deleted. These two families are the whole type system now. */
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-400.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-ext-400.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-500.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-ext-500.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-600.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-ext-600.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-700.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-ext-700.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-800.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Poppins';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url(fonts/Poppins/poppins-latin-ext-800.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Nunito Sans';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url(fonts/NunitoSans/nunitosans-latin.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Nunito Sans';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url(fonts/NunitoSans/nunitosans-latin-ext.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}



:root{
    /* 2026-06-14: portal-wide font scale. Edit --font-scale to resize ALL text.
       Every --*-text-size + --fs-* below is calc(basepx * --font-scale). */
    --font-scale: 1;
    --fs-10: calc(10px * var(--font-scale));
    --fs-12: calc(12px * var(--font-scale));
    --fs-16: calc(16px * var(--font-scale));
    --fs-16-5: calc(16.5px * var(--font-scale));   /* NEW 2026-08-27. Design creed line. */
    --fs-17: calc(17px * var(--font-scale));
    --fs-19: calc(19px * var(--font-scale));
    --fs-20: calc(20px * var(--font-scale));
    --fs-22: calc(22px * var(--font-scale));
    --fs-26: calc(26px * var(--font-scale));
    --fs-28: calc(28px * var(--font-scale));
    --fs-30: calc(30px * var(--font-scale));
    --fs-31: calc(31px * var(--font-scale));   /* NEW 2026-08-27. Design --text-2xl. */
    --fs-32: calc(32px * var(--font-scale));
    --fs-34: calc(34px * var(--font-scale));
    --fs-36: calc(36px * var(--font-scale));
    --fs-38: calc(38px * var(--font-scale));
    --fs-40: calc(40px * var(--font-scale));
    --fs-42: calc(42px * var(--font-scale));
    --fs-44: calc(44px * var(--font-scale));
    --fs-46: calc(46px * var(--font-scale));
    --fs-48: calc(48px * var(--font-scale));
    --fs-49: calc(49px * var(--font-scale));   /* NEW 2026-08-27. Design --text-4xl. */
    --fs-52: calc(52px * var(--font-scale));

    /* NEW STANDARD ROOT CSS */
    /*theme-color*/
    --primary-color : #C0222A;
    --secondary-color: #262C78;
    --tertiary-color: #C0222A;
    --fourth-color: #262C78;
    --fifth-color: #EEEFF4; 
    --sixth-color: #F6F7FA;
    --error-color: #C0222A;
    --warning-color: #D4A93E;
    --success-color: #1F8A4C;
    --hyperlink-text-color: #262C78;

 /*webpage*/
    --default-border: 1px solid #E0E1EA;
    --default-bg-color: #FBFBFD;
    --default-primary-bg-color: #14152A;
    --default-secondary-bg-color: #EEEFF4;
    --default-primary-text-color: #14152A;
    --default-secondary-text-color: #fff;
    --primary-border-color: #E0E1EA;
    --secondary-border-color: #EEEFF4;   /* CANON slot, was missing from this file. Hairline
                                            dividers (summary panel sections, table cells).
                                            Canon default #F1F2F4; design wins at #EDEDF1. */
    --primary-bg-color : #fff;
    --secondary-bg-color: #F6F7FA;
    --primary-text-color:#14152A;
    --secondary-text-color : #fff;
    --tertiary-text-color : #34374F;
    --fourth-text-color : #6B6E85;
    --fifth-text-color : #6B6E85;
    --onhover-text-color: #262C78;
    --default-border-radius: 12px;
    --xl-text-size: calc(24px * var(--font-scale));
    --lg-text-size: calc(18px * var(--font-scale));
    --md-text-size: calc(15px * var(--font-scale));
    --sm-text-size: calc(13px * var(--font-scale));
    --xs-text-size: calc(11px * var(--font-scale));
    --header-height: auto;
    --border-radius-xs: 4px;
    --border-radius-md: 12px;
    --border-radius-lg: 16px;
    --border-radius-full: 9999px;   /* CANON slot, was missing from this file. Pills / badges /
                                       fully-rounded buttons. Exactly the design's pill radius, so
                                       --btn-border-radius (30px) is deliberately NOT retuned. */
    --card-border-radius: 16px;     /* CANON slot, was missing. Design's card/panel radius.
                                       Canon default is var(--border-radius-md); design wins. */
    --radius-panel: 12px;
    --radius-menu-item: 8px;
    --radius-option-row: 12px;      /* NEW. Selectable option rows (shipping-method,
                                       payment-method). No canon step exists between 10px and
                                       15px, and page-reskin's step-9b gate forbids a raw 12px
                                       literal in markup, so this has to be a real token. */
    --radius-sm: 8px;               /* NEW. The design system's OWN --radius-sm, added for the PDP
                                       reskin (member/shop.php): the design draws the "Conditions"
                                       variant pills (.pdp-color-btn) and the Buy Now button at 8px.
                                       ⚠️ NOT Add To Cart — this comment said so until 2026-08-27 and
                                       was WRONG. #add-btn is .axt-btn.axt-btn--accent.axt-btn--lg and
                                       .axt-btn's base rule is border-radius:10px, i.e.
                                       --btn-border-radius. See the changelog correction. No rung exists between
                                       --border-radius-xs (4px) and --border-radius-md (12px), and
                                       page-reskin's step-9b gate forbids a raw 8px literal in markup,
                                       so this has to be a real token. Same value as
                                       --radius-menu-item, kept separate because the roles are
                                       unrelated and either may be retuned without the other. */
    --radius-xl: 24px;              /* NEW 2026-08-27. The design system's OWN --radius-xl, added
                                       for the welcome reskin (member/welcome.php): the greeting
                                       band is drawn at 24px. This ladder stopped at 16px
                                       (--border-radius-lg / --card-border-radius) and canon may
                                       not be extended, so --radius-xl joins the non-canon
                                       --radius-* family alongside --radius-sm / -panel /
                                       -menu-item / -option-row. */
    --js-header-height: 76px;

/* font-weight */
    --default-font-weight-thin: 300;
    --default-font-weight-medium: 500;
    --default-font-weight-thick: 600;
    --default-font-weight-bold: 700;
    --default-font-weight-max: 900;

/*button*/
    --btn-primary-bg-color :  #C0222A;   /* 2026-08-19 GoTasty coral. History (MrFlower): #68796B sage from 2026-07-08, #4A5740 before that, #6B6437 olive before that. CONTRAST NOT RE-VERIFIED — the sage note claimed white text at ~4.65:1, but coral #F06353 with #ffffff is ~3.1:1, which FAILS WCAG AA for normal text and passes only for large/bold. The design itself uses white-on-coral buttons, so this matches the reference; flag it if AA on small button text is required.
       2026-08-26 CORRECTION — the value above is no longer coral. It is AXTNJI vermilion #C0222A,
       and the contrast concern this note recorded is RESOLVED by the rebrand rather than argued
       away: #C0222A with #ffffff measures ~6.5:1, which passes WCAG AA for normal text. The
       history above is kept because it explains why the note existed, not what the value is. */
    --btn-secondary-bg-color : #EEEFF4;
    --btn-tertiary-bg-color : #fff;
    --btn-disabled-bg-color : #C3C5D2;
    --btn-onHover-primary-bg-color : '';
    --btn-onHover-secondary-bg-color : '';
    --btn-onHover-tertiary-bg-color : '';
    --btn-primary-text-color: #ffffff;
    --btn-secondary-text-color: #14152A;
    --btn-tertiary-text-color: #262C78;
    --btn-disabled-text-color: #9598AC;
    --btn-onHover-primary-text-color : '';
    --btn-onHover-secondary-text-color : '';
    --btn-onHover-tertiary-text-color : '';
    --btn-width-long: 200px;
    --btn-width-short: 120px;
    --btn-border-radius: 10px;
    --button-border: 1.5px solid #262C78;
    --btn-font-size : 15px;

/*input*/
    --input-label-size: 14px;
    --input-text-size: calc(16px * var(--font-scale));
    --input-border-radius: 12px;
    --input-border : 1.5px solid #E0E1EA;
    --input-bg-color :#fff;
    --input-width-long: '';
    --input-width-short: '';
    --input-placeholder-size: 15px;
    --input-placeholder-color: #9598AC;
    --input-disabled-bg-color: #EEEFF4;
    /* 2026-08-26 — FILLED, was ''. The design focuses fields with border-color --axt-blue plus a
       3px 35%-blue ring (--focus-ring-color carries the ring). An empty value is invalid CSS, so
       until now this slot did nothing at all and any page wanting a focus border wrote its own.
       Safe to fill and inert for every existing page: grepped the whole portal, ZERO consumers
       read this token. That is exactly why it differs from the six --btn-onHover-* slots left
       empty above — filling those WOULD switch on hover states across ~30 pages that have never
       had them, which is a behaviour change, not a recolour. Requested by the auth-reskin
       session, whose sign-in / sign-up pages are almost entirely input fields. */
    --input-onfocus-border-color: #262C78;

/*header*/
    --header-bg-color: rgba(255,255,255,0.93);
    --header-text-size: calc(15px * var(--font-scale));
    --header-text-color: #34374F;
    --header-onHover-color: #262C78;

/*footer*/
    --footer-bg-color: #5C1A1A;
    /* 2026-08-26 — gradient END. The AXTNJI footer ground is not a flat fill:
       linear-gradient(180deg, --footer-bg-color 0%, --footer-bg-color-deep 100%), design 墨紅
       #5C1A1A -> #3F1010. Named in the --footer-* family rather than as a brand primitive
       (it was --axt-maroon-deep for about half an hour) because it has exactly ONE consumer,
       the footer gradient, and sitting next to its own start colour makes the pair obvious.
       Requested under this name by the footer session; adopted rather than duplicated. */
    --footer-bg-color-deep: #3F1010;
    --footer-text-size: calc(14.5px * var(--font-scale));
    --footer-text-color: #FFFFFF;
    --footer-border-color: rgba(255,255,255,0.14);
    --footer-onHover-color: #F0C962;

/*listing*/
    --listing-header-color: #EEEFF4;
    --listing-row-primary-color: #F6F7FA;
    --listing-row-secondary-color: #fff;
    --listing-header-child-color: #6B6E85;
    --listing-row-child-primary-color: #F6F7FA;
    --listing-row-child-secondary-color: #F6F7FA;
    --listing-header-text-size: calc(13px * var(--font-scale));
    --listing-row-text-size: calc(17px * var(--font-scale));
    --listing-header-child-text-size: calc(17px * var(--font-scale));
    --listing-row-child-text-size: calc(17px * var(--font-scale));

    /* ── Non-canonical extension — prefix: --signin-* (sign-in page scope) ──
       Added 2026-06-08 (see changelog). The Mr Flower "Sign In.html" design
       (Claude Design bundle) hardcodes a DARKER auth-button green than the
       brand button (--btn-primary-bg-color #6B6437). Scoped to the sign-in
       page so the portal-wide button is unaffected. Consumed by
       member/sign-in.php via bg-[color:var(--signin-btn-bg-color)]. */
    --signin-btn-bg-color: #C0222A;
    --signin-btn-hover-bg-color: #9C1A22;

    /* ── Mr Flower design-system primitives (--color-* mirror) ──────────────────
       Added 2026-06-10 via /build-theme (Extend path). Source: the MrFlower design
       system colors_and_type.css (Claude Design bundle). ADDITIVE ONLY — no existing
       token above was changed; the legacy semantic aliases already equal these values.
       Non-canonical extension under the explicit --color-* prefix (the design system's
       own namespace), NOT the locked 126-token canonical schema. Consumed by pages via
       bg-[color:var(--color-bg-warm)] etc. Lets about-us / terms / privacy / the modal
       re-point their inlined one-off hex to real tokens. */
    /* Brand */
    --color-brand-primary:       #C0222A;
    --color-brand-primary-dark:  #9C1A22;
    --color-brand-primary-light: #E2434B;
    --color-brand-primary-hover: #9C1A22;  /* 2026-08-20: the design's --brand-hover, which it
       defines as --coral-600 (#E14A39) — the same rung as --color-text-link, but a BUTTON
       BACKGROUND role, so it gets its own name rather than a text token doing double duty.
       Measured on the Blog handoff's CTA button: .scp4:hover sets background #E14A39. */
    --color-brand-primary-deep:  #9C1A22;  /* 2026-08-20: the design's own --coral-700, declared in the Blog Article handoff's :root. NOT derived. It is the text colour the design puts on a --color-brand-blush (#FEF3F1) ground - the pale-pill case, where plain coral #F06353 is too light to read and --color-brand-primary-dark (#D94A3A) is a different hue. First used by blog-detail.php's category pill, tag-pill hover and Sources link hover.
       2026-08-26 CORRECTION — value is now #9C1A22 (AXTNJI --axt-red-deep). The claim above that
       it is the design's own --coral-700 described the GoTasty era and no longer holds. The AXTNJI
       design ships ONE red-deep, so this rung, -dark and -hover all collapsed onto it; if a
       pale-pill text colour distinct from -dark is ever needed again, it has to come from the
       design, not from re-deriving one here. */
    --color-brand-accent:        #262C78;
    --color-brand-blush:         color-mix(in srgb, #C0222A 12%, #fff);  /* design token — subtle accents (subcategory border-left, etc.) */
    --color-brand-blush-deep:    color-mix(in srgb, #C0222A 24%, #fff);  /* 2026-08-20: the design's own --coral-200, declared
       in the Blog handoff's :root — so the 2026-08-19 value #FDE0DB (DERIVED from #FEF3F1,
       itself replacing MrFlower #FFDDE0) is no longer a guess and is superseded. Used on the
       product card image frame, the Promise divider rule, the featured-carousel arrow hover
       border, and now the blog CTA band's top rule. Distinct from --color-brand-blush. */
    --color-tint-red:            color-mix(in srgb, #C0222A 11%, #fff);  /* NEW 2026-08-27. The design's
       icon-tile wash on the welcome page's Orders card. 11%, NOT the 12% of --color-brand-blush above:
       blush was measured off the GoTasty-era design and has its own consumers, so the two coexist
       rather than one being retuned onto the other. */
    --color-tint-blue:           color-mix(in srgb, #262C78 11%, #fff);  /* NEW 2026-08-27. Sibling of
       --color-tint-red, same 11% wash on the brand blue. No blue tint token existed. First consumer is
       the welcome page's Account icon tile. */
    /* Backgrounds */
    --color-bg-white:            #FFFFFF;
    --color-bg-warm:             #EEEFF4;
    --color-bg-light:            #EEEFF4;
    --color-bg-subtle:           #F6F7FA;
    --color-bg-pink-subtle:      #F6F7FA;
    --color-bg-cream-subtle:     #EEEFF4;
    --color-bg-dark:             #262C78;
    --color-bg-dark-mid:         #1B205C;
    /* Borders */
    --color-border-default:      #E0E1EA;
    --color-border-warm:         #C3C5D2;
    --color-border-product:      #E0E1EA;
    /* Text */
    --color-text-primary:        #14152A;
    --color-text-secondary:      #FFFFFF;
    /* 2026-08-26 — NEW, owner-approved as a SEVENTH token beyond the rebrand's six. The design's
       ink ramp has 10 steps and the rebrand mapped 5 roles; this is ink-800 (#1F2138), the step
       the design uses for FORM FIELD LABELS (.axt-field__label). Requested by the auth-reskin
       session, whose sign-in / sign-up pages are almost entirely labelled fields, so the gap
       landed squarely on them.
       NOT folded into --color-text-primary (#14152A, ink-900): one step apart, and LAW 1 treats a
       near-miss as a conflict needing a decision rather than a rounding. Sits here, between
       primary (900) and neutral (600), because that is where the design's own ramp puts it.
       ⚠️ The ramp is still incomplete on purpose — ink-400 and ink-300 have partial homes
       (--color-text-faint, --color-border-warm) and ink-600 is --color-text-neutral. Do NOT add
       the remaining steps speculatively; add one when a page actually needs it, as happened here. */
    --color-text-heading:        #1F2138;
    --color-text-neutral:        #4C4F68;  /* 2026-08-20: the design's own --neutral-600,
       declared in the Blog handoff's :root. NOT derived. Sits between --color-text-primary
       (#2A2D41) and --color-text-muted (#727272): the design uses it for the uppercase
       category label on a --color-bg-light (#F1F1F4) pill, where #727272 loses contrast
       against the grey and #2A2D41 reads as a heading. First used by _blog-card.php. */
    --color-text-muted:          #6B6E85;
    --color-text-faint:          #9598AC;
    --color-text-label:          #4C4F68;
    --color-text-brand:          #C0222A;
    --color-text-link:           #262C78;  /* 2026-08-20: was #F06353. The design system
       declares --text-link as --coral-600 (#E14A39), not --coral-500 — measured in the Blog
       handoff's :root and on its card "Read" link. #F06353 on white is ~3.1:1, which fails AA
       for the 14px link text it is used on; #E14A39 is ~4.0:1. Two consumers, both underlined
       body links, both improved: .blog-body a (input.css) and #legalDoc .legal-body a
       (terms-and-conditions.php). Owner decision 2026-08-20 (design-wins).
       2026-08-26 SUPERSEDED — value is now #262C78 (AXTNJI blue), the design's own --text-link.
       The AA reasoning above no longer applies to this token: blue on white measures ~9.7:1,
       comfortably past AA, so the coral-500-vs-coral-600 argument it settles is moot. Links in
       this portal are BLUE now, not a red rung — that is the design's decision, not a tweak. */
    --color-text-error:          #C0222A;
    --color-text-success:        #1F8A4C;
    --color-text-disabled:       #9598AC;
    /* Typography scale (design default; member theme had sm/md but not default) */
    --default-text-size: calc(14px * var(--font-scale));

    /* Status-badge pairs — centralizes colors that were previously hardcoded hex, repeated
       across my-orders.php (order-status pills) and addresses.php ("not deliverable" badge).
       Added 2026-07-03 (design-consistency audit). Values match what was already live —
       this is a rename/centralize, not a re-color. */
    --badge-warning-bg:     #FEF3C7;
    --badge-warning-text:   #B45309;
    --badge-warning-border: #FDE68A;
    --badge-info-bg:        #E8F0FE;
    --badge-info-text:      #1A56DB;
    --badge-purple-bg:      #E0E7FF;
    --badge-purple-text:    #3730A3;
    --badge-success-bg:     #D4EDDA;
    --badge-success-text:   #155724;
    --badge-danger-bg:      #F8D7DA;
    --badge-danger-text:    #721C24;

    /* Design-token audit 2026-07-06 — centralize colors that were hardcoded (repeated)
       across member pages. Values match what was already live (rename/centralize, not re-color).
       - OTP verify box bg: reset-password.php + verify-otp.php (7 hits)
       - Pink accents: product-detail sale pills + about-us/privacy/terms callouts
       - Grey chips: product-detail size/variant selector chips */
    --color-bg-otp:         #F0EDD0;
    --color-pink-soft:      #F2C4C8;
    --color-pink-soft-deep: #ECB1B7;
    --color-pink-dark:      #6B2737;
    --color-pink-vivid:     #E74C6F;
    --color-chip-bg:        #F0F0F0;
    --color-chip-bg-active: #E0E0E0;
    --color-chip-border:    #C8C8C8;
    --color-chip-text:      #555555;

    /* Surfaces on dark, overlays, elevation and focus — added 2026-08-20 with the GoTasty
       chrome rebuild. Every one of these replaced a literal that was sitting in page markup
       (a raw rgba() in a Tailwind arbitrary value, or a bg-white/NN opacity utility), which
       is exactly what the no-hardcoded-values-at-page-level rule forbids. Values are the
       design system's own; the mapping to its token names is in the changelog at the top. */
    --surface-on-dark:        rgba(255,255,255,0.10);
    --surface-on-dark-hover:  rgba(255,255,255,0.20);
    --overlay-bg-color:       rgba(20,21,42,0.50);
    --overlay-strong-bg-color: rgba(20,21,42,0.70);
    --focus-ring-color:       rgba(38,44,120,0.35);
    --shadow-sm:              0 2px 6px rgba(20,21,42,0.08);
    /* 2026-08-20 — four elevations read straight off the GoTasty Blog handoff's own CSS, not
       were carrying a raw rgba() literal before this entry. The two hover values differ by 2px of
       blur and offset because the design genuinely uses a slightly larger lift on the wide hero
       panel (.scp2) than on a grid card (.scp3) — kept separate rather than averaged. */
    --shadow-card:            0 1px 2px rgba(20,21,42,0.06);    /* card + panel at rest */
    --shadow-card-hover:      0 16px 40px rgba(20,21,42,0.14);  /* grid card hover (.scp3) */
    --shadow-panel-hover:     0 16px 40px rgba(20,21,42,0.14);  /* wide hero panel hover (.scp2) */
    --shadow-brand:           0 12px 30px rgba(38,44,120,0.22);  /* coral button hover (.scp4) */
    --shadow-card-soft:       0 2px 10px rgba(20,21,42,0.07);  /* NEW 2026-08-27. The AXTNJI
                                     .axt-pcard REST shadow (favourite page). Distinct from
                                     --shadow-card (0 1px 2px .06, flatter) and --shadow-sm
                                     (0 2px 6px .08, tighter and darker) — neither is this value. */
    --shadow-dropdown:        0 6px 18px rgba(20,21,42,0.10);  /* ALSO the AXTNJI card HOVER shadow.
                                     The design names it --shadow-md; this file's copy happens to
                                     live under the dropdown name at the IDENTICAL value, so the
                                     favourite cards reference it rather than minting a duplicate
                                     name for one value. NOT --shadow-card-hover — see the 2026-08-27c
                                     changelog entry. */
    --shadow-drawer:          -8px 0 30px rgba(20,21,42,0.22);

    /* NON-CANONICAL EXTENSION — 2026-08-20, GoTasty Home body reskin. Roles the Home design draws
       that NO existing token covers, so page markup had nothing to point at. Named under this
       file's established --color-* convention (the same one --color-pink-vivid / --color-chip-bg
       use), NOT a new prefix. Each mirrors a value the design declares:
         --color-star-gold    <- the testimonial card's 5-star row (design #F5A623). The stars were
                                 coral (--color-brand-accent) before, which is the brand colour,
                                 not a rating colour; the design draws them gold.
         --color-whatsapp-bg  <- the contact panel's WhatsApp icon tile ground (design #E7F9EE).
         --color-whatsapp     <- WhatsApp brand green (design #25D366). A THIRD-PARTY BRAND colour,
                                 deliberately not folded into --success-color (#2E9E5B): they are
                                 near-neighbours but mean different things, and reusing the success
                                 token here would make a later success-colour change silently
                                 repaint the WhatsApp mark. */
    --color-star-gold:        #F5A623;
    --color-whatsapp-bg:      #E7F9EE;
    --color-whatsapp:         #25D366;
    /* 2026-08-21: two more third-party brand marks, same rule as --color-whatsapp — declared once
       here so no page carries the literal; never restyle them to brand tokens. */
    --color-facebook:         #1877F2;
    --color-instagram-gradient: linear-gradient(45deg, #F9CE34, #EE2A7B, #6228D7);

    /* Elevation + on-dark values the Home body needs. ADDITIVE ONLY — nothing already on a page
       changes. Each replaced a raw rgba() sitting inside a Tailwind arbitrary value in index.php,
       which LAW 1 forbids at page level.
         --shadow-lift             <- the "what we sell" card's lift on hover (translateY -4px)
         --border-on-dark          <- hairline on the dark services cards. NOT folded into
                                      --surface-on-dark (rgba(255,255,255,0.10)): that is a SURFACE
                                      fill and this is a BORDER, they differ in alpha, and the
                                      design names them separately.
         --text-on-dark-muted /    <- the two on-dark values the services band needs as TOKENS
         --surface-on-dark-subtle     rather than Tailwind's white/NN opacity utilities. Not
                                      cosmetic: `text-white/70` computes to oklab(...) in current
                                      Chromium while the design's own rgba(255,255,255,.7) stays
                                      rgba, so a design-fidelity comparison reported them as
                                      different COLOURS when they render identically.
       WHAT THIS ENTRY NO LONGER DECLARES, and why. The Home pass also added --shadow-card,
       --shadow-card-hover and --shadow-brand. All three arrived FIRST from the GoTasty Blog pass
       (see the changelog entries above) while this work sat unmerged, so the Blog values stand and
       this entry reuses them rather than re-declaring. Two of them differ between the two designs'
       readings — card-hover 0 14px 34px here vs 0 6px 18px in the Home export, brand 0 8px 24px vs
       0 6px 20px — and the committed Blog value was kept in both cases. If the Home export is the
       authority for those two, change them HERE, in one place; do not add a second name. */
    --shadow-lift:            0 16px 40px rgba(20,21,42,0.14);
    --border-on-dark:         rgba(255,255,255,0.14);
    --text-on-dark-muted:     rgba(255,255,255,0.70);
    --surface-on-dark-subtle: rgba(255,255,255,0.05);

    /* 2026-08-21 standardisation pass — shared literals promoted to tokens. See changelog.
       --hero-scrim-gradient        <- the photo-hero ink scrim every public hero repeats
                                       (products, sign-in, sign-up, reset-password). One token,
                                       one place to retint.
       --hero-scrim-gradient-strong <- the heavier variant (contact-us, welcome, careers).
       --hero-text-shadow           <- white hero headline legibility shadow (sign-in/up, welcome).
       --shadow-modal               <- the ONE modal/dialog elevation. Replaces four near-identical
                                       one-offs (28,30,44/.3 · 0,0,0/.22 · 0,0,0/.25 · 42,45,65/.12).
       --shadow-panel               <- soft resting shadow of the big auth/OTP white cards.
       --toast-warning-*            <- toast palette out of head_v* CSS; values unchanged.
       --toast-error-border         <- was #d62a64 (Metronic pink relic); now darker brand red.
       --loader-size                <- full-screen loader glyph size (head_v* spinner). */
    --hero-scrim-gradient:        linear-gradient(90deg, rgba(16,18,28,.72) 0%, rgba(16,18,28,.45) 52%, rgba(16,18,28,.55) 100%);
    --hero-scrim-gradient-strong: linear-gradient(90deg, rgba(16,18,28,.86) 0%, rgba(16,18,28,.62) 52%, rgba(16,18,28,.72) 100%);
    --hero-text-shadow:           0 2px 14px rgba(0,0,0,.40);
    --shadow-modal:               0 24px 60px rgba(20,21,42,0.30);
    --shadow-panel:               0 4px 26px rgba(20,21,42,0.07);
    --toast-warning-bg:           #B7791F;
    --toast-warning-border:       #9C6717;
    --toast-error-border:         #9C1A22;
    --loader-size:                70px;

    /* ── NON-CANONICAL EXTENSION — prefix: --axt-* (AXTNJI brand primitives) ──────────────
       Added 2026-08-26 via /build-theme (Extend path), owner-approved alongside the AXTNJI
       colour remap above. SIX roles the AXTNJI design draws that NO existing token in this
       file covers, so page markup would otherwise carry the literal (LAW 1 forbids that at
       page level). Prefix is the design system's own namespace, NOT the locked canonical
       schema. Every value is DECLARED by the design source (Homepage.html Claude Design
       bundle, :root token block) — none is derived.
         --axt-gold / -deep / -soft  <- 鵬程金. The gold has no role in the coral-era palette
                                        at all. Footer column links hover to --axt-gold
                                        (design .axt-footer__col a:hover); -deep is the gold
                                        border/hover rung, -soft the gold wash. NOT folded
                                        into --color-star-gold (#F5A623): that is a RATING
                                        colour and this is a BRAND colour, and reusing it
                                        would make a later star-colour change repaint the
                                        footer.
         (the footer gradient's END colour was briefly declared here as --axt-maroon-deep. It
          moved UP into the --footer-* family as --footer-bg-color-deep, next to its own start
          colour — one consumer, so a footer-scoped name is the honest one. Nothing referenced
          the old name; see that token's own comment.)
         --axt-blue-soft             <- lifted blue for links on a DARK ground. Distinct from
                                        --color-text-link (#262C78), which is the on-white
                                        link: plain --axt-blue on the maroon footer or the
                                        blue services band is unreadable.
         --axt-container-xl          <- the design's own max content width. Header, footer and
                                        main all centre on it (design .axt-header__nav,
                                        .axt-footer__inner, .axt-about). The portal had no
                                        layout-width token; every page hardcoded its own.
       STILL UNSLOTTED, deliberately not added here (out of the six the owner approved):
         --container-lg 1120px  — the design's narrower reading measure (.axt-about-page,
           .axt-about-hero). The body-reskin pass will need it; add it there, in ONE place,
           rather than a second name.
         the type stack (Noto Sans TC / Noto Serif TC / Figtree) — NOT a theme concern in this
           portal. Font selection is Tailwind v4 @theme config in member/css/input.css, and
           the faces are @font-face blocks above. Deferred by owner decision 2026-08-26 to a
           single joint pass after the header / footer / body sessions land, because it needs
           an input.css edit plus a style.css rebuild. Until then this portal still renders
           Poppins + Nunito Sans under AXTNJI colours. */
    --axt-gold:          #F0C962;
    --axt-gold-deep:     #D4A93E;
    --axt-gold-soft:     #FBEBBF;
    --axt-blue-soft:     #3A41A0;
    --axt-container-xl:  1280px;
    /* 2026-08-26 — sibling of the above, added at the body session's request. The design nests
       TWO content spines: a 1280px outer and a 1120px inner (its own names --container-xl /
       --container-lg). First consumer is .axt-contact__inner, the Contact section's image|form
       spine sitting inside the 1280px .axt-contact. NOT a "narrow reading measure" — that was
       this session's initial guess and the body session corrected it. */
    --axt-container-lg:  1120px;

    /* ── NON-CANONICAL EXTENSION — MOTION. A new CATEGORY, not just new tokens ────────────────
       Added 2026-08-26, owner-approved, at the body session's request. This theme had NO motion
       tokens of any name before now: every page improvised with Tailwind's duration-* / ease-*
       utilities, whose defaults are 150ms and cubic-bezier(0.4,0,0.2,1). The AXTNJI design ships
       a real motion system and its numbers are NOT Tailwind's, so every improvised transition in
       this portal is quietly off-design. Not hypothetical: the same pass that added these found
       homepageHeader_v1.0.22.php running Tailwind's 150ms where the design's .axt-icon-btn asks
       for 140ms — a drift no gate we can currently run would have caught, since a plausible
       duration is not a WRONG value, just not the design's.
       Names are the DESIGN'S OWN (--dur-*, --ease-*) rather than an --axt-* prefix: no existing
       family to conform to, no collision, and a reader diffing this file against the design
       source should not have to translate.
         --dur-fast   140ms   icon and colour hovers   (design .axt-icon-btn; .axt-btn transform)
         --dur-base   220ms   the default              (design .axt-btn background; .axt-fbband lift)
         --ease-out           the design's standard curve, used with both durations above
       --dur-slow (400ms) and --ease-inout are in the design's system but have NO consumer in any
       in-scope file, so they are deliberately NOT declared. Add one when a page needs it — the
       same discipline the ink ramp follows. */
    --dur-fast:          140ms;
    --dur-base:          220ms;
    --ease-out:          cubic-bezier(0.22, 1, 0.36, 1);

    /* ── NON-CANONICAL EXTENSION — CHECKOUT RESKIN RUNGS ─────────────────────────────────────
       Added 2026-08-28, owner-approved (all nine, explicitly, over the "snap to nearest"
       alternative), for the member/checkout.php reskin. Every value was MEASURED off the frozen
       design (spec/design-src/checkout.design.html, rendered in headless Chromium and read with
       getComputedStyle) — none is derived, guessed, or rounded.
       WHY THEY EXIST AT ALL: the checkout design asks for nine values this theme had no token
       for. The nearest existing rung was wrong in each case, and the reskin's own law forbids
       inlining a literal in page markup, so the only honest routes were "add the token" or
       "change the design". The owner chose the tokens.
       WHY --axt-fs-* AND NOT --fs-13 / --fs-22 / --fs-26: --fs-* is this file's CANON family and
       the canon catalog is LOCKED — extending it in place would look canonical to the next
       reader and to any tool that diffs this file against the schema. The declared extension
       prefix carries the honesty instead. Same reasoning the motion block records for its own
       naming, arrived at from the other direction (there, no family existed to conform to).
         --axt-ink-800        <- design ink-800. The FIELD-LABEL ink on checkout. Distinct from
                                 --tertiary-text-color (#34374F, the design's ink-700) and from
                                 --color-text-primary (#14152A, ink-900): the design uses all
                                 three on one screen, so collapsing any pair loses a real step.
         --axt-brand-tint-5   <- the design's selected-state wash: the red at FIVE percent, on the
                                 selected-address card and the selected payment row. Written the
                                 same way as its sibling --color-brand-blush (literal red, not
                                 var()) so the two read as one family. NOT reusable in place of
                                 blush: blush is 12%, which on a full-width card reads as a pink
                                 panel rather than a tint — the reason this token was needed.
         (--axt-border-dashed was declared here at 17:21 and REMOVED at 17:45, unused, by the
          session that added it. It was #C3C5D2 — which --color-border-warm has been all along
          (line 1011). A second name for a value already in the file is the exact name-vs-value
          trap this file's own header warns about. The dashed "Add New Address" border on the
          checkout reskin uses --color-border-warm. Recorded rather than silently dropped so the
          next reader does not re-add it.)
         --axt-fs-13          <- add-address button label.
         --axt-fs-14-5        <- summary rows (Merchandise Subtotal / Shipping Total). The design
                                 really is on a half-pixel here; 14px and 15px both exist in this
                                 file and neither is it.
         --axt-fs-22          <- the Summary card title, and the 60px item glyph tile.
         --axt-fs-26          <- the Total Payment figure. --xl-text-size (24px) and --fs-28 sit
                                 either side of it.
         All four rungs carry * var(--font-scale), like every other size token here, so the
         portal's text-scaling control keeps working on the reskinned page.
         --axt-radius-xl      <- the address modal shell. This file's radius scale stops at 16px
                                 (--border-radius-lg / --card-border-radius).
         --axt-shadow-lg      <- the address modal's lift. --shadow-dropdown (0 6px 18px) is a
                                 menu shadow and visibly too tight for a 500px dialog.
       NOTHING EXISTING WAS TOUCHED in this pass — additive only, by design and verified by diff. */
    --axt-ink-800:       #1F2138;
    --axt-brand-tint-5:  color-mix(in srgb, #C0222A 5%, #fff);
    --axt-fs-13:         calc(13px * var(--font-scale));
    --axt-fs-14-5:       calc(14.5px * var(--font-scale));
    --axt-fs-22:         calc(22px * var(--font-scale));
    --axt-fs-26:         calc(26px * var(--font-scale));
    --axt-radius-xl:     24px;
    --axt-shadow-lg:     0 16px 40px rgba(20,21,42,0.14);

    /* ── NON-CANONICAL EXTENSION — REWARDS RESKIN RUNGS ──────────────────────────────────────
       Added 2026-08-28, owner-approved (both, explicitly, over the "snap to nearest" alternative),
       for the member/rewards.php AXTNJI reskin. Both values were READ OFF the design's own CSS —
       the `__bundler/template` blob inside the owner's "My Account (3).html" Claude Design bundle,
       extracted and parsed, not sampled off a screenshot. Independently corroborated: the session
       doing the member/checkout.php reskin found the identical rules in a DIFFERENT bundle
       ("Checkout (2).html", whose shared design-system block carries the same "My Account · Rewards"
       section) — `.axt-rw__num{font-size:54px}` and `.axt-rw__card-label{font-size:21px}`. Two
       independent bundle reads agreeing is why these are not guesses.
       WHY THEY EXIST AT ALL: the rewards design asks for two sizes this theme has no token for.
       The reskin's LAW 1 forbids a raw literal in page markup, so the only honest routes were
       "add the token" or "change the design". The owner chose the tokens.
       WHY --axt-fs-* AND NOT --fs-21 / --fs-54: --fs-* is this file's CANON family and the canon
       catalog is LOCKED at 126 tokens. Extending it in place would make these look canonical to
       the next reader and to any tool diffing this file against the schema. Same reasoning the
       CHECKOUT RESKIN RUNGS block above records, and the declared prefix carries the honesty.
         --axt-fs-21   <- .axt-rw__card-label ("My Rewards", on the navy balance card) AND
                          .axt-rw__how-title ("How Rewards Work"). Canon jumps --fs-20 -> --fs-22.
                          --axt-fs-22 already exists but is 1px off AND is checkout's Summary-card
                          title — borrowing it would couple two unrelated pages to one rung.
         --axt-fs-54   <- .axt-rw__num, the points figure. The focal element of the whole panel.
                          Canon runs 48 / 49 / 52 and stops; --fs-52 is a visible 2px shrink on a
                          54px numeral, which is the one place on this page it would be noticed.
       NO 44px RUNG, deliberately, and recorded because it was offered: the design's mobile
       step-down is `@media (max-width:900px){.axt-rw__num{font-size:44px}}`, and --fs-44 ALREADY
       EXISTS in canon (line ~790, ladder 28/34/42/44/46/48/52). Adding --axt-fs-44 would have
       duplicated a canon token under an extension name — the exact shape the block above argues
       hardest against. Checked, not assumed.
       NOT ADDED, because two owner rulings removed the demand: the design's 13px card caption
       (the portal's no-13px rule from 2026-08-25 wins -> --default-text-size) and the design's
       rgba(255,255,255,.78) caption ink (--text-on-dark-muted at 0.70 wins). Neither needed a rung.
       Both carry * var(--font-scale), like every size token here, so the portal's text-scaling
       control keeps working on the reskinned page.
       NOTE FOR THE PAGE SIDE: text-[length:var(--axt-fs-21)] is a Tailwind ARBITRARY value. It
       does not exist until member/css/style.css is rebuilt. Shipping the token without the rebuild
       renders the type unstyled, not wrong-but-close.
       NOTHING EXISTING WAS TOUCHED in this pass — additive only, by design and verified by diff. */
    --axt-fs-21:         calc(21px * var(--font-scale));
    --axt-fs-54:         calc(54px * var(--font-scale));
}
