/*
 * KickItHome design tokens and component layer — the web surface.
 *
 * Three regions, in order:
 *
 *   1. Between the two GENERATED TOKENS markers: written by
 *      `cargo run -p design-tokens --bin generate` from
 *      `design/tokens.toml`, the one canonical design source, and
 *      never hand-edited. Holds every tier — the palette, the
 *      semantic colours in their light forms with the dark forms
 *      under `prefers-color-scheme: dark`, the type roles, weights,
 *      radii, spacing, sizes, and opacities — and the component
 *      contract as `--<treatment>-<part>` custom properties.
 *   2. Between the two GENERATED COMPONENTS markers: also written by
 *      the same command, from the same source, and never hand-edited.
 *      One CSS rule set per component-contract treatment
 *      (`.brand-primary-button { background-color:
 *      var(--primary-button-fill); ... }`), mapping each declared part
 *      onto a fixed property.
 *
 *   3. Below the second END marker: the HAND-WRITTEN layout, state,
 *      motion, and responsiveness layer — the only region a person
 *      edits. It consumes the custom properties and component rules
 *      the two generated regions declare (`var(--colour-accent)`,
 *      `var(--primary-button-fill)`, `.brand-card`) and never a
 *      literal colour, size, or radius of its own. Both applications
 *      are fully styled from these three regions; a page adds no CSS
 *      and no class of its own.
 *
 * `crates/design-tokens/tests/regeneration.rs` fails on any difference
 * between what the source renders and either generated region;
 * `crates/design-tokens/tests/cross_surface_audit.rs` fails on any
 * name declared in region 1 that the source does not carry, or named
 * by the source but missing here; `crates/design-tokens/tests/
 * web_layout_audit.rs` fails on a literal in region 3, so the rule
 * that region states about itself is enforced rather than trusted.
 *
 * Both Leptos applications link this one file through Trunk (`<link
 * data-trunk rel="css" href="../web-common/design.css" />` in each
 * `index.html`), so it serves the member app and the back office
 * alike.
 *
 * This product is white-label: a deployment's own club colours belong
 * in a runtime override of the SEMANTIC tier below (`--colour-accent`,
 * `--colour-accent-foreground`, `--colour-ring`), never a hand edit to
 * the generated palette — see `design/tokens.toml`'s own header and
 * `CONTRIBUTING.md`'s "Design tokens" section for why. That override
 * is NOT part of this file: the server serves it at `/branding.css`
 * from its `[branding]` configuration, and both applications link it
 * immediately after this sheet. Such an override must always supply
 * BOTH appearances: the dark forms below sit in a
 * `@media (prefers-color-scheme: dark)` rule at the same specificity
 * as `:root`, so a later `:root` override of a light form wins in
 * dark mode too and silently collapses the dark form to the light
 * one. Override a token in both places, or in neither — which is why
 * a half-supplied `[branding]` entry refuses startup rather than
 * being completed by derivation.
 */

