/* =============================================================================
   axogeo design tokens
   -----------------------------------------------------------------------------
   The one file to open to change how the app looks. Loaded before styles.css.

   THREE LAYERS, and the order matters:

     1. PRIMITIVES  the raw ramp, named by what a colour IS. Never referenced
                    by a component. Changing one here changes every semantic
                    token built on it.
     2. SEMANTIC    named by ROLE, and the only thing a component may use. A
                    theme is a remapping of THIS layer onto the primitives, so
                    components never learn a theme exists.
     3. LEGACY      the old literal names, aliased onto the semantic layer so
                    stylesheets that have not been migrated keep working. This
                    block shrinks to nothing as the migration proceeds.

   WHY THIS EXISTS. Measured 2026-09-01: 169 distinct colours across 643
   declarations in 43 stylesheets, against a declared palette of eleven. Five
   near-identical greys for one job, three off-whites, eleven near-blacks, and
   fourteen greens where one accent is declared. Nobody chose 169; each was
   eyeballed once and never reconciled.

   THE RULE THAT KEEPS IT FIXED: a component stylesheet declares no literal
   colour. If a role is missing, add a semantic token here rather than a hex
   there.
   ========================================================================== */

:root {
	/* --- 1. PRIMITIVES ---------------------------------------------------- */

	/* Neutral ramp. Cool-biased rather than pure grey, which is what the app
	   already reached for by hand: the near-black cluster it replaces sat at
	   hue 214 to 220 in every one of its eleven variants. */
	--grey-950: #17181c;
	--grey-900: #1e1f22;
	--grey-850: #2c2c2c;
	--grey-800: #353535;
	--grey-700: #45464d;
	--grey-600: #5c5d63;
	--grey-500: #7d7e84;
	--grey-400: #9a9aa0;
	--grey-200: #c9c9ce;
	--grey-100: #e8e8ea;
	--grey-050: #f5f5f5;

	/* Accent. --teal-500 is the declared brand colour; the rest are the states
	   the app was already improvising with separate hexes. */
	--teal-700: #0e5d54;
	--teal-600: #14b88a;
	--teal-500: #25d0b9;
	--teal-400: #4fe3cd;
	--teal-500-rgb: 37, 208, 185;

	/* Status. One value each, replacing seven reds and six ambers. */
	--coral-400: #ff8a76;
	--coral-400-rgb: 255, 138, 118;
	--amber-400: #e5b567;
	--amber-400-rgb: 229, 181, 103;
	--blue-400: #7aa7e8;
	--blue-400-rgb: 122, 167, 232;
	/* The animation system's mark: a keyed param, a curve handle. Reads as
	   gold rather than warning, and is 37 deltaE from --amber-400. */
	--gold-400: #ffbf00;

	/* Alpha ramps. The app had 25 distinct white alphas and 9 black, including
	   0.028 and 0.035. These four and two are the whole vocabulary. */
	--white-a06: rgba(255, 255, 255, 0.06);
	--white-a07: rgba(255, 255, 255, 0.07);
	--white-a09: rgba(255, 255, 255, 0.09);
	--white-a10: rgba(255, 255, 255, 0.1);
	--white-a14: rgba(255, 255, 255, 0.14);
	--white-a18: rgba(255, 255, 255, 0.18);
	--white-a24: rgba(255, 255, 255, 0.24);
	--white-a38: rgba(255, 255, 255, 0.38);
	--black-a35: rgba(0, 0, 0, 0.35);
	--black-a25: rgba(0, 0, 0, 0.25);
	--black-a45: rgba(0, 0, 0, 0.45);
	--black-a55: rgba(0, 0, 0, 0.55);

	/* --- 2. SEMANTIC ------------------------------------------------------ */

	/* Surfaces. base is the page, raised is a panel, sunken is a well or an
	   input. The four OVERLAYS are distinct jobs, not shades of one: a menu
	   must be readable over anything, a control floating on the viewport is
	   deliberately see-through, and a veil is neither. */
	--surface-base: var(--grey-850);
	--surface-raised: var(--grey-800);
	--surface-sunken: var(--grey-950);
	--surface-overlay: rgba(24, 24, 26, 0.96);
	--surface-overlay-light: rgba(30, 30, 32, 0.92);
	--surface-overlay-glass: rgba(30, 30, 32, 0.82);
	--surface-overlay-dark: rgba(12, 12, 14, 0.58);
	--surface-hover: var(--white-a06);
	--surface-active: var(--white-a10);
	--surface-selected: var(--white-a18);
	/* The filled background of a control: an input, a slider track, a
	   playback button. Opaque, and lighter than a panel, which no other
	   surface token was. */
	--surface-control: var(--grey-700);

	/* Text. --text-primary is #e8e8ea rather than the declared --lightest-grey
	   because that is what the app ACTUALLY renders body text at, in 32 places. */
	--text-primary: var(--grey-100);
	--text-secondary: var(--grey-400);
	--text-muted: var(--grey-500);
	--text-disabled: var(--grey-600);
	--text-on-primary: #16191c;
	/* A scrim stays dark in every theme, so text on one stays light. */
	--text-on-scrim: rgba(255, 255, 255, 0.72);

	/* Borders. Translucent so a border reads correctly on any surface beneath
	   it, which is why the app reached for white alphas rather than greys. */
	--border-subtle: var(--white-a07);
	--border-default: var(--white-a09);
	--border-strong: var(--white-a14);
	/* the emphatic end. Added for batch 1a: 26 declarations sat at 0.22-0.30
	   and snapping them to --border-strong would have halved them. */
	--border-emphasis: var(--white-a24);
	/* the border of a control the pointer is on. Added for the primitive
	   sweep: three hover rules sat at --grey-500, which is 13 to 19 deltaE
	   from --border-emphasis, so snapping them would have flattened the
	   hover ramp rather than migrated it. */
	--border-hover: var(--white-a38);
	--border-focus: var(--teal-500);

	/* Accent. */
	--color-primary: var(--teal-500);
	--color-primary-hover: var(--teal-400);
	--color-primary-active: var(--teal-600);
	--color-primary-deep: var(--teal-700);
	/* The accent as bare channels, for the tints that need an alpha. This is
	   SEMANTIC, not the primitive it starts out as: every theme restates it,
	   because 71 declarations read it and each one stayed teal while the
	   accent beside it changed. */
	--color-primary-rgb: var(--teal-500-rgb);
	--color-primary-soft: rgba(var(--color-primary-rgb), 0.14);
	/* Tinted borders on an accent-status chip. Three steps because the run
	   indicator encodes running / done / hover as three strengths, and one
	   step would have collapsed running into done. */
	--color-primary-border-soft: rgba(var(--color-primary-rgb), 0.3);
	--color-primary-border: rgba(var(--color-primary-rgb), 0.45);
	--color-primary-border-hover: rgba(var(--color-primary-rgb), 0.6);

	/* Status. */
	/* A status colour is a WASH, a BORDER, or the mark itself, and the first
	   two need a hover step. Measured over --surface-overlay, two wash steps
	   carry the nine alphas the app had improvised, worst case 7.4 deltaE. */
	--color-danger: var(--coral-400);
	--color-danger-soft: rgba(var(--coral-400-rgb), 0.14);
	--color-danger-soft-hover: rgba(var(--coral-400-rgb), 0.22);
	--color-danger-border: rgba(var(--coral-400-rgb), 0.3);
	--color-danger-border-hover: rgba(var(--coral-400-rgb), 0.42);
	--color-warning: var(--amber-400);
	--color-warning-soft: rgba(var(--amber-400-rgb), 0.14);
	--color-warning-border: rgba(var(--amber-400-rgb), 0.65);
	--color-info: var(--blue-400);
	--color-info-soft: rgba(var(--blue-400-rgb), 0.14);
	--color-info-border: rgba(var(--blue-400-rgb), 0.55);
	--color-animated: var(--gold-400);

	/* Node header legend, keyed to what a node RETURNS. Not an accent: these
	   stay tellable apart from each other rather than following the theme.
	   The light grounds take pastels of the same four hues, because the dark
	   ones carry near-black text at 2.1 to 2.2:1 there. */
	--node-primitive: var(--color-primary-deep);
	--node-operation: #334d96;
	--node-computed: #9c2a07;
	--node-terminal: #7040a8;

	/* Frame tints. The hue is the person's pick, so only its STRENGTH is
	   themed: how much fill reads against the grid, and how far the label is
	   darkened. Darkening toward black rather than toward the ink keeps the
	   hue exactly, which is the whole point of a frame colour. */
	--frame-fill: 8%;
	--frame-edge: 45%;
	--frame-shade: 0%;

	/* Depth. */
	--shadow-soft: var(--black-a25);
	--shadow-panel: var(--black-a35);
	--shadow-deep: var(--black-a55);
	--scrim: var(--black-a45);

	/* --- 3. LEGACY ALIASES ------------------------------------------------ */
	/* Every one of these points at the SEMANTIC layer, never at a primitive.
	   Four of them used to point at the grey ramp, which is theme-invariant
	   by design, so a light theme turned surfaces light and left the text
	   on them near-white. Keep this rule: an alias that resolves to a
	   primitive cannot theme, and the failure is silent. */
	/* The old literal names, pointed at the semantic layer. Nothing new may use
	   these. Each disappears when the last stylesheet referencing it migrates,
	   and the block is empty when the migration is done.

	   NOT YET IN EFFECT. styles.css declares these same names after this file
	   loads, so its literals still win. That is deliberate: it keeps adding
	   this file a change of nothing at all. Deleting that block is the first
	   step that moves a pixel, and four of these aliases DO shift when it
	   happens (--light-grey, --medium-light-grey, --medium-grey and
	   --darkest-grey-alpha-90 all resolve to a consolidated value). */
	--lightest-grey: var(--text-primary);
	--light-grey: var(--text-secondary);
	--medium-light-grey: var(--text-muted);
	--medium-grey: var(--surface-control);
	--dark-grey: var(--surface-raised);
	--darkest-grey: var(--surface-base);
	--darkest-grey-alpha-90: var(--surface-overlay-dark);
	--axogeo-green: var(--color-primary);
	--axogeo-green-rgb: var(--color-primary-rgb);
	--dark-green: var(--color-primary-deep);
}

