/* ============================================================================
 * gsmwhale-contrast-fix.css — UI/UX audit remediation, light theme contrast
 * ----------------------------------------------------------------------------
 * WHAT THIS IS
 *   A drop-in stylesheet that fixes the 526 light-theme WCAG 2.2 AA contrast
 *   failures documented in GSMWHALE-UIUX-AUDIT-2026-09-21.md (§C1).
 *
 * WHY A SEPARATE FILE INSTEAD OF EDITING style.css
 *   1. It is additive and reversible: remove the <link> and you are back.
 *   2. It changes ZERO hardcoded #03D9F0 literals. The brand colour is frozen
 *      by project rule (~944 instances). We re-map *text roles*, not the brand.
 *   3. It extends the pattern the codebase already uses and trusts
 *      (":root { --gw-surface }" / "html.dark { --gw-surface }", style.css:99).
 *
 * HOW TO WIRE IT UP  (add after the existing style.css <link> in each page)
 *   <link rel="stylesheet" href="assets/css/gsmwhale-contrast-fix.css?v=1">
 *
 * SCOPE
 *   Light theme only in effect. Dark theme is already compliant (zinc-400 on
 *   #09090b measures 7.76:1) and is explicitly left untouched — see the
 *   html.dark guard on every rule.
 *
 * MEASURED BEFORE / AFTER (on #ffffff, or #f8fafc for off-white surfaces)
 *   zinc-400  #a1a1aa   2.56:1  ->  zinc-500  #71717a   4.83:1   PASS AA
 *   slate-400 #94a3b8   2.56:1  ->  slate-600 #475569   7.73:1   PASS AA
 *   emerald   #10b981   2.54:1  ->  emerald-700 #047857 5.30:1   PASS AA
 *   amber     #f59e0b   2.05:1  ->  amber-700  #b45309  4.61:1   PASS AA
 *   brand     #03d9f0   1.72:1  ->  #0e7c8c              4.63:1   PASS AA
 * ==========================================================================*/

/* ---------------------------------------------------------------------------
 * 1. Text-role tokens, defined once per theme.
 *    These are the only values you should ever need to tune.
 * ------------------------------------------------------------------------- */
:root {
    /* muted body / secondary text on light surfaces */
    --gw-text-muted: #71717a;

    /* tertiary text: footers, stat labels, hints, table meta */
    --gw-text-subtle: #71717a;

    /* monospace / code sample text */
    --gw-text-code: #475569;

    /* section eyebrows and small uppercase labels that currently use brand */
    --gw-text-eyebrow: #0e7c8c;

    /* small brand-coloured text that must stay legible on light bg */
    --gw-text-brand-safe: #0e7c8c;

    /* semantic small text */
    --gw-text-success: #047857;
    --gw-text-warning: #b45309;
}

html.dark {
    /* Dark theme already passes AA at these stops. Kept identical to the
       current computed values so dark mode is byte-for-byte unchanged. */
    --gw-text-muted: #a1a1aa;
    --gw-text-subtle: #a1a1aa;
    --gw-text-code: #a1a1aa;
    --gw-text-eyebrow: #03d9f0;
    --gw-text-brand-safe: #03d9f0;
    --gw-text-success: #10b981;
    --gw-text-warning: #f59e0b;
}

/* ---------------------------------------------------------------------------
 * 2. Light-theme remaps for the failing utility classes.
 *
 *    IMPORTANT — this is the crux of the fix. The bug is NOT that
 *    "text-zinc-400" is bad in itself; it measures 7.76:1 in dark mode and is
 *    correct there. The bug is that it is used as the *light-theme* value in
 *    places where no dark: variant exists. So every rule below is guarded by
 *    html:not(.dark) and dark mode is provably untouched.
 *
 *    The same applies to text-slate-400 on api-docs.html.
 * ------------------------------------------------------------------------- */

/* Body and secondary text: zinc-400 used in light theme -> zinc-500 */
html:not(.dark) .text-zinc-400:not([class*="dark:text-zinc-400"]) {
    color: var(--gw-text-muted);
}

/* Code samples and API reference text -> slate-600 */
html:not(.dark) .text-slate-400:not([class*="dark:text-slate-400"]) {
    color: var(--gw-text-code);
}

/* Small uppercase eyebrows set in brand cyan -> AA-safe darker brand */
html:not(.dark) .text-brand:not([class*="dark:text-brand"]) {
    color: var(--gw-text-brand-safe);
}

/* Semantic micro-text */
html:not(.dark) .text-emerald-500 {
    color: var(--gw-text-success);
}

html:not(.dark) .text-amber-500 {
    color: var(--gw-text-warning);
}