/* GENERATED TOKENS BEGIN — do not hand-edit; regenerate with `cargo run -p design-tokens --bin generate` from `design/tokens.toml`. */
:root {
	/* ---- Palette (placeholder swatches, exact hex) ---- */
	--colour-white: #ffffff;
	--colour-grey-50: #f5f5f5;
	--colour-grey-100: #f2f2f2;
	--colour-grey-200: #e2e2e2;
	--colour-grey-400: #a3a3a3;
	--colour-grey-500: #6b6b6b;
	--colour-ink-700: #2e2f33;
	--colour-ink-800: #232427;
	--colour-ink-850: #17181a;
	--colour-ink-900: #111111;
	--colour-ink-950: #0b0b0c;
	--colour-blue-500: #3b82f6;
	--colour-blue-600: #2563eb;
	--colour-red-400: #f87171;
	--colour-red-600: #dc2626;
	--colour-green-400: #4ade80;
	/* Not the swatch a naive placeholder pick would land on
	   (`#16a34a`): white text on that one clears only 3.30:1 — under
	   the 4.5:1 text floor (SC 1.4.3). White on this one is 5.02:1. */
	--colour-green-700: #15803d;

	/* ---- Semantic tokens (light forms; the dark forms follow under
	   `prefers-color-scheme: dark`) ---- */
	/* The screen's own background, behind every card. */
	--colour-page: var(--colour-white);
	/* Body text and icons directly on `page`. */
	--colour-foreground: var(--colour-ink-900);
	/* A card, sheet, or field's fill — sits above `page`. */
	--colour-card: var(--colour-white);
	/* Text and icons on `card`. */
	--colour-card-foreground: var(--colour-ink-900);
	/* A nested well inside a card, or a disabled control's ground. */
	--colour-muted: var(--colour-grey-100);
	/* Secondary / de-emphasised text. */
	--colour-muted-foreground: var(--colour-grey-500);
	/* The club's own brand accent — primary actions, links, selected
	   state. A deployment overrides this token (and its foreground
	   pair below) with the club's colours; see this file's own header
	   for why the palette itself is never overridden. */
	--colour-accent: var(--colour-blue-600);
	/* Text and icons on `accent`. */
	--colour-accent-foreground: var(--colour-white);
	/* Destructive actions and error states. */
	--colour-destructive: var(--colour-red-600);
	/* Text and icons on `destructive`. */
	--colour-destructive-foreground: var(--colour-white);
	/* Hairlines: card edges, field outlines, dividers. */
	--colour-border: var(--colour-grey-200);
	/* Focus ring / focused-field outline. Declared with `accent`'s own
	   swatches, and overridable alongside it: a club's controls in its
	   colours behind a generic blue ring is a split this file does not
	   make. */
	--colour-ring: var(--colour-blue-600);
	/* Positive / confirmation states — order confirmed, points
	   awarded. The light form is the one value in this file that is
	   NOT the naive placeholder pick — see `green-700`'s own note. */
	--colour-success: var(--colour-green-700);
	/* Text and icons on `success`. */
	--colour-success-foreground: var(--colour-white);
	/* The wash a modal draws over the page behind it. The one
	   translucent entry in this file, and the one written as a literal
	   CSS colour function rather than a palette reference: a scrim is
	   a dimming of whatever it covers, so it is the same value in both
	   appearances and belongs to neither `page` nor `card`. The
	   contrast gate never measures it — no treatment names it, and the
	   gate refuses a translucent colour rather than measuring it as if
	   it were opaque. */
	--colour-scrim: rgb(17 17 17 / 45%);

	/* ---- Typography ---- */
	/* The browser's own `system-ui` face. Type SIZES below are ROLES
	   with a reference size in CSS pixels — a future native shell maps
	   each role onto its own type scale (iOS's Dynamic Type styles,
	   Material's type scale), which is platform idiom and deliberately
	   not forced to match: the rendered hierarchy must agree, the code
	   need not. */
	--font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	/* Control labels and an empty state's title. */
	--font-size-headline: 17px;
	/* Row titles, toggle labels, a field's own text. */
	--font-size-body: 17px;
	/* Row subtitles, state messages, text links, banners, a page's own
	   subtitle. */
	--font-size-subheadline: 15px;
	/* Small print: a toggle's subtitle, a badge. */
	--font-size-caption: 12px;
	/* A screen's own name: the heading a page opens with. */
	--font-size-title: 24px;

	/* ---- Weights ---- */
	/* Plain text: subtitles, messages, links, a field's own text. */
	--weight-regular: 400;
	/* A row's or toggle's title, a badge. */
	--weight-medium: 500;
	/* Control labels, a card's heading, a page's own name. */
	--weight-semibold: 600;

	/* ---- Radii ---- */
	--radius-field: 14px;
	--radius-panel: 20px;
	/* "Full": the capsule, whatever the control's width. Any radius
	   over half the control's height renders as the capsule, so this
	   is spelt as a number a future native shell can clamp its own
	   way (SwiftUI's corner radius; Compose's `RoundedCornerShape
	   (percent = 50)`, the same shape by its own spelling). */
	--radius-pill: 999px;

	/* ---- Spacing ---- */
	/* The rungs the components actually use today, in order; a
	   placeholder scale, not a grid. Rung 4 (10) exists for the
	   badge's horizontal inset alone. */
	--space-1: 2px;
	--space-2: 4px;
	--space-3: 8px;
	--space-4: 10px;
	--space-5: 12px;
	--space-6: 16px;
	--space-7: 24px;
	--space-8: 32px;

	/* ---- Sizes ---- */
	/* Fixed dimensions that are neither spacing nor type: the tier the
	   component contract below — and the stylesheet's own hand-written
	   layout layer — names for a control's height, a switch's track
	   and knob, a spinner's square and stroke, and a page's width cap,
	   so no surface carries the raw number. */
	/* Every button's minimum height. */
	--size-button-height: 52px;
	/* A field's height. */
	--size-field-height: 56px;
	/* The widest a page's content stretches on a regular-width
	   window — keyed on the width the page is given, never on the
	   device class. */
	--size-page-max-content-width: 600px;
	/* The widest a table-bearing page stretches — the `wide-page`
	   treatment's own cap, wider than `page-max-content-width`
	   because an admin bookings or orders table on a laptop needs the
	   room a phone browser never has. */
	--size-page-max-wide-width: 1200px;
	/* The square a loading indicator's spinner is drawn in. */
	--size-icon: 24px;
	/* The stroke a loading indicator's spinner ring is drawn with. */
	--size-spinner-stroke: 3px;
	/* The switch track's width. The knob's travel is derived from it
	   rather than declared — track width less the knob less the two
	   insets — so the three can never disagree. */
	--size-toggle-track-width: 44px;
	/* The switch track's height. */
	--size-toggle-track-height: 26px;
	/* The square of the switch's knob — the part that shows the
	   state, and the reason `toggle`'s `text` is held to SC 1.4.11's
	   3:1 against the track it sits on. */
	--size-toggle-knob: 20px;

	/* ---- Opacities ---- */
	/* How far a disabled control is dimmed. A filled button dims its
	   FILL alone and keeps its label; the secondary button dims as a
	   whole, because a faded fill under a full-strength border reads
	   as enabled. */
	--opacity-disabled: 0.4;
	/* How far a button dims while pressed. */
	--opacity-pressed: 0.85;
	/* How much of its own colour a loading indicator's spinner ring
	   keeps outside the travelling arc — the unfilled part of the
	   ring, so the arc reads as motion rather than as the whole ring
	   flickering. */
	--opacity-spinner-track: 0.25;

	/* ---- Component contract ---- */

	/* The primary action — buy tickets, confirm an order: a
	   full-width capsule filled with the accent, its label in the
	   accent's foreground. Disabled, the fill dims to
	   `--opacity-disabled` and the label stays; pressed, the whole
	   control dims to `--opacity-pressed`. */
	--primary-button-fill: var(--colour-accent);
	--primary-button-text: var(--colour-accent-foreground);
	--primary-button-radius: var(--radius-pill);
	--primary-button-height: var(--size-button-height);
	--primary-button-label-size: var(--font-size-headline);
	--primary-button-label-weight: var(--weight-semibold);

	/* The secondary action: the same capsule on the card fill with a
	   hairline border, its label in the card's foreground. Disabled,
	   the WHOLE control dims (fill, border, and label together). */
	--secondary-button-fill: var(--colour-card);
	--secondary-button-text: var(--colour-card-foreground);
	--secondary-button-border: var(--colour-border);
	--secondary-button-border-width: 1px;
	--secondary-button-radius: var(--radius-pill);
	--secondary-button-height: var(--size-button-height);
	--secondary-button-label-size: var(--font-size-headline);
	--secondary-button-label-weight: var(--weight-semibold);

	/* A destructive action — cancel an order, delete an account: the
	   same capsule filled with the destructive colour. Its own
	   treatment rather than a flag on the primary one, so a safe
	   default can never sit on a destructive action. Disabled and
	   pressed as the primary button. */
	--destructive-button-fill: var(--colour-destructive);
	--destructive-button-text: var(--colour-destructive-foreground);
	--destructive-button-radius: var(--radius-pill);
	--destructive-button-height: var(--size-button-height);
	--destructive-button-label-size: var(--font-size-headline);
	--destructive-button-label-weight: var(--weight-semibold);

	/* A single-line text field on the card fill: a visible label in
	   the muted foreground above the control, the entered text in the
	   card's foreground, a hairline border that becomes the 2px ring
	   while focused. No leading icon: this product has no icon set,
	   and a slot nothing can fill is a promise, not a feature. */
	--field-fill: var(--colour-card);
	--field-text: var(--colour-card-foreground);
	--field-border: var(--colour-border);
	--field-border-width: 1px;
	--field-focus: var(--colour-ring);
	--field-focus-width: 2px;
	--field-radius: var(--radius-field);
	--field-height: var(--size-field-height);
	--field-label-size: var(--font-size-body);
	--field-label-weight: var(--weight-regular);
	--field-label-colour: var(--colour-muted-foreground);
	--field-padding-horizontal: var(--space-6);

	/* The content panel every form and list section sits on: the card
	   fill with a hairline border and the panel radius, its children
	   stacked on the gap. The optional `title` is the panel's own
	   heading. */
	--card-fill: var(--colour-card);
	--card-text: var(--colour-card-foreground);
	--card-border: var(--colour-border);
	--card-border-width: 1px;
	--card-radius: var(--radius-panel);
	--card-title-size: var(--font-size-headline);
	--card-title-weight: var(--weight-semibold);
	--card-title-colour: var(--colour-card-foreground);
	--card-padding-horizontal: var(--space-6);
	--card-padding-vertical: var(--space-6);
	--card-gap: var(--space-5);

	/* One row of a list, on the card fill around it: a medium-weight
	   title in the card's foreground over an optional subtitle in the
	   muted foreground, with a trailing slot — a fixture row, an
	   order line, a rewards entry. */
	--row-text: var(--colour-card-foreground);
	--row-label-size: var(--font-size-body);
	--row-label-weight: var(--weight-medium);
	--row-label-colour: var(--colour-card-foreground);
	--row-message-size: var(--font-size-subheadline);
	--row-message-weight: var(--weight-regular);
	--row-message-colour: var(--colour-muted-foreground);
	--row-padding-horizontal: var(--space-6);
	--row-padding-vertical: var(--space-5);
	--row-gap: var(--space-1);

	/* The page frame every screen sits in: the page fill, the
	   foreground for text, and a CAP on the content width on a
	   regular-width window. */
	--page-fill: var(--colour-page);
	--page-text: var(--colour-foreground);
	--page-width: var(--size-page-max-content-width);

	/* The second page frame — for a table-bearing screen: the page
	   fill, the foreground for text, and a WIDER cap than `page`. A
	   second treatment rather than a flag on `page`, for the
	   contract's own reason: two treatments make the wrong page
	   width unrepresentable where a flag would make one of them a
	   default nobody chose. */
	--wide-page-fill: var(--colour-page);
	--wide-page-text: var(--colour-foreground);
	--wide-page-width: var(--size-page-max-wide-width);

	/* A data table — the back office's orders or members list: the
	   card fill with a hairline border and the panel radius. It
	   carries no width of its own: it stretches to the page frame
	   around it, which is `wide-page` and already caps there — a
	   second cap saying the same thing is a second thing to keep in
	   step. `label` carries the header cells' style; `message`
	   carries the body cells' — the same split `row` makes between a
	   title and a subtitle. */
	--table-fill: var(--colour-card);
	--table-text: var(--colour-card-foreground);
	--table-border: var(--colour-border);
	--table-border-width: 1px;
	--table-radius: var(--radius-panel);
	--table-label-size: var(--font-size-caption);
	--table-label-weight: var(--weight-semibold);
	--table-label-colour: var(--colour-muted-foreground);
	--table-message-size: var(--font-size-subheadline);
	--table-message-weight: var(--weight-regular);
	--table-message-colour: var(--colour-card-foreground);
	--table-padding-horizontal: var(--space-6);
	--table-padding-vertical: var(--space-5);

	/* A named empty state, never a blank screen — no fixtures yet, no
	   orders yet: a semibold title in the foreground over a muted
	   message, centred in the space it is given. No icon, for
	   `field`'s reason. */
	--empty-state-text: var(--colour-muted-foreground);
	--empty-state-title-size: var(--font-size-headline);
	--empty-state-title-weight: var(--weight-semibold);
	--empty-state-title-colour: var(--colour-foreground);
	--empty-state-message-size: var(--font-size-subheadline);
	--empty-state-message-weight: var(--weight-regular);
	--empty-state-message-colour: var(--colour-muted-foreground);
	--empty-state-padding-horizontal: var(--space-8);
	--empty-state-padding-vertical: var(--space-8);
	--empty-state-gap: var(--space-5);

	/* A loading indicator tinted with the accent rather than the
	   browser's default. */
	--loading-text: var(--colour-accent);

	/* An inline text action — "Forgot your password?": subheadline
	   text in the accent, no fill, no frame. Its own treatment so a
	   screen never reaches for the browser's own link styling and
	   inherits the system tint. */
	--text-link-text: var(--colour-accent);
	--text-link-label-size: var(--font-size-subheadline);
	--text-link-label-weight: var(--weight-regular);

	/* A labelled on/off switch — a marketing-consent preference, a
	   notification setting. `fill`, `text`, and `border` are the OFF
	   track, knob, and outline; `selected` is the ON track and knob;
	   the label sits beside the switch on the card fill, over an
	   optional subtitle. `text` really is the KNOB: the stylesheet
	   sets it as the track's `color` and paints the knob with
	   `currentColor`, so the colour this contract holds to SC
	   1.4.11's 3:1 against the track is the colour the knob is
	   actually drawn in — which is what `field`'s and `toggle`'s
	   contrast exemptions rest on. */
	--toggle-fill: var(--colour-muted);
	--toggle-text: var(--colour-muted-foreground);
	--toggle-selected-fill: var(--colour-accent);
	--toggle-selected-text: var(--colour-accent-foreground);
	--toggle-border: var(--colour-border);
	--toggle-border-width: 2px;
	--toggle-height: var(--size-toggle-track-height);
	--toggle-width: var(--size-toggle-track-width);
	--toggle-label-size: var(--font-size-body);
	--toggle-label-weight: var(--weight-medium);
	--toggle-label-colour: var(--colour-card-foreground);
	--toggle-message-size: var(--font-size-caption);
	--toggle-message-weight: var(--weight-regular);
	--toggle-message-colour: var(--colour-muted-foreground);

	/* An inline, non-blocking notice — an offline indicator, an
	   integration-degraded warning: a line of text in the muted
	   foreground on the muted fill. The two TONED notices are
	   treatments of their own (`error-banner`, `success-banner`)
	   rather than variants of this one, for `destructive-button`'s
	   reason: a tone a page has to remember to ask for is a tone a
	   page can forget, and an unnamed variant is a pair the contrast
	   gate never sees. */
	--banner-fill: var(--colour-muted);
	--banner-text: var(--colour-muted-foreground);
	--banner-radius: var(--radius-field);
	--banner-label-size: var(--font-size-subheadline);
	--banner-label-weight: var(--weight-regular);
	--banner-padding-horizontal: var(--space-6);
	--banner-padding-vertical: var(--space-5);
	--banner-gap: var(--space-3);

	/* The same inline notice in the error tone — a failed save, a
	   refused sign-in: the destructive fill with its own foreground.
	   Filled rather than washed: a wash is an opacity over whatever
	   happens to be behind it, which is a colour no token names and
	   therefore a pair this file's contrast gate cannot measure, and
	   the one pair once invented outside this contract is the one
	   that turned out to fail (4.01:1 against a 4.5:1 floor). */
	--error-banner-fill: var(--colour-destructive);
	--error-banner-text: var(--colour-destructive-foreground);
	--error-banner-radius: var(--radius-field);
	--error-banner-label-size: var(--font-size-subheadline);
	--error-banner-label-weight: var(--weight-regular);
	--error-banner-padding-horizontal: var(--space-6);
	--error-banner-padding-vertical: var(--space-5);
	--error-banner-gap: var(--space-3);

	/* The same inline notice in the positive tone — a saved profile,
	   a redeemed reward, a recorded refund: the success fill with its
	   own foreground. `error-banner`'s reasoning, the other way up —
	   and the consumer that makes `success`/`success-foreground` a
	   pair this product actually paints rather than one it only
	   declares. */
	--success-banner-fill: var(--colour-success);
	--success-banner-text: var(--colour-success-foreground);
	--success-banner-radius: var(--radius-field);
	--success-banner-label-size: var(--font-size-subheadline);
	--success-banner-label-weight: var(--weight-regular);
	--success-banner-padding-horizontal: var(--space-6);
	--success-banner-padding-vertical: var(--space-5);
	--success-banner-gap: var(--space-3);

	/* A small capsule label — an order's status, a reward's tier:
	   caption text in the muted foreground on the muted fill. One
	   tone only: every call site labels a value (a role, a kind, a
	   position, a percentage), not a verdict, so a status tone would
	   be a colour nothing here knows how to choose. */
	--badge-fill: var(--colour-muted);
	--badge-text: var(--colour-muted-foreground);
	--badge-radius: var(--radius-pill);
	--badge-label-size: var(--font-size-caption);
	--badge-label-weight: var(--weight-medium);
	--badge-padding-horizontal: var(--space-4);
	--badge-padding-vertical: var(--space-2);

	/* A hairline row separator: a border with nothing inside it. */
	--divider-border: var(--colour-border);
	--divider-border-width: 1px;

	/* The bottom or top tab bar: on the card fill, the selected tab
	   in the accent and the rest in the muted foreground; the
	   selected fill equals the bar's, which hides any default
	   indicator pill a framework would otherwise draw. */
	--tab-bar-fill: var(--colour-card);
	--tab-bar-text: var(--colour-muted-foreground);
	--tab-bar-selected-fill: var(--colour-card);
	--tab-bar-selected-text: var(--colour-accent);

	/* The compact bar above a screen's own name: a back control and a
	   body-sized caption in the foreground. Body-sized deliberately
	   — this is a bar's caption, never the page's own heading, which
	   is `page-header`'s own `title`. */
	--screen-header-text: var(--colour-foreground);
	--screen-header-label-size: var(--font-size-body);
	--screen-header-label-weight: var(--weight-regular);
	--screen-header-label-colour: var(--colour-foreground);

	/* The heading block a page opens with: the page's own name at the
	   `title` role over an optional muted subtitle, under an optional
	   `screen-header` bar. Every one of this product's screens renders
	   one, which is why it is a treatment: a heading block outside the
	   contract is a type ramp and a gap nothing gates. */
	--page-header-title-size: var(--font-size-title);
	--page-header-title-weight: var(--weight-semibold);
	--page-header-title-colour: var(--colour-foreground);
	--page-header-message-size: var(--font-size-subheadline);
	--page-header-message-weight: var(--weight-regular);
	--page-header-message-colour: var(--colour-muted-foreground);
	--page-header-gap: var(--space-2);

	/* A destructive-action confirmation — leave a group, cancel an
	   order: the card fill, the title in the card's foreground, the
	   message muted, its buttons the destructive button and the text
	   link. */
	--confirm-dialog-fill: var(--colour-card);
	--confirm-dialog-text: var(--colour-card-foreground);
	--confirm-dialog-message-size: var(--font-size-subheadline);
	--confirm-dialog-message-weight: var(--weight-regular);
	--confirm-dialog-message-colour: var(--colour-muted-foreground);

	/* A pull-to-refresh list of rows on the card fill. */
	--list-fill: var(--colour-card);

	/* The vertical gap between standalone text actions rendered
	   outside a list — two links stacked below a form's card, say.
	   Nothing about a link's colour or type changes here; only the
	   space around it does, and space is exactly what a treatment
	   with a `gap` and nothing else declares. */
	--link-row-gap: var(--space-3);

	/* A row of sibling actions sharing the width equally — a list's
	   previous/next pager, a form's save-and-cancel pair. Its own
	   treatment because every button treatment is full-width by
	   design: two of them dropped side by side with no row around
	   them render as one undivided slab, which is what happened on
	   sixteen screens before this entry existed. `equal_width` is the
	   rule a native surface reads; the web surface spells the same
	   rule as its own flex basis. */
	--button-row-gap: var(--space-5);
}