/* =============================================================================
   THEMES — preview layer
   =============================================================================
   Additive. Nothing here applies until an element carries data-theme, so the
   default appearance is unchanged and this block can be deleted without
   touching anything else.

   These override LAYER 2 (semantic) only. Layer 1 primitives stay put, and no
   component stylesheet is touched, which is what makes a theme swap possible
   at all: the accent is 110 token references and zero hardcoded values.

   Try one:  document.documentElement.dataset.theme = 'rust'
   Back:     delete document.documentElement.dataset.theme

   SURFACES ARE ONLY PARTLY THEMED. 39 of 44 stylesheets still hold literal
   greys, so a theme changes the accent everywhere and the panels only where
   they already read a token. That is the token migration, not a theme bug.
   ========================================================================== */

:root[data-theme='rust'] {
	--color-primary-rgb: 156, 42, 7;
	--color-primary: #9c2a07;
	--color-primary-hover: #b83a12;
	--color-primary-active: #7d1f04;
	--color-primary-deep: #5c1703;
	--color-primary-soft: rgba(156, 42, 7, 0.16);
	--border-focus: #b83a12;
	/* rust is dark, so text on it must go light rather than near-black */
	--text-on-primary: #f7ece8;
}

/* Cozy low poly: honey and amber against warm brown, the palette of the
   reference renders. Warmer neutrals too, so panels stop reading as cool grey
   wherever they already use a surface token. */