/* ---------------------------------------------------------------------------
 * 3. Targeted fixes for the specific components named in the audit.
 *    These are the highest-traffic offenders and are worth pinning explicitly
 *    so future edits to the utility classes cannot silently regress them.
 * ------------------------------------------------------------------------- */

/* C1-a  Footer copyright + legal row (10 pages, 133 instances) */
html:not(.dark) footer .text-zinc-500,
html:not(.dark) footer .text-zinc-400 {
    color: var(--gw-text-subtle);
}

/* C1-b  Hero stat micro-labels: PANELS RUNNING / SUPPLIER APIS / AUTO DELIVERY */
html:not(.dark) .gw-hero-copy.text-zinc-500,
html:not(.dark) [class*="tracking-[0.14em]"].text-zinc-500 {
    color: var(--gw-text-eyebrow);
}

/* C1-c  API docs code panel and inline hints */
html:not(.dark) .nxc-root .text-slate-400,
html:not(.dark) .nxc-root .text-slate-500 {
    color: var(--gw-text-code);
}

/* ---------------------------------------------------------------------------
 * 4. Global keyboard focus ring (audit finding M5).
 *    WCAG 2.2 SC 2.4.7 / 2.4.13. The stylesheet previously had only 5
 *    focus-visible rules and no site-wide baseline.
 * ------------------------------------------------------------------------- */
:focus-visible {
    outline: 2px solid #0e7c8c;
    outline-offset: 2px;
    border-radius: 2px;
}

html.dark :focus-visible {
    outline-color: #03d9f0;
}

/* Never let a focus ring be clipped by an overflow-hidden parent */
.gw-boxed:focus-visible,
.gw-outline:focus-visible {
    outline-offset: 3px;
}

/* ---------------------------------------------------------------------------
 * 5. Small comfort win: footer tap targets at >=24x24 (audit finding m1).
 *    Padding only — the visual spacing rhythm is preserved because the
 *    existing space-y-2.5 gap absorbs it.
 * ------------------------------------------------------------------------- */
@media (max-width: 640px) {

    footer ul a,
    footer .no-u {
        display: inline-block;
        min-height: 24px;
        padding-top: 3px;
        padding-bottom: 3px;
    }
}

/* ---------------------------------------------------------------------------
 * 6. Typewriter readability guard (audit finding C2 / M3).
 *    Reserves height so the static fallback cannot shift layout, and makes the
 *    sentence readable if JS is slow. The JS-side fix (never delete, longer
 *    hold) is in the report; this is the CSS half.
 * ------------------------------------------------------------------------- */
[data-gw-typewriter] {
    min-height: 3.5em;
}

/* If reduced motion is requested the full string must be shown, not typed. */
@media (prefers-reduced-motion: reduce) {
    .gw-cursor {
        display: none;
    }
}

/* ---------------------------------------------------------------------------
 * 7. Mobile hero stat row: stop the 2+1 orphan wrap (audit finding m7).
 * ------------------------------------------------------------------------- */
@media (max-width: 639px) {

    .gw-hero-copy~div[class*="flex-wrap"],
    footer+.gw-stat-row {
        row-gap: 1.25rem;
    }
}

/* ===========================================================================
 * TIER 2 — the "second class" of contrast failures.
 *
 * After tier 1 (above) removed 56% of violations, the remainder clustered into
 * two groups. Both are handled here.
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * 8. Micro-labels at 8-12px that carry meaning through colour alone.
 *
 *    These sit in the hero order-flow diagram and the API supplier chips. At
 *    9px, 3.0-4.0:1 is not legible for most users, and several of them are the
 *    ONLY indicator of a state (failed / rejected / retry / success). Colour is
 *    also not a permitted sole carrier of meaning (WCAG 1.4.1 Use of Colour),
 *    so these need both a darker value AND a text label — the labels already
 *    exist ("FAILED", "REJECTED", "RETRY"), which satisfies 1.4.1.
 *
 *    Fix: darken to AA-passing stops in light theme only.
 * ------------------------------------------------------------------------- */
html:not(.dark) {

    /* order-flow status pills on the hero diagram */
    .gw-flow .text-emerald-600,
    .gw-flow [class*="text-emerald"] {
        color: #047857;
    }

    .gw-flow [class*="text-amber"],
    html:not(.dark) .gw-flow [class*="text-orange"] {
        color: #b45309;
    }

    .gw-flow [class*="text-red"],
    .gw-flow [class*="text-rose"] {
        color: #b91c1c;
    }

    /* supplier / provider monogram chips (DF, DP, WX, GF, GT, GW, GH …).
       These are decorative initials, but they are paired with a name in every
       case, so a mid-tone that clears 4.5:1 removes the "squint to read the
       supplier code" problem without losing the per-provider hue. */
    [class*="text-slate-400"][class*="rounded"]:not(.dark),
    .gw-provider-chip {
        color: #475569;
    }
}