@media (prefers-color-scheme: dark) {
	:root {
		--colour-page: var(--colour-ink-950);
		--colour-foreground: var(--colour-grey-50);
		--colour-card: var(--colour-ink-850);
		--colour-card-foreground: var(--colour-grey-50);
		--colour-muted: var(--colour-ink-800);
		--colour-muted-foreground: var(--colour-grey-400);
		--colour-accent: var(--colour-blue-500);
		--colour-accent-foreground: var(--colour-ink-950);
		--colour-destructive: var(--colour-red-400);
		--colour-destructive-foreground: var(--colour-ink-950);
		--colour-border: var(--colour-ink-700);
		--colour-ring: var(--colour-blue-500);
		--colour-success: var(--colour-green-400);
		--colour-success-foreground: var(--colour-ink-950);
	}
}
/* GENERATED TOKENS END */

/* GENERATED COMPONENTS BEGIN — do not hand-edit; regenerate with `cargo run -p design-tokens --bin generate` from `design/tokens.toml`. */
.brand-primary-button {
	background-color: var(--primary-button-fill);
	color: var(--primary-button-text);
	border-radius: var(--primary-button-radius);
	min-block-size: var(--primary-button-height);
}

.brand-primary-button__label {
	font-size: var(--primary-button-label-size);
	font-weight: var(--primary-button-label-weight);
}

