/* Foilworks design tokens.
 *
 * Values only. Nothing in this file selects anything but `:root`, and nothing
 * outside it declares a raw colour — index.test.js enforces both, because the
 * one thing that reliably wrecks a stylesheet this size is a hex code typed
 * inline "just this once".
 *
 * Where a rule goes:
 *
 *   tokens.css   values, no selectors
 *   base.css     bare element selectors — what an <input> or an <h2> looks
 *                like anywhere on the site
 *   ui.css       classes — the reusable widgets
 *
 * If you find yourself wanting a fourth place, you want a class in ui.css.
 *
 * PALETTE PROVENANCE. The colours are taken wholesale from ~/ml's "dusk over
 * the dunes" palette, deliberately and with permission — the two are the same
 * operator's projects and are meant to look related. What is NOT carried across
 * is that palette's provenance story: the magenta there was sampled from ~198k
 * fabric pixels of a photographed speedwing, and the contrast ratios quoted
 * below were measured against that palette's surfaces. Those are ~/ml's
 * measurements. They are reproduced here as the reason the values are what they
 * are, not as claims this repo has verified. Re-measure before changing any.
 *
 * Dark throughout, and there is no light theme. That suits this application:
 * the pages that will matter are plots — Cp distributions, streamlines,
 * boundary-layer profiles — and a near-black instrument surface is the right
 * ground for a chart.
 */