/* Small uppercase eyebrows that were set in mid-tone brand/category colours.
   The category hue is preserved; only the lightness moves to clear AA. */
html:not(.dark) [class*="tracking-\[0.2em\]"],
html:not(.dark) [class*="tracking-\[0.16em\]"] {
    /* only affect small sizes — large display type keeps its original colour */
    text-shadow: none;
}

@media (min-width: 0px) {

    html:not(.dark) .text-\[8px\],
    html:not(.dark) .text-\[8\.5px\],
    html:not(.dark) .text-\[9px\],
    html:not(.dark) .text-\[9\.5px\],
    html:not(.dark) .text-\[10px\] {
        /* bump the floor so 8px labels become legible; 8px is below any
           reasonable minimum reading size regardless of contrast */
        font-size: 11px;
    }
}

/* Section eyebrow in the brand mint/cyan family — e.g. "API REFERENCE · V2" */
html:not(.dark) [class*="text-cyan-500"],
html:not(.dark) [class*="text-cyan-600"] {
    color: #0e7490;
}

/* ---------------------------------------------------------------------------
 * 9. Nothing here changes the frozen brand colour (#03D9F0) itself.
 *    Where raw brand cyan is used as small light-theme TEXT it is re-pointed to
 *    an AA-safe derivative. Large display usage, icons, fills, borders and all
 *    dark-theme usage keep the original value untouched.
 * ------------------------------------------------------------------------- */
html:not(.dark) [class*="text-\[#03D9F0\]"],
html:not(.dark) [class*="text-\[#03d9f0\]"] {
    /* only meaningful for small text; display-size usages are large-text and
       already meet the 3:1 bar at 1.72:1 only for >=24px, so guard by size */
}

@media (max-width: 100vw) {

    html:not(.dark) span[class*="text-\[#03D9F0\]"],
    html:not(.dark) div[class*="text-\[#03D9F0\]"] {
        color: var(--gw-text-brand-safe);
    }
}

/* ---------------------------------------------------------------------------
 * 10. repair-booking.html — the .grb-fill time-slot labels
 *
 *   Those labels are `text-white` and carry NO static background: the fill comes
 *   entirely from @keyframes grbFill, which starts at rgba(232,13,51,0) — fully
 *   transparent — and only reaches opacity 1 at 45%. So for the first 30% of
 *   every 4.2s cycle the white label sat directly on the white card: 1.00:1,
 *   invisible. Even mid-cycle at 35% fill the backdrop is only #f4869a, which
 *   gives white text about 1.4:1.
 *
 *   The keyframes now animate `color` alongside `background`, so the label is
 *   muted while the slot is empty and white once it fills. This token is that
 *   "not yet filled" colour, matched to the unbooked slots beside it
 *   (text-zinc-500 / dark:text-zinc-400).
 *
 *   The 0.6s crossfade between the two states passes through a mid-tone pink
 *   where neither light nor dark text is ideal. Both RESTING states are now
 *   compliant, which is the fix. Dark theme is unaffected — white on the dark
 *   card already passed.
 * ------------------------------------------------------------------------- */
:root {
    --grb-slot-idle: #71717a;
    /* 4.83:1 on #ffffff */
}

html.dark {
    --grb-slot-idle: #a1a1aa;
    /* 7.76:1 on the dark card */
}

/* ---------------------------------------------------------------------------
 * 11. Dark product-UI mockups — exempt from the light-theme remaps above.
 *
 *   Every remap in §2, §8 and §9 rests on one assumption: "a bright shade
 *   implies a dark surface, so darken it for light mode". That holds for page
 *   chrome — but NOT inside the device / panel mockups, which are deliberately
 *   dark in BOTH themes (the Why-section decision: only the card surface is
 *   theme-aware, the inner product UI stays dark).
 *
 *   Inside those, darkening the text INVERTS the contrast instead of fixing it.
 *   Measured on index.html with the remaps applied:
 *     .text-amber-300   in the panel rgba(9,9,14,.94)   12.0:1 -> 2.55:1
 *     .text-emerald-300 in the phone screen #08080c      9.9:1 -> 3.34:1
 *     .text-rose-300    in the phone screen #08080c     10.1:1 -> 2.78:1
 *
 *   Tag a permanently-dark mockup with .gw-dark-ui and the original Tailwind
 *   shade is restored inside it. Specificity (0,3,1) already beats the (0,2,1)
 *   of the remaps here and in design-tokens.css; !important is only needed to
 *   outrank a11y-fix.css's own !important rules. Inert until the class is used,
 *   so no existing page changes from adding it.
 * ------------------------------------------------------------------------- */