.brand-secondary-button {
	background-color: var(--secondary-button-fill);
	color: var(--secondary-button-text);
	border: var(--secondary-button-border-width) solid var(--secondary-button-border);
	border-radius: var(--secondary-button-radius);
	min-block-size: var(--secondary-button-height);
}

.brand-secondary-button__label {
	font-size: var(--secondary-button-label-size);
	font-weight: var(--secondary-button-label-weight);
}

.brand-destructive-button {
	background-color: var(--destructive-button-fill);
	color: var(--destructive-button-text);
	border-radius: var(--destructive-button-radius);
	min-block-size: var(--destructive-button-height);
}

.brand-destructive-button__label {
	font-size: var(--destructive-button-label-size);
	font-weight: var(--destructive-button-label-weight);
}

.brand-field {
	background-color: var(--field-fill);
	color: var(--field-text);
	border: var(--field-border-width) solid var(--field-border);
	border-radius: var(--field-radius);
	min-block-size: var(--field-height);
	padding-inline: var(--field-padding-horizontal);
}

.brand-field:focus-within, .brand-field:focus-visible {
	outline: var(--field-focus-width) solid var(--field-focus);
	outline-offset: 0;
}

.brand-field__label {
	font-size: var(--field-label-size);
	font-weight: var(--field-label-weight);
	color: var(--field-label-colour);
}