:root {
	/* --- Palette -------------------------------------------------------
	 *
	 * Raw colour, named for what it is. Do not reference these outside this
	 * file: use the semantic roles below, so a value changes in one place.
	 *
	 *   canopy     the signal colour. One accent carries the interface, so it
	 *              has to be the loud one. Sampled from a wing canopy.
	 *   iris       the violet from the same wing's trailing-edge band, hue
	 *              278°. This is the lit band rather than the median violet,
	 *              which is only 3.5:1 on --basalt and cannot carry text.
	 *   marram     positive and available states. Grey-green and deliberately
	 *              undersaturated, so "good" reads as calm rather than as a
	 *              notification.
	 *   amber      attention that is not an error — a notice, something
	 *              waiting, an EXPERIMENTAL marker.
	 *   vermilion  destructive only, never decorative. Orange-red rather than
	 *              crimson: crimson sits 41° from canopy, close enough that a
	 *              destructive button reads as a slightly-off primary one. At
	 *              57° it cannot be misread.
	 */
	--void: #08090D;
	--void-hi: #0B0D13;
	--basalt: #101319;
	--basalt-hi: #171B23;

	--dune-1: #E7EBF3;
	--dune-2: #A6AEC0;
	--dune-3: #838CA1;

	--canopy: #E629B7;
	--canopy-hi: #F55BCE;
	--iris: #B56BE1;
	--iris-hi: #C689EA;

	--marram: #93B79A;
	--amber: #FFB84D;
	--vermilion: #FF6B47;

	/* --- Surfaces ------------------------------------------------------ */

	--surface-page: linear-gradient(-0.0625turn, var(--void), var(--void-hi));
	/* A raised panel: cards, the header band, the nav bar. */
	--surface-1: var(--basalt);
	--surface-2: var(--basalt-hi);
	/* A well you type into — recessed, not raised. */
	--surface-inset: rgba(0, 0, 0, 0.32);
	/* Subtle lift for a field row or a neutral chip. These genuinely lighten;
	   the inherited values were black at low alpha, which on a near-black
	   surface did nothing at all. */
	--surface-raised: rgba(231, 235, 243, 0.05);
	--surface-raised-hi: rgba(231, 235, 243, 0.10);

	/* --- Foreground ---------------------------------------------------- */

	--fg-1: var(--dune-1);
	--fg-2: var(--dune-2);
	/* Body copy. Held at ~5.5:1 on --surface-1 rather than dimmed to taste. */
	--fg-3: var(--dune-3);
	/* For a label sitting on an accent fill. Dark, because near-white reaches
	   only 3.2:1 on the pink end of the gradient and 2.7:1 on the purple. */
	--fg-on-accent: var(--basalt);

	/* --- Lines ---------------------------------------------------------
	 *
	 * Light at very low alpha rather than dark: on a near-black surface a
	 * darker line is invisible, which is how every inherited rgba(0,0,0,0.08)
	 * border read once the theme went dark.
	 */
	--line: rgba(231, 235, 243, 0.10);
	--line-strong: rgba(231, 235, 243, 0.18);

	/* --- Accent --------------------------------------------------------
	 *
	 * Canopy into iris — the two colours off the same wing. Used on the eyebrow
	 * labels and the primary button; not on everything, or it stops reading as
	 * an accent. Both stops clear AA on --surface-1 (4.75:1 and 5.51:1), which
	 * is the constraint that picked them: a gradient is only as legible as its
	 * worst stop, and text has to hold up at every point along it.
	 */
	--accent: var(--canopy);
	--accent-hi: var(--canopy-hi);
	--accent-gradient: linear-gradient(100deg, var(--canopy) 0%, var(--iris) 100%);
	--accent-gradient-hi: linear-gradient(100deg, var(--canopy-hi) 0%, var(--iris-hi) 100%);

	/* --- Status --------------------------------------------------------
	 *
	 * Three weights, each a colour and a 12%-alpha tint of it. Badges and
	 * message boxes are both built as "tint fill, 1px border in the colour,
	 * text in the colour", so a status reads the same whatever shape carries
	 * it. The tints are kept in sync by hand — CSS cannot derive them without
	 * color-mix, which is not safe to rely on yet.
	 */
	--positive: var(--marram);
	--positive-tint: rgba(147, 183, 154, 0.12);
	--attention: var(--amber);
	--attention-tint: rgba(255, 184, 77, 0.12);
	--alarm: var(--vermilion);
	--alarm-tint: rgba(255, 107, 71, 0.12);

	/* --- Spacing -------------------------------------------------------
	 *
	 * Every gap, pad and margin on the site comes from here. Nine steps is
	 * enough; if a layout wants something between two of them, it almost always
	 * wants one of them.
	 *
	 * --space-hair sits below the scale and is not part of it: it is the
	 * vertical padding of a chip, where a 4px step is already too much. Named
	 * rather than numbered because a `--space-0` holding 2px reads as zero.
	 */
	--space-hair: 2px;
	--space-1: 4px;
	--space-2: 8px;
	--space-3: 12px;
	--space-4: 16px;
	--space-5: 20px;
	--space-6: 24px;
	--space-7: 32px;
	--space-8: 40px;
	--space-9: 56px;

	/* --- Radii ---------------------------------------------------------
	 *
	 * The rule, so a fourth radius does not appear: sm is anything you click or
	 * type into, md is a box of text the interface is showing you, lg is a
	 * surface that contains other things, pill is a chip.
	 */
	--radius-sm: 5px;
	--radius-md: 8px;
	--radius-lg: 12px;
	--radius-pill: 999px;

	/* --- Type ----------------------------------------------------------
	 *
	 * rem, not em, so a size means the same thing wherever it lands. The one
	 * deliberate exception is an inline qualifier — a badge or a parenthetical
	 * inside a label — which sizes in `em` so it stays in proportion to the
	 * text it is qualifying. Those are marked at their rules in ui.css.
	 */
	--font-sans: sans-serif;
	--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

	--text-2xs: 0.75rem;
	--text-xs: 0.85rem;
	--text-sm: 0.9rem;
	--text-md: 0.95rem;
	--text-base: 1rem;
	--text-lg: 1.05rem;
	--text-h2: clamp(1.3rem, 3vw, 1.8rem);
	--text-h1: clamp(2rem, 5vw, 3rem);

	--weight-normal: 400;
	--weight-medium: 600;
	--weight-bold: 700;

	--leading-tight: 1.05;
	--leading-snug: 1.5;
	--leading-normal: 1.65;
	/* Only for the recovery key, which is meant to be transcribed by hand. */
	--leading-loose: 1.7;

	--tracking-tight: -0.025em;
	--tracking-snug: -0.02em;
	--tracking-wide: 0.06em;
	/* Uppercase micro-labels need far more air than lowercase text. */
	--tracking-caps: 0.14em;

	/* --- Layout --------------------------------------------------------
	 *
	 * One content column, shared by the page body and the nav bar. They were
	 * 860px and 900px respectively, so the brand did not line up with the page
	 * title beneath it.
	 */
	--content-width: 860px;

	/* --- Motion --------------------------------------------------------
	 *
	 * One duration. Everything that animates here is a control acknowledging a
	 * hover or a focus, and there is no reason for two of those to disagree.
	 * base.css turns all of it off under prefers-reduced-motion.
	 */
	--motion: 200ms;

	/* --- Focus ---------------------------------------------------------
	 *
	 * One visible focus treatment for the whole site, as a token rather than a
	 * rule, so a component that must draw its own ring draws the same one.
	 */
	--focus-ring: 2px solid var(--accent);
	--focus-offset: 2px;
}