:root[data-theme='cozy'] {
	--color-primary-rgb: 232, 163, 61;
	--color-primary: #e8a33d;
	--color-primary-hover: #f2b658;
	--color-primary-active: #cf8a29;
	--color-primary-deep: #8a5a1c;
	--color-primary-soft: rgba(232, 163, 61, 0.16);
	--border-focus: #e8a33d;
	--text-on-primary: #2b1d0c;

	--surface-base: #2a2320;
	--surface-raised: #342b26;
	--surface-sunken: #1e1917;
	--surface-overlay: rgba(42, 35, 32, 0.96);
	--surface-overlay-dark: rgba(20, 16, 14, 0.62);

	--text-primary: #f0e2c8;
	--text-secondary: #b9a68c;
	--text-muted: #8d7c66;

	--border-subtle: rgba(240, 226, 200, 0.07);
	--border-default: rgba(240, 226, 200, 0.1);
	--border-strong: rgba(240, 226, 200, 0.16);

	/* sage, from the trees in the reference */
	--color-info: #7c9a6b;
	--color-warning: #d4762a;
	--scrim: rgba(20, 16, 14, 0.55);
}

/* --- light -----------------------------------------------------------------
   The one that proves how far the migration has to go. Surfaces invert, so
   every stylesheet still holding a literal dark grey keeps it and ends up
   dark-on-light. Judge the palette here, not the result. */