.brand-card {
	background-color: var(--card-fill);
	color: var(--card-text);
	border: var(--card-border-width) solid var(--card-border);
	border-radius: var(--card-radius);
	padding-block: var(--card-padding-vertical);
	padding-inline: var(--card-padding-horizontal);
	gap: var(--card-gap);
}

.brand-card__title {
	font-size: var(--card-title-size);
	font-weight: var(--card-title-weight);
	color: var(--card-title-colour);
}

.brand-row {
	color: var(--row-text);
	padding-block: var(--row-padding-vertical);
	padding-inline: var(--row-padding-horizontal);
	gap: var(--row-gap);
}

.brand-row__label {
	font-size: var(--row-label-size);
	font-weight: var(--row-label-weight);
	color: var(--row-label-colour);
}

.brand-row__message {
	font-size: var(--row-message-size);
	font-weight: var(--row-message-weight);
	color: var(--row-message-colour);
}

.brand-page {
	background-color: var(--page-fill);
	color: var(--page-text);
}

.brand-wide-page {
	background-color: var(--wide-page-fill);
	color: var(--wide-page-text);
}

.brand-table {
	background-color: var(--table-fill);
	color: var(--table-text);
	border: var(--table-border-width) solid var(--table-border);
	border-radius: var(--table-radius);
	padding-block: var(--table-padding-vertical);
	padding-inline: var(--table-padding-horizontal);
}

