/* =========================================================
   az-theme.css — THE ONLY PLACE COLOUR IS DEFINED
   Written by admin/theme.php. Do not hand-edit on the server.

   Measured from 127 local :root blocks; these are the dominant values, so
   adopting this file is visually a no-op. Derived shades use color-mix so
   one base value moves a whole family.
   ========================================================= */
:root{
  /* --- the four bases the panel writes ------------------- */
  --paper:#ffffff;          /* page background */
  --ink-base:#14121a;       /* body text */
  --chrome-base:#151b25;    /* app bar */
  --chrome-accent:#f5a524;  /* app bar highlight */
  /* Ink for text sitting ON --chrome-accent. White on this amber measures
     2.04:1; the ink token measures 9.1:1. The accent itself is NOT darkened,
     because against the dark bar -- where it does its actual job -- it is
     correct as it stands. The pairing was the bug, not the hue. */
  --on-chrome-accent:var(--ink-base);
  /* Text-highlight wash. layout-studio referenced var(--hi,#ffe9a8) for its
     highlighted runs and never defined --hi, so the literal won every time --
     a highlight the palette could not reach. Derived from --warning so it
     moves with the palette. */
  --highlight:color-mix(in srgb,var(--warning) 28%,var(--paper));
  /* --- the app bar ------------------------------------------
     Seven tokens, all derived from --chrome-base, so the panel moves the
     whole bar by changing one field. This is the derivation the pages were
     already carrying in a local :root; it belongs here. Until now
     az-common.css referenced all seven and NONE of them was defined
     anywhere -- the bar rendered only because every var() carried a literal
     fallback, which is a literal with extra steps and exactly the defect
     guard 3 exists to catch. */
  --chrome-bg:var(--chrome-base);
  --chrome-ink:var(--chrome-base);
  --chrome-text:color-mix(in srgb,var(--chrome-base) 8%,#fff);
  --chrome-muted:color-mix(in srgb,var(--chrome-base) 42%,#fff);
  --chrome-hover:color-mix(in srgb,var(--chrome-base) 72%,#fff);
  --chrome-line:color-mix(in srgb,var(--chrome-base) 78%,#fff);
  --chrome-raised:color-mix(in srgb,var(--chrome-base) 88%,#fff);


  /* --- surfaces --------------------------------------------
     PINNED, not derived. An earlier version computed these as
     color-mix(--paper -> --ink-base) and produced desaturated grey-brown:
     mixing cream toward dark brown does not pass through the warm shades
     this site actually uses. The palette is not a linear ramp between two
     colours, so the measured values are the defaults. The panel writes six
     fields instead of two -- a fair trade for not inventing colours that
     were never chosen. */
  /* Repainted from cream to the violet identity the studio hubs use. The
     six surface fields stay PINNED for the reason the note above gives --
     mixing white toward near-black gives flat greys, not the tinted violet
     these pages want. Each of these is white carrying a little --primary,
     so the whole family moves when the brand colour moves. */
  --bg:var(--paper);
  --surface:#f7f5ff;
  --surface-2:#efecfb;
  --panel:var(--surface);
  --line:#ece9f7;

  /* --- text ------------------------------------------------ */
  --text:var(--ink-base);
  --muted:#6b6880;
  --faint:color-mix(in srgb,var(--muted) 62%,var(--paper));

  /* --- brand and state ----------------------------------- */
  /* SPLIT, and the split is load-bearing. The dark scope needs to derive
     its accent FROM the brand colour, and a custom property cannot refer
     to itself -- `--primary:color-mix(...var(--primary)...)` is a cycle and
     computes to nothing. So the panel writes --primary-base and every
     scope resolves --primary from it. Pages keep reading var(--primary)
     and never learn about the split. */
  /* The wordmark gradient. Three brand hues that lived as literals inside the
     chrome CSS; the theme owns them so nothing outside this file holds a
     colour value. */
  --wordmark-1:#FFB067;
  --wordmark-2:#F43F73;
  --wordmark-3:#9B84FF;
  --primary-base:#6d28d9;
  --primary:var(--primary-base);
  /* Ink for text sitting ON a filled --primary surface. Pages were using
     var(--ink) for this, which is the page text colour and turned every
     violet button into 2.61:1. */
  --on-primary:var(--paper);
  --primary-soft:color-mix(in srgb,var(--primary) 10%,var(--paper));
  /* was #d92662: 4.77 on white but 4.10 on --surface-2, so every .danger
     button on a raised panel failed. #c81f57 measures 4.77 on the WORST
     surface and is the same hue. */
  --danger:#c81f57;
  --success:#0f766e;
  --warning:#9a4708;   /* was #b45309: 4.32 on --surface-2 */
  /* --focus was a green (#095c4f) left over from an earlier palette. Tools
     alias --accent to it, so vector rendered a green drop zone in the
     middle of a cream site -- one visual identity means this reads from
     the same family as everything else. */
  --focus:color-mix(in srgb,var(--primary) 82%,#000);

  /* --- syntax colours ---------------------------------------
     The code editor's highlighting. PINNED, not derived: these have to stay
     DISTINCT from each other to do their job, and deriving twelve hues from
     one brand colour would collapse them toward a single tint -- the same
     reason themevars.py refuses to flatten the --c-* family to one token.
     Centrally editable is the goal, not centrally generated.

     Replaces Ace's vendor palette, eight of whose colours failed WCAG on
     white: the type hue at 3.86, library/constant at 3.91, variable at 4.32,
     invisibles at 1.84, regex at 4.00, parameter at 2.18. Every value below
     is verified >=4.5 against --paper in this scope and in the dark scope. */
  --code-keyword:#930f80;
  --code-string:#1a1aa6;
  --code-number:#0000cd;
  --code-comment:#236e24;
  --code-type:#4a55c4;
  --code-func:#3c4c72;
  --code-var:#2a6f7e;
  --code-const:#0a7a12;
  --code-attr:#994409;
  --code-mute:#6b6880;
  --code-param:#8a5200;
  --code-regex:#c40000;
  --code-invalid:#990000;


  /* --accent is NOT here. Each tool tints itself -- Quote is crimson,
     Compress brown -- and 43 distinct values across 32 files says that is
     deliberate. Tools set it locally. The app bar reads --chrome-accent
     instead, so it never inherits a tool's tint. */

  /* =====================================================
     PRESENTATION TOKENS
     Everything below is here so the whole site can be
     adjusted from this one file: type, rhythm, shape,
     measure and the breakpoint the layouts turn at.
     The values are MEASURED from what the site already
     uses, not invented, so adopting them changes nothing
     until someone deliberately moves one.
     ===================================================== */

  /* --- type scale -----------------------------------------
     The site's real distribution, clustered: 210 uses of
     12px, 138 of 13px, 121 of 11.5px and so on. Each step is
     a name rather than a number so a whole tier moves at
     once. --text-base drives the body; the rest are ratios
     of it, so changing one value rescales the site. */
  --text-base:13px;
  --text-3xs:calc(var(--text-base) * .77);   /* 10px  */
  --text-2xs:calc(var(--text-base) * .85);   /* 11px  */
  --text-xs:calc(var(--text-base) * .88);    /* 11.5px */
  --text-sm:calc(var(--text-base) * .92);    /* 12px  */
  --text-md:var(--text-base);                /* 13px  */
  --text-lg:calc(var(--text-base) * 1.08);   /* 14px  */
  --text-xl:calc(var(--text-base) * 1.15);   /* 15px  */
  --text-2xl:calc(var(--text-base) * 1.69);  /* 22px  */
  --text-3xl:calc(var(--text-base) * 2);     /* 26px  */
  --text-hero:clamp(30px,5vw,52px);

  /* --- line heights --------------------------------------- */
  --lh-tight:1.2;
  --lh-snug:1.45;
  --lh-base:1.55;
  --lh-relaxed:1.65;

  /* --- spacing --------------------------------------------
     One step drives the rhythm: padding, gaps and margins are
     multiples of --space, so tightening or opening up the
     whole site is a single edit. */
  --space:8px;
  --space-2xs:calc(var(--space) * .25);   /* 2px  */
  --space-xs:calc(var(--space) * .5);     /* 4px  */
  --space-sm:calc(var(--space) * .75);    /* 6px  */
  --space-md:var(--space);                /* 8px  */
  --space-lg:calc(var(--space) * 1.5);    /* 12px */
  --space-xl:calc(var(--space) * 2);      /* 16px */
  --space-2xl:calc(var(--space) * 3);     /* 24px */
  --space-3xl:calc(var(--space) * 5);     /* 40px */

  /* --- typefaces ------------------------------------------
     One place to change the face of the whole site. Tools
     that alias --sl-body or --mono onto these keep working. */
  --font-display:"Bricolage Grotesque","Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
  --font-body:system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans",sans-serif;
  --font-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Noto Sans Mono",monospace;
  --font-serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Noto Serif",serif;

  /* --- measure and containers -----------------------------
     --measure is the comfortable reading column, in CHARACTERS
     so it tracks the typeface and size automatically. */
  --measure:68ch;
  --container:1220px;
  --container-wide:1500px;

  /* --- the breakpoint the layouts turn at -----------------
     Not usable inside @media (custom properties cannot be),
     but recorded here as the single documented value so the
     generators and any hand-written rule agree on it. */
  --bp-mobile:900px;

  /* --- metrics ------------------------------------------- */
  --radius:12px;
  --radius-sm:9px;
  --radius-xs:6px;
  --radius-lg:14px;
  --radius-pill:999px;
  /* On cream a warm 18% shadow read as depth; on white the same value is a
     grey smudge, so the shadow is tinted with the brand instead. */
  --shadow:0 10px 30px color-mix(in srgb,var(--primary) 12%,transparent);
}

/* =========================================================
   THE DARK SCOPE

   Not a second palette with second names. This block redefines the SAME
   core token names under one canonical selector, so every var(--paper),
   var(--text), var(--muted) already written across the site keeps working
   and simply resolves darker inside the scope. No page rewrites a single
   reference.

   Because custom properties inherit, the scope NESTS. A dark inspector
   panel inside a light page is <div data-az-scope="dark"> and everything
   below it re-resolves. That is the structural fix for the dark-on-dark
   class: tools/dom.html's inspector was painting a dark surface with a
   literal while its text token resolved from the light theme (1.08:1).

   WHY EVERY DERIVED TOKEN IS RE-DECLARED HERE
   This is the trap. var() inside a custom property is substituted at the
   element where that property is DECLARED, not where it is used. --faint
   is declared on :root, so it substitutes :root's --muted and --paper and
   inherits down ALREADY RESOLVED. Redefining --muted in this block would
   not move it. Every token whose value contains var() or color-mix must be
   re-declared here or it silently keeps its light value -- which is exactly
   the failure mode this whole exercise exists to remove. themevars.py
   copies these from :root and FAILS the build if the two lists diverge.

   The surfaces are PINNED, for the reason the top of this file gives:
   mixing white toward near-black gives flat greys, not the tinted violet
   the site uses. These are near-black carrying a little violet.

   The ACCENT is the one exception and is DERIVED. It has to be: a pinned
   dark accent stops following the panel, so changing the brand colour
   would silently break dark mode. Mixing 40% of the brand into the scope's
   own ink keeps it on-brand and floors the contrast -- measured across
   violet, navy, crimson, teal, amber, jade and pure black, the worst case
   is 4.83:1 on --surface-2. A fixed mix cannot PROVE 4.5 for every
   conceivable brand value, so audit.js remains the check.
   ========================================================= */
[data-az-scope="dark"]{
  color-scheme:dark;

  /* --- the bases (pinned) --------------------------------- */
  --paper:#12101a;
  --ink-base:#ecebf3;
  --surface:#1a1726;
  --surface-2:#241f33;
  --line:#322c46;
  --muted:#a29dba;

  /* --- state, lifted off the dark ground ------------------ */
  --danger:#ff8fb0;
  --success:#5ecfc0;
  --warning:#f0b757;

  /* --- everything below is DERIVED and must be repeated ---
     Keep in step with :root. themevars.py checks this. */
  --bg:var(--paper);
  --panel:var(--surface);
  --text:var(--ink-base);
  --faint:color-mix(in srgb,var(--muted) 62%,var(--paper));
  --primary:color-mix(in srgb,var(--primary-base) 40%,var(--ink-base));
  --on-primary:var(--paper);
  --primary-soft:color-mix(in srgb,var(--primary) 18%,var(--paper));
  /* :root mixes --focus toward black, which is invisible here. */
  --focus:color-mix(in srgb,var(--primary) 82%,#fff);
  --shadow:0 10px 30px rgb(0 0 0 / .45);
  --border:var(--line);
  --mut:var(--muted);
  --raised:var(--surface-2);
  --panel-2:var(--surface-2);
  --bg-2:var(--surface);
  --ink:var(--text);

  /* syntax, lifted for the dark ground -- same twelve roles, verified >=4.5
     against this scope's --paper. */
  --code-keyword:#f19ae0;
  --code-string:#9fb6ff;
  --code-number:#8fa8ff;
  --code-comment:#7fd08a;
  --code-type:#a7b0ff;
  --code-func:#a8c0e8;
  --code-var:#7fd0dd;
  --code-const:#7ddc8a;
  --code-attr:#e8b07a;
  --code-mute:#a29dba;
  --code-param:#e8bb7a;
  --code-regex:#ff9a9a;
  --code-invalid:#ff8f8f;
}

/* The dark scope paints its own ground. Without this a scope on <html> or
   <body> leaves the canvas white behind short pages. */
html[data-az-scope="dark"],
body[data-az-scope="dark"]{background:var(--paper);color:var(--text);}

/* =========================================================
   LEGACY ALIASES — delete as each page's own :root is removed.
   These let the 127 local blocks come out a few at a time instead of in one
   flag day: a page that still defines --border keeps working, a page that
   has had its block removed picks up the alias here.
   ========================================================= */
:root{
  --border:var(--line);
  --mut:var(--muted);
  --raised:var(--surface-2);
  --panel-2:var(--surface-2);
  --bg-2:var(--surface);
}

/* ---------------------------------------------------------
   --ink COMPATIBILITY, scoped and temporary.

   --ink is the one token that cannot be aliased globally: it means
   BACKGROUND in some files (var(--paper), 26 uses) and TEXT in others (#35251f,
   16 uses). Same name, opposite meaning -- a global alias would invert a
   third of the pages that use it.

   On the unframed tool pages it is used in the text sense: Compress's
   Convert button is background:var(--ink);color:#fff, which renders white on
   cream when --ink is undefined. Scoping the text-sense alias to those pages
   fixes it without touching anything else. Remove this block once --ink has
   been resolved file by file (THEME-PROTOCOL.md section 2).
   --------------------------------------------------------- */
body[data-az-scroll="page"]{
  --ink:var(--text);
}