:root[data-theme='light'] {
	--color-primary-rgb: 18, 168, 146;
	--surface-control: #e2e2e6;
	--shadow-soft: rgba(0, 0, 0, 0.07);
	--shadow-panel: rgba(0, 0, 0, 0.14);
	--shadow-deep: rgba(0, 0, 0, 0.22);
	--border-emphasis: rgba(0, 0, 0, 0.28);
	--surface-selected: rgba(0, 0, 0, 0.14);
	--surface-base: #f7f7f8;
	--surface-raised: #ffffff;
	--surface-sunken: #ececee;
	--surface-overlay: rgba(255, 255, 255, 0.97);
	--surface-overlay-light: rgba(255, 255, 255, 0.92);
	--surface-overlay-glass: rgba(255, 255, 255, 0.82);
	--surface-overlay-dark: rgba(0, 0, 0, 0.35);
	--surface-hover: rgba(0, 0, 0, 0.05);
	--surface-active: rgba(0, 0, 0, 0.09);

	--text-primary: #1c1d21;
	--text-secondary: #55575f;
	--text-muted: #7d7e84;
	--text-disabled: #a8a9af;
	--text-on-primary: #06241f;

	--border-subtle: rgba(0, 0, 0, 0.07);
	--border-default: rgba(0, 0, 0, 0.11);
	--border-strong: rgba(0, 0, 0, 0.18);
	--border-hover: rgba(0, 0, 0, 0.4);

	--color-primary: #12a892;
	--color-primary-hover: #0f9280;
	--color-primary-active: #0c7a6b;
	--color-primary-deep: #0a5f54;
	--color-animated: #8a6200;
	/* A status colour is TEXT as often as it is a wash, and the dark ramp's
	   coral, amber and blue sit at 1.8 to 2.3:1 on this ground. Darkened to
	   clear 4.5:1. The washes are built on the primitive rgb, so they keep
	   their pale tint and only the mark moves. */
	--color-danger: #c0341a;
	--color-warning: #8a6200;
	--color-info: #2c5fa8;
	--node-primitive: #bae8e3;
	--node-operation: #bac6e8;
	--node-computed: #e8c5ba;
	--node-terminal: #cfbae8;
	--frame-fill: 18%;
	--frame-edge: 60%;
	--frame-shade: 50%;

	--shadow-panel: rgba(0, 0, 0, 0.14);
	--scrim: rgba(0, 0, 0, 0.32);
}

/* --- forest: the sage and moss of the reference renders, kept dark -------- */
:root[data-theme='forest'] {
	--color-primary-rgb: 143, 191, 106;
	--color-primary: #8fbf6a;
	--color-primary-hover: #a3ce80;
	--color-primary-active: #78a556;
	--color-primary-deep: #47632f;
	--color-primary-soft: rgba(143, 191, 106, 0.16);
	--border-focus: #8fbf6a;
	--text-on-primary: #17240d;
	--surface-base: #23281f;
	--surface-raised: #2c3227;
	--surface-sunken: #1a1e17;
	--text-primary: #e9eee2;
	--text-secondary: #a7b39a;
	--scrim: rgba(15, 18, 12, 0.55);
}