.brand-table__label {
	font-size: var(--table-label-size);
	font-weight: var(--table-label-weight);
	color: var(--table-label-colour);
}

.brand-table__message {
	font-size: var(--table-message-size);
	font-weight: var(--table-message-weight);
	color: var(--table-message-colour);
}

.brand-empty-state {
	color: var(--empty-state-text);
	padding-block: var(--empty-state-padding-vertical);
	padding-inline: var(--empty-state-padding-horizontal);
	gap: var(--empty-state-gap);
}

.brand-empty-state__title {
	font-size: var(--empty-state-title-size);
	font-weight: var(--empty-state-title-weight);
	color: var(--empty-state-title-colour);
}

.brand-empty-state__message {
	font-size: var(--empty-state-message-size);
	font-weight: var(--empty-state-message-weight);
	color: var(--empty-state-message-colour);
}

.brand-loading {
	color: var(--loading-text);
}

.brand-text-link {
	color: var(--text-link-text);
}

.brand-text-link__label {
	font-size: var(--text-link-label-size);
	font-weight: var(--text-link-label-weight);
}

.brand-toggle__track {
	background-color: var(--toggle-fill);
	color: var(--toggle-text);
	border: var(--toggle-border-width) solid var(--toggle-border);
	min-block-size: var(--toggle-height);
}

.brand-toggle--selected {
	background-color: var(--toggle-selected-fill);
	color: var(--toggle-selected-text);
}

.brand-toggle__label {
	font-size: var(--toggle-label-size);
	font-weight: var(--toggle-label-weight);
	color: var(--toggle-label-colour);
}

.brand-toggle__message {
	font-size: var(--toggle-message-size);
	font-weight: var(--toggle-message-weight);
	color: var(--toggle-message-colour);
}

.brand-banner {
	background-color: var(--banner-fill);
	color: var(--banner-text);
	border-radius: var(--banner-radius);
	padding-block: var(--banner-padding-vertical);
	padding-inline: var(--banner-padding-horizontal);
	gap: var(--banner-gap);
}

.brand-banner__label {
	font-size: var(--banner-label-size);
	font-weight: var(--banner-label-weight);
}

.brand-error-banner {
	background-color: var(--error-banner-fill);
	color: var(--error-banner-text);
	border-radius: var(--error-banner-radius);
	padding-block: var(--error-banner-padding-vertical);
	padding-inline: var(--error-banner-padding-horizontal);
	gap: var(--error-banner-gap);
}

.brand-error-banner__label {
	font-size: var(--error-banner-label-size);
	font-weight: var(--error-banner-label-weight);
}

.brand-success-banner {
	background-color: var(--success-banner-fill);
	color: var(--success-banner-text);
	border-radius: var(--success-banner-radius);
	padding-block: var(--success-banner-padding-vertical);
	padding-inline: var(--success-banner-padding-horizontal);
	gap: var(--success-banner-gap);
}

.brand-success-banner__label {
	font-size: var(--success-banner-label-size);
	font-weight: var(--success-banner-label-weight);
}

.brand-badge {
	background-color: var(--badge-fill);
	color: var(--badge-text);
	border-radius: var(--badge-radius);
	padding-block: var(--badge-padding-vertical);
	padding-inline: var(--badge-padding-horizontal);
}