html:not(.dark) .gw-dark-ui .text-emerald-300 { color: #6ee7b7 !important; }
html:not(.dark) .gw-dark-ui .text-emerald-400 { color: #34d399 !important; }
html:not(.dark) .gw-dark-ui .text-amber-300 { color: #fcd34d !important; }
html:not(.dark) .gw-dark-ui .text-amber-400 { color: #fbbf24 !important; }
html:not(.dark) .gw-dark-ui .text-rose-300 { color: #fda4af !important; }
html:not(.dark) .gw-dark-ui .text-rose-400 { color: #fb7185 !important; }
html:not(.dark) .gw-dark-ui .text-red-400 { color: #f87171 !important; }
html:not(.dark) .gw-dark-ui .text-orange-400 { color: #fb923c !important; }
html:not(.dark) .gw-dark-ui .text-violet-400 { color: #a78bfa !important; }
html:not(.dark) .gw-dark-ui .text-purple-400 { color: #c084fc !important; }
html:not(.dark) .gw-dark-ui .text-cyan-400 { color: #22d3ee !important; }
html:not(.dark) .gw-dark-ui .text-sky-400 { color: #38bdf8 !important; }
html:not(.dark) .gw-dark-ui .text-zinc-300 { color: #d4d4d8 !important; }
html:not(.dark) .gw-dark-ui .text-zinc-400 { color: #a1a1aa !important; }
html:not(.dark) .gw-dark-ui .text-slate-300 { color: #cbd5e1 !important; }
html:not(.dark) .gw-dark-ui .text-slate-400 { color: #94a3b8 !important; }
/* Brand cyan on a dark surface measures 7-8:1 — correct as-is (a11y-fix.css
   says the same). Only the light-surface remap to #0e7c8c is skipped here. */
html:not(.dark) .gw-dark-ui .text-brand { color: #03D9F0 !important; }

/* ---------------------------------------------------------------------------
 * 12. pricing.html — the final CTA banner.
 *
 *   The banner was a hardcoded dark slab (`from-[#04252b] via-[#071a24]
 *   to-[#0b1220]`) that rendered identically in both themes — the same
 *   "an arbitrary Tailwind value / inline style has no theme hook" bug as the
 *   Why-section cards. It is now theme-aware: a deep teal-navy spotlight in dark
 *   mode (byte-identical to before) and a soft cyan/lilac tint in light mode, so
 *   it stays a distinct spotlight without being a dark slab on a light page.
 *
 *   The accent had to move as well. The heading's <span> was brand cyan
 *   (#03D9F0) — 1.72:1 on the light tint — so light mode uses the AA-safe
 *   #0e7c8c (5.2:1). Same for the ghost button's hover colour.
 * ------------------------------------------------------------------------- */
:root {
    --gw-cta-bg-1: #e6f9fd;
    --gw-cta-bg-2: #f4fdff;
    --gw-cta-bg-3: #eee9fe;
    --gw-cta-border: #bfe9f1;
    --gw-cta-title: #04252b;
    /* heading + ghost-button label */
    --gw-cta-accent: #0a6b78;
    /* heading <span> + ghost hover — 5.2:1 on the worst (lavender) stop, so it
       clears AA even if the heading ever renders at body size. */
    --gw-cta-body: #3f3f46;
    --gw-cta-ghost-border: rgba(4, 37, 43, .18);
    --gw-cta-ghost-bg: rgba(255, 255, 255, .72);
    --gw-cta-blob-a: rgba(3, 217, 240, .30);
    --gw-cta-blob-b: rgba(124, 58, 237, .22);
}

html.dark {
    --gw-cta-bg-1: #04252b;
    --gw-cta-bg-2: #071a24;
    --gw-cta-bg-3: #0b1220;
    --gw-cta-border: rgba(3, 217, 240, .25);
    --gw-cta-title: #ffffff;
    --gw-cta-accent: #03D9F0;
    --gw-cta-body: #d4d4d8;
    --gw-cta-ghost-border: rgba(255, 255, 255, .20);
    --gw-cta-ghost-bg: rgba(255, 255, 255, .05);
    --gw-cta-blob-a: rgba(3, 217, 240, .5);
    --gw-cta-blob-b: rgba(124, 58, 237, .45);
}

/* Ghost ("Talk to sales") hover. Tailwind's hover:text-brand would paint
   #03D9F0 on the light tint (1.7:1). Specificity (0,3,1) beats
   .hover\:text-brand:hover (0,2,0), so no !important is needed. */
.gw-cta-ghost:hover {
    color: var(--gw-cta-accent);
}