/* --- ember: deep coals, the warm end without the honey -------------------- */
:root[data-theme='ember'] {
	--color-primary-rgb: 226, 100, 60;
	--color-primary: #e2643c;
	--color-primary-hover: #ee7a54;
	--color-primary-active: #c44f2c;
	--color-primary-deep: #7d2f16;
	--color-primary-soft: rgba(226, 100, 60, 0.16);
	--border-focus: #e2643c;
	--text-on-primary: #2a0f06;
	--surface-base: #2b2422;
	--surface-raised: #362d2a;
	--surface-sunken: #1f1a18;
	--text-primary: #f2e6e1;
	--text-secondary: #b8a49c;
	--scrim: rgba(20, 15, 13, 0.55);
}

/* --- slate: cool and quiet, the accent pulled toward blue ----------------- */
:root[data-theme='slate'] {
	--color-primary-rgb: 90, 169, 230;
	--color-primary: #5aa9e6;
	--color-primary-hover: #7dbcee;
	--color-primary-active: #4590c9;
	--color-primary-deep: #285d87;
	--color-primary-soft: rgba(90, 169, 230, 0.16);
	--border-focus: #5aa9e6;
	--text-on-primary: #06202f;
	--surface-base: #24272d;
	--surface-raised: #2d313a;
	--surface-sunken: #1a1c21;
	--text-primary: #e6e9ef;
	--text-secondary: #9aa2b1;
	--scrim: rgba(14, 16, 20, 0.55);
}

/* --- sand: the light warm one, cozy inverted ----------------------------- */
:root[data-theme='sand'] {
	--color-primary-rgb: 208, 138, 30;
	--surface-control: #e4d8c4;
	--shadow-soft: rgba(60, 42, 20, 0.08);
	--shadow-panel: rgba(60, 42, 20, 0.15);
	--shadow-deep: rgba(60, 42, 20, 0.24);
	--border-emphasis: rgba(60, 42, 20, 0.30);
	--surface-selected: rgba(90, 60, 20, 0.16);
	--surface-base: #f4ede1;
	--surface-raised: #fffaf2;
	--surface-sunken: #e7ddcc;
	/* sand is a LIGHT theme, so it has to say so for the overlays too. Without
	   these four it inherited the dark root values, and every menu, dialog and
	   toolbar pill stayed near-black while the text on them went dark. The
	   five dark themes inherit the same values correctly, which is why only
	   this one needed them. */
	--surface-overlay: rgba(255, 250, 242, 0.97);
	--surface-overlay-light: rgba(255, 250, 242, 0.92);
	--surface-overlay-glass: rgba(255, 250, 242, 0.82);
	--surface-overlay-dark: rgba(40, 28, 12, 0.35);
	--text-disabled: #b3a68d;
	--surface-hover: rgba(90, 60, 20, 0.06);
	--surface-active: rgba(90, 60, 20, 0.1);
	--text-primary: #2b2318;
	--text-secondary: #6b5c47;
	--text-muted: #93836b;
	--text-on-primary: #2b1d0c;
	--border-subtle: rgba(60, 42, 20, 0.08);
	--border-default: rgba(60, 42, 20, 0.12);
	--border-strong: rgba(60, 42, 20, 0.2);
	--border-hover: rgba(60, 42, 20, 0.42);
	--color-primary: #d08a1e;
	--color-primary-hover: #e09c31;
	--color-primary-active: #b07314;
	--color-primary-deep: #7d5210;
	--color-animated: #8a6200;
	/* A status colour is TEXT as often as it is a wash, and the dark ramp's
	   coral, amber and blue sit at 1.8 to 2.3:1 on this ground. Darkened to
	   clear 4.5:1. The washes are built on the primitive rgb, so they keep
	   their pale tint and only the mark moves. */
	--color-danger: #c0341a;
	--color-warning: #8a6200;
	--color-info: #2c5fa8;
	--node-primitive: #bae8e3;
	--node-operation: #bac6e8;
	--node-computed: #e8c5ba;
	--node-terminal: #cfbae8;
	--frame-fill: 18%;
	--frame-edge: 60%;
	--frame-shade: 50%;
	--scrim: rgba(40, 28, 12, 0.32);
}