.brand-badge__label {
	font-size: var(--badge-label-size);
	font-weight: var(--badge-label-weight);
}

.brand-divider {
	border: var(--divider-border-width) solid var(--divider-border);
}

.brand-tab-bar {
	background-color: var(--tab-bar-fill);
	color: var(--tab-bar-text);
}

.brand-tab-bar--selected {
	background-color: var(--tab-bar-selected-fill);
	color: var(--tab-bar-selected-text);
}

.brand-screen-header {
	color: var(--screen-header-text);
}

.brand-screen-header__label {
	font-size: var(--screen-header-label-size);
	font-weight: var(--screen-header-label-weight);
	color: var(--screen-header-label-colour);
}

.brand-page-header__title {
	font-size: var(--page-header-title-size);
	font-weight: var(--page-header-title-weight);
	color: var(--page-header-title-colour);
}

.brand-page-header__message {
	font-size: var(--page-header-message-size);
	font-weight: var(--page-header-message-weight);
	color: var(--page-header-message-colour);
}

.brand-page-header {
	gap: var(--page-header-gap);
}

.brand-confirm-dialog {
	background-color: var(--confirm-dialog-fill);
	color: var(--confirm-dialog-text);
}

.brand-confirm-dialog__message {
	font-size: var(--confirm-dialog-message-size);
	font-weight: var(--confirm-dialog-message-weight);
	color: var(--confirm-dialog-message-colour);
}

.brand-list {
	background-color: var(--list-fill);
}

.brand-link-row {
	gap: var(--link-row-gap);
}

.brand-button-row {
	gap: var(--button-row-gap);
}

/* GENERATED COMPONENTS END */

/*
 * Hand-written layout layer.
 *
 * The two generated regions above carry colour, type, and spacing-as-
 * padding-or-gap only — no display, position, or responsiveness (see
 * this file's own header). Everything below is the layer that header
 * says belongs here: both web applications need one. Every rule below
 * styles a class or bare element a component in
 * `crates/web-common/src/components/` already emits, and consumes only
 * the custom properties and `.brand-*` rules declared above — never a
 * literal colour, size, or radius of its own. A page never adds a
 * class of its own; the layout a page needs is a gap here, not there.
 *
 * That "never a literal" rule is not left to review:
 * `crates/design-tokens/tests/web_layout_audit.rs` reads this region
 * and fails on any hex, `rgb()`/`hsl()`, or `px`/`rem`/`em` length it
 * finds outside its own enumerated waiver list — which today holds
 * exactly one entry, the visually-hidden `.brand-toggle__control`
 * block, whose `1px`/`-1px` are the standard clip idiom rather than a
 * design value. The waiver list is self-policing: an entry matching
 * nothing here fails the same test.
 */

*, *::before, *::after {
	box-sizing: border-box;
}

html, body {
	margin: 0;
	min-height: 100%;
}

body {
	font-family: var(--font-body);
	font-size: var(--font-size-body);
	background-color: var(--colour-page);
	color: var(--colour-foreground);
	-webkit-font-smoothing: antialiased;
}

button, input, select, textarea {
	font: inherit;
	color: inherit;
}

h1, h2, p, dl, dt, dd {
	margin: 0;
}

/* ---- Page frames ---- */

.brand-page, .brand-wide-page {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-height: 100vh;
}

/*
 * The `<main>` every page frame renders. The gap is what gives a
 * signed-in screen its vertical rhythm: the page's own blocks are this
 * element's direct children, so anything interposed between it and
 * them (a second `<main>`, a wrapper `<div>`) collapses that rhythm to
 * nothing — which is exactly what happened before the frame owned the
 * landmark.
 */
.brand-page__content, .brand-wide-page__content {
	width: 100%;
	display: flex;
	flex-direction: column;
	gap: var(--space-6);
	padding: var(--space-6);
}

.brand-page__content {
	max-width: var(--page-width);
}

.brand-wide-page__content {
	max-width: var(--wide-page-width);
}

/* ---- Screen header / masthead / page heading ---- */

.brand-page-header {
	display: flex;
	flex-direction: column;
}

.brand-screen-header {
	display: flex;
	align-items: center;
	gap: var(--space-5);
	padding: var(--space-5) var(--space-6);
	border-bottom: var(--divider-border-width) solid var(--divider-border);
}

.brand-screen-header__back {
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	display: flex;
	align-items: center;
}

/* ---- Cards ---- */

.brand-card {
	display: flex;
	flex-direction: column;
}

/* ---- Lists and rows ---- */

.brand-list {
	list-style: none;
	margin: 0;
	padding: 0;
	border: var(--divider-border-width) solid var(--divider-border);
	border-radius: var(--radius-panel);
	overflow: hidden;
}

.brand-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-5);
}

.brand-row > div:first-child {
	display: flex;
	flex-direction: column;
	gap: var(--row-gap);
	min-width: 0;
}

.brand-row > div:last-child:not(:only-child) {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	flex-shrink: 0;
}

.brand-divider {
	border: none;
	border-top: var(--divider-border-width) solid var(--divider-border);
	margin: 0;
	height: 0;
}

/* ---- Forms and fields ---- */

form {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}

label:has(> .brand-field) {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	width: 100%;
}

.brand-field {
	width: 100%;
}

textarea.brand-field {
	padding-block: var(--space-4);
	resize: vertical;
}

/* ---- Buttons ---- */

.brand-primary-button, .brand-secondary-button, .brand-destructive-button {
	width: 100%;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	padding-inline: var(--space-7);
}

.brand-primary-button, .brand-destructive-button {
	border: none;
}

.brand-primary-button:disabled, .brand-destructive-button:disabled,
.brand-secondary-button:disabled {
	opacity: var(--opacity-disabled);
	cursor: not-allowed;
}

.brand-primary-button:active:not(:disabled),
.brand-secondary-button:active:not(:disabled),
.brand-destructive-button:active:not(:disabled) {
	opacity: var(--opacity-pressed);
}

/*
 * The web spelling of the contract's `equal_width` rule: every child
 * takes an equal share of the row whatever its label's length, which
 * is what stops a pager's two full-width buttons rendering as one
 * undivided slab.
 */
.brand-button-row {
	display: flex;
	align-items: center;
}

.brand-button-row > * {
	flex: 1 1 0;
	min-width: 0;
}

/* ---- Text links ---- */

.brand-text-link {
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	text-decoration: none;
	width: fit-content;
}

.brand-text-link:hover {
	text-decoration: underline;
}

.brand-link-row {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

/* ---- Toggles ---- */

label:has(> .brand-toggle__control) {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	cursor: pointer;
	width: fit-content;
}

/*
 * The visually-hidden checkbox that actually holds the state. The
 * `1px`/`-1px`/`rect(0, 0, 0, 0)` here are the standard clip idiom for
 * hiding a control from sight while leaving it in the accessibility
 * tree, not design values — the one waiver
 * `crates/design-tokens/tests/web_layout_audit.rs` carries.
 */
.brand-toggle__control {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.brand-toggle__track {
	position: relative;
	display: inline-flex;
	align-items: center;
	inline-size: var(--toggle-width);
	flex-shrink: 0;
	border-radius: var(--radius-pill);
	padding: var(--space-1);
}

/*
 * The knob takes the track's own `currentColor` — which the generated
 * region sets to `--toggle-text` on the track, and to
 * `--toggle-selected-text` on the selected track. That is what makes
 * the contract's claim true: the colour `contrast.rs` holds to SC
 * 1.4.11's 3:1 against the track is the colour the knob is drawn in,
 * in both states. Painting it from any other token (a card fill, say)
 * leaves an off knob at 1.12:1 while the gate reports 4.76:1 for a
 * pair nothing renders.
 */
.brand-toggle__knob {
	inline-size: var(--size-toggle-knob);
	block-size: var(--size-toggle-knob);
	border-radius: 50%;
	background-color: currentColor;
	transform: translateX(0);
	transition: transform 0.15s ease;
}

/*
 * The travel is derived, never declared: the track's width less the
 * knob less the two insets, so the three can never disagree.
 */
.brand-toggle--selected .brand-toggle__knob {
	transform: translateX(calc(var(--toggle-width) - var(--size-toggle-knob) - 2 * var(--space-1)));
}

label:has(> .brand-toggle__control) > span:last-child {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

/* ---- Banners ---- */

.brand-banner, .brand-error-banner, .brand-success-banner {
	display: flex;
	align-items: center;
}

/* ---- Badges ---- */

.brand-badge {
	display: inline-flex;
	align-items: center;
	width: fit-content;
}

/* ---- Empty state and loading ---- */

.brand-empty-state {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
}

.brand-loading {
	display: flex;
	align-items: center;
	gap: var(--space-3);
}

.brand-loading__spinner {
	inline-size: var(--size-icon);
	block-size: var(--size-icon);
	flex-shrink: 0;
	border-radius: 50%;
	border: var(--size-spinner-stroke) solid
		color-mix(in srgb, currentColor calc(var(--opacity-spinner-track) * 100%), transparent);
	border-top-color: currentColor;
	animation: brand-loading-spin 0.7s linear infinite;
}

@keyframes brand-loading-spin {
	to {
		transform: rotate(360deg);
	}
}

/* ---- Tab bar ---- */

.brand-tab-bar {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	padding: var(--space-4) var(--space-6);
	border-bottom: var(--divider-border-width) solid var(--divider-border);
}

.brand-tab-bar__tab {
	background: none;
	border: none;
	cursor: pointer;
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-pill);
	font-weight: var(--weight-medium);
}

/* ---- Confirm dialog ---- */

.brand-confirm-dialog__backdrop {
	position: fixed;
	inset: 0;
	background-color: var(--colour-scrim);
	z-index: 40;
}

.brand-confirm-dialog {
	position: fixed;
	inset-inline: var(--space-6);
	top: 50%;
	transform: translateY(-50%);
	margin-inline: auto;
	max-width: calc(var(--size-page-max-content-width) - (2 * var(--space-6)));
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	padding: var(--card-padding-vertical) var(--card-padding-horizontal);
	border-radius: var(--radius-panel);
	z-index: 41;
}

.brand-confirm-dialog__actions {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--space-5);
}

/* ---- Tables ---- */

.brand-table__scroll {
	overflow-x: auto;
}

.brand-table {
	inline-size: 100%;
	border-collapse: separate;
	border-spacing: 0;
	overflow: hidden;
}

.brand-table__label, .brand-table__message {
	text-align: start;
	padding-block: var(--table-padding-vertical);
	padding-inline: var(--table-padding-horizontal);
	border-bottom: var(--divider-border-width) solid var(--divider-border);
}
