/* Goald admin panel — the design tokens and the rules that read them.
   The tokens themselves are no longer hand-rolled here: they are
   generated from `design/tokens.toml`, the one source the mobile
   shells and the web app read too, into the marked block below — so
   the admin's surfaces, ink and borders are now the app's own values
   rather than the violet-tinted variants it carried alone. The admin
   stays a desktop information-dense tool (light-surface tables and
   dashboard cards, dark-violet sidebar chrome); this is a shared
   palette, not a mobile-pattern rebuild. No CSS framework dependency
   — keeps the wasm bundle and build graph small. */

/* Bai Jamjuree, self-hosted (SIL OFL 1.1 — /OFL.txt). The font files
   sit at the dist root: see index.html for why they must not live
   under a subdirectory. Weights 400/500/600/700 — the family tops out
   at 700; the mobile app's heavier strokes are synthesised. */

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("/bai-jamjuree-latin-400.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
		U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("/bai-jamjuree-latin-ext-400.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
		U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("/bai-jamjuree-latin-500.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
		U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("/bai-jamjuree-latin-ext-500.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
		U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("/bai-jamjuree-latin-600.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
		U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("/bai-jamjuree-latin-ext-600.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
		U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("/bai-jamjuree-latin-700.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
		U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Bai Jamjuree";
	font-style: normal;
	font-weight: 700;
	font-display: swap;
	src: url("/bai-jamjuree-latin-ext-700.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
		U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The design tokens below are GENERATED from `design/tokens.toml` by
   `cargo run -p design-tokens --bin generate`, which writes the same
   block into both web stylesheets and into the Swift and Kotlin
   surfaces beside them. Do not hand-edit between the markers — edit
   the source and regenerate; `cargo test -p design-tokens` fails if
   the two disagree. Everything outside the markers is hand-written
   and the generator leaves it alone. */

/* GENERATED TOKENS BEGIN */
:root {
	/* ---- Palette: the literal swatches, exact hex ---- */
	/* The brand pink: every call to action, on light and dark alike.
	   Darkened from the shipped `#EC0A8C` on 2026-09-14 by the
	   smallest step that carries white label text over the 4.5:1
	   body-text floor (4.21:1 -> 4.58:1), holding hue and saturation. */
	--colour-pink: #E10A86;
	--colour-magenta: #A0016B;
	/* The magenta lifted for dark surfaces: the same hue at half the
	   saturation and twice the lightness, so magenta text clears 4.5:1
	   on the dark page, card and sunken fills alike. */
	--colour-magenta-light: #C969A9;
	--colour-indigo: #20034B;
	/* The indigo lifted for dark surfaces the same way: the same hue
	   at half the saturation, its lightness raised until heading ink
	   clears 4.5:1 on the dark page, card and sunken fills alike. */
	--colour-lavender: #9C7ACF;
	/* The Goald Gradient's middle stop. Referenced by the gradients
	   hand-written beneath this block and by nothing else — a gradient
	   is a value, not a token, so it is not generated. */
	--colour-gradient-mid: #5B0356;
	--colour-violet-deep: #1A0340;
	--colour-ink-violet: #23083A;
	--colour-violet-mist: #6A5A8A;
	--colour-prize-gold: #E8B33C;
	--colour-impact-green: #00C46A;
	/* Darkened from the shipped `#ED4C5C` on 2026-09-14: white label
	   text on the destructive button measured 3.63:1 and now measures
	   4.56:1. The smallest lightness step that clears the floor while
	   holding BOTH hue and saturation (354.0 degrees, 0.82), so the
	   danger colour stays a red rather than softening towards a
	   brick — the owner's call between the two ways down. */
	--colour-error-red: #E8192E;
	--colour-text-muted: #6B6280;
	--colour-sunken: #F3F3F3;
	--colour-raised: #FCFCFC;
	--colour-header: #F8F8F8;
	--colour-grey-divider: #D9D9D9;
	/* The decorative hairline: a card's edge, a table rule, a tab
	   strip. NOT a control boundary — that is `grey-boundary` below,
	   and the two are separate swatches precisely so the boundary can
	   be strong enough to identify a control without every hairline
	   in both apps darkening with it. */
	--colour-grey-border: #E7E7E7;
	/* The control boundary: the lightest grey that identifies a text
	   field against the white page at the 3:1 non-text floor
	   (3.07:1, where the `#E7E7E7` hairline measures 1.24:1). */
	--colour-grey-boundary: #939393;
	--colour-picton-blue: #34ACEE;
	--colour-silver: #B3B9C4;
	--colour-bronze: #C1680F;
	--colour-white: #FFFFFF;

	/* The award-style organisation-page swatches. */
	--colour-award-purple-deep: #361453;
	--colour-award-purple: #532A78;
	--colour-award-magenta: #E400B7;
	--colour-award-gold: #F3BA25;
	--colour-award-navy: #15133E;
	--colour-award-sky: #00BCFE;

	/* The dark-appearance swatches: the violet-tinted surface and ink
	   ramps the dark override reaches for. Nothing outside that
	   override references them. */
	--colour-dark-page: #170B1D;
	--colour-dark-raised: #21112B;
	--colour-dark-sunken: #100716;
	--colour-dark-header: #1D0E25;
	--colour-dark-border: #3A2547;
	/* The control boundary in the dark appearance — `grey-boundary`'s
	   counterpart, and derived the same way: `dark-border`'s own hue
	   raised until the boundary clears 3:1 against the dark page
	   (3.06:1, where the `#3A2547` hairline measures 1.39:1). */
	--colour-violet-boundary: #7B4E96;
	--colour-dark-ink: #F3F3F3;
	--colour-dark-muted: #B6A9BF;

	/* ---- Semantic layer: the role names every surface reads ---- */
	--colour-page: var(--colour-white);
	/* A card, a sheet, a table row: the surface that sits above the
	   page. */
	--colour-page-raised: var(--colour-raised);
	/* A well beneath the page: a secondary button, an idle chip, an
	   input group's backing. */
	--colour-page-sunken: var(--colour-sunken);
	--colour-page-header: var(--colour-header);
	--colour-ink: var(--colour-ink-violet);
	--colour-ink-muted: var(--colour-text-muted);
	/* Text and glyphs drawn on an accent or danger fill. */
	--colour-on-accent: var(--colour-white);
	--colour-accent: var(--colour-pink);
	/* The quieter half of the accent pair: a secondary button's label,
	   a pressed state. The deep magenta on light; the lifted magenta
	   on dark, where the deep one is unreadable on the sunken fill
	   this label sits on. */
	--colour-accent-muted: var(--colour-magenta);
	/* The heading's quieter partner: a secondary heading, a secondary
	   action. Takes the same two magentas as `accent-muted`, for the
	   same reason — a heading has to be readable in both appearances. */
	--colour-subheading: var(--colour-magenta);
	/* A page or section title, an author's name, a statistic's value,
	   a chip's or a badge's label: the ink a heading is set in. The
	   deep indigo on light; the lavender lifted from it on dark, where
	   the indigo disappears into the surface behind it. */
	--colour-heading: var(--colour-indigo);
	/* Fixed chrome: the nav bar and the sidebar stay dark violet in
	   both appearances, which is why this entry has no dark form of its
	   own. */
	--colour-nav-bar: var(--colour-violet-deep);
	--colour-nav-idle: var(--colour-violet-mist);
	--colour-nav-bar-border: rgb(255 255 255 / 20%);
	/* The decorative hairline every container and separator draws:
	   card, tile, table, tab strip, the nav bar's edges. A boundary
	   that IDENTIFIES a control is `field-border` instead — the split
	   is what keeps a control legible without darkening every
	   hairline in both apps. */
	--colour-border: var(--colour-grey-border);
	/* The boundary that identifies an input: strong enough to clear
	   SC 1.4.11's 3:1 floor against the page in both appearances,
	   which a hairline is not. The `field` treatment and the
	   stylesheets' own input rules are its only readers. */
	--colour-field-border: var(--colour-grey-boundary);
	--colour-divider: var(--colour-grey-divider);
	/* The hairline around a raised surface: ink at a tenth on light,
	   white at a tenth on dark. */
	--colour-card-edge: rgb(35 8 58 / 10%);
	--colour-prize: var(--colour-prize-gold);
	--colour-impact: var(--colour-impact-green);
	--colour-danger: var(--colour-error-red);
	--colour-on-danger: var(--colour-white);
	--colour-rank-first: var(--colour-award-gold);
	--colour-rank-second: var(--colour-silver);
	--colour-rank-third: var(--colour-bronze);
	--colour-info: var(--colour-picton-blue);
	/* The wash behind a modal or a sheet. */
	--colour-scrim: rgb(26 16 48 / 8%);
	/* The colour a drop shadow is drawn in — the `--shadow-card` value
	   hand-written beneath this block composes it. Equal to the scrim
	   today; separate because one is a wash over content and the other
	   is a shadow under it, and they will not stay equal. */
	--colour-shadow: rgb(26 16 48 / 8%);

	/* The organisation-page roles. The award-style palette is the
	   design's own vocabulary, and these name the part each swatch
	   plays on that page rather than the colour it happens to be. */
	--colour-award-surface-deep: var(--colour-award-purple-deep);
	--colour-award-surface: var(--colour-award-purple);
	--colour-award-action: var(--colour-award-magenta);
	--colour-award-highlight: var(--colour-award-gold);
	--colour-award-ink: var(--colour-award-navy);
	--colour-award-pill: var(--colour-award-sky);

	/* ---- Type ---- */

	/* Bai Jamjuree throughout, self-hosted by the web surfaces and
	   bundled by the shells. THE FACE CARRIES LATIN AND LATIN-EXTENDED
	   ONLY: Cyrillic, Devanagari, Bengali, Japanese and Simplified
	   Chinese all render in whichever fallback face the platform picks,
	   and no per-script stack is declared anywhere. That is a recorded
	   gap for the owner, not a decision — the stack below is one
	   family, and inventing a per-script vocabulary here would put an
	   untested answer in front of a design question nobody has asked
	   yet. */
	--font-sans: "Bai Jamjuree", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	--font-size-display: 32px;
	--font-size-h1: 24px;
	--font-size-h2: 20px;
	--font-size-h3: 18px;
	--font-size-body: 15px;
	--font-size-small: 13px;
	--font-size-caption: 11px;
	--weight-regular: 400;
	--weight-medium: 500;
	--weight-semibold: 600;
	/* The family tops out at 700; heavier strokes are synthesised. */
	--weight-bold: 700;

	/* ---- Shape: 5px chips, 10px buttons / inputs / cards, 16px
	   sheets, and the pill ---- */
	--radius-sm: 5px;
	--radius-md: 10px;
	--radius-lg: 16px;
	--radius-pill: 999px;

	/* ---- Spacing ---- */

	/* Declared here in PIXELS — one number every surface shares, and
	   what the shells want verbatim (`Dp`, `CGFloat`). The web writer
	   is the one place that converts, dividing by the 16px root font
	   size to emit `rem`: a gap in `px` ignores the reader's own base
	   font size, so a page spaced entirely in `px` stops growing when
	   someone enlarges their browser's default text. One declaration,
	   both truths — never a second spacing scale in rem. */
	--space-1: 0.25rem;
	--space-2: 0.5rem;
	--space-3: 0.75rem;
	--space-4: 1rem;
	--space-6: 1.5rem;
	--space-8: 2rem;

	/* ---- Fixed sizes: the dimensions that are neither spacing nor
	   type, so no shell carries a raw number of its own ---- */
	/* A button's and a field's touch target. */
	--size-control-height: 50px;
	--size-avatar: 40px;
	--size-avatar-sm: 36px;
	--size-avatar-lg: 88px;
	--size-tab-chip: 36px;

	/* ---- Opacity ---- */
	/* The dimming every disabled control shares, so all four surfaces
	   dim by the same amount. */
	--opacity-disabled: 0.5;

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

	/* The primary action: a filled accent button with white text. Its
	   label is UPPERCASED — a presentational rule the schema has no
	   field for, recorded here and applied by each surface's own text
	   transform. */
	--primary-button-fill: var(--colour-accent);
	--primary-button-text: var(--colour-on-accent);
	--primary-button-radius: var(--radius-md);
	--primary-button-height: var(--size-control-height);
	--primary-button-label-size: var(--font-size-body);
	--primary-button-label-weight: var(--weight-bold);

	/* The secondary action: the same shape on the sunken fill, its
	   label in the muted accent. */
	--secondary-button-fill: var(--colour-page-sunken);
	--secondary-button-text: var(--colour-accent-muted);
	--secondary-button-radius: var(--radius-md);
	--secondary-button-height: var(--size-control-height);
	--secondary-button-label-size: var(--font-size-body);
	--secondary-button-label-weight: var(--weight-bold);

	/* A destructive action: the same shape in the danger colour. Its
	   own treatment rather than a flag on the primary one, so a safe
	   default can never sit on a destructive action. */
	--destructive-button-fill: var(--colour-danger);
	--destructive-button-text: var(--colour-on-danger);
	--destructive-button-radius: var(--radius-md);
	--destructive-button-height: var(--size-control-height);
	--destructive-button-label-size: var(--font-size-body);
	--destructive-button-label-weight: var(--weight-bold);

	/* A single-line text input: ink on the raised surface, inside the
	   boundary that identifies it, at the control height. */
	--field-fill: var(--colour-page-raised);
	--field-text: var(--colour-ink);
	--field-border: var(--colour-field-border);
	--field-border-width: 1px;
	--field-radius: var(--radius-md);
	--field-height: var(--size-control-height);

	/* The content card: the raised surface behind the card edge,
	   padded on the spacing scale. */
	--card-fill: var(--colour-page-raised);
	--card-border: var(--colour-card-edge);
	--card-border-width: 1px;
	--card-radius: var(--radius-md);
	--card-padding-horizontal: var(--space-4);
	--card-padding-vertical: var(--space-4);

	/* A small label on the sunken fill, at the chip radius. */
	--chip-fill: var(--colour-page-sunken);
	--chip-text: var(--colour-heading);
	--chip-radius: var(--radius-sm);

	/* The chip's rounded form: the same colours at the pill radius. */
	--badge-fill: var(--colour-page-sunken);
	--badge-text: var(--colour-heading);
	--badge-radius: var(--radius-pill);

	/* A tab in a chip row: a fixed-height chip filled with the deep
	   violet chrome colour, carrying on-accent text. The per-tab blue,
	   green, gold and bronze fills a screen may apply are SCREEN-LEVEL
	   MODIFIERS on this one treatment, not treatments of their own —
	   the violet declared here is the tab's own colour, and what the
	   contrast gate measures the on-accent text against.

	   It reads `nav-bar` rather than `heading`, which it carried until
	   2026-09-14: this is a FILL under white glyphs, not heading ink,
	   and the two roles part company in the dark appearance, where a
	   heading has to lighten and a fill under white content must not. */
	--tab-chip-fill: var(--colour-nav-bar);
	--tab-chip-text: var(--colour-on-accent);
	--tab-chip-radius: var(--radius-sm);
	--tab-chip-height: var(--size-tab-chip);

	/* The page itself: ink on the page fill. */
	--page-fill: var(--colour-page);
	--page-text: var(--colour-ink);
}

/* The dark appearance. Only the colours whose dark form differs from
   their light form are re-declared here; every other entry keeps its
   light value in both, a recorded design gap until dark mode is
   designed rather than a claim that it already has been. */
@media (prefers-color-scheme: dark) {
	:root {
		--colour-page: var(--colour-dark-page);
		--colour-page-raised: var(--colour-dark-raised);
		--colour-page-sunken: var(--colour-dark-sunken);
		--colour-page-header: var(--colour-dark-header);
		--colour-ink: var(--colour-dark-ink);
		--colour-ink-muted: var(--colour-dark-muted);
		--colour-accent-muted: var(--colour-magenta-light);
		--colour-subheading: var(--colour-magenta-light);
		--colour-heading: var(--colour-lavender);
		--colour-border: var(--colour-dark-border);
		--colour-field-border: var(--colour-violet-boundary);
		--colour-divider: var(--colour-dark-border);
		--colour-card-edge: rgb(255 255 255 / 10%);
		--colour-scrim: rgb(0 0 0 / 40%);
		--colour-shadow: rgb(0 0 0 / 40%);
	}
}
/* GENERATED TOKENS END */

/* Hand-written, immediately beneath the generated block: a gradient
   and a box-shadow are composed VALUES, not tokens — the generator
   emits the colours they compose, and these spell out how they
   compose them. One gradient everywhere: same angle, same stops. Do
   not re-declare it per surface. The stops are PALETTE swatches
   rather than semantic roles deliberately: a brand gradient is one
   fixed asset in both appearances, where the roles that used to
   supply this one's two ends — `subheading` and `heading` — lighten
   in the dark one. */
:root {
	--gradient-auth:
		linear-gradient(100deg, var(--colour-magenta) 0%, var(--colour-gradient-mid) 45%, var(--colour-indigo) 100%); /* the Goald Gradient — the only purple */

	--shadow-card:          0 2px 3px var(--colour-shadow);
}

* {
	box-sizing: border-box;
}

html,
body {
	margin: 0;
	padding: 0;
	height: 100%;
	background: var(--colour-page);
	color: var(--colour-ink);
	font-family: var(--font-sans);
	font-size: 15px;
	line-height: 1.5;
}

/* The admin's canvas is grey, not white: its dashboard cards, stat
   tiles and tables are raised surfaces, and a raised surface needs a
   page beneath it to be raised ABOVE. The `page` token itself stays
   white — that is the app's canvas, and one token serves both — so
   this is the admin reaching for the header grey it already carries
   rather than a second page colour being invented for it. */
body {
	background: var(--colour-page-header);
}

a {
	color: var(--colour-accent);
	font-weight: 600;
}

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

/* App shell: dark-violet sidebar chrome + light content column. */

.app-shell {
	display: grid;
	grid-template-columns: 220px 1fr;
	min-height: 100vh;
}

.app-sidebar {
	background: var(--colour-nav-bar);
	border-right: none;
	padding: var(--space-4);
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

.app-sidebar__brand {
	font-weight: 700;
	font-size: 1.2rem;
	color: #ffffff;
	margin-bottom: var(--space-4);
}

.app-sidebar a {
	display: block;
	padding: var(--space-2) var(--space-3);
	border-radius: var(--radius-sm);
	color: rgb(255 255 255 / 70%);
	font-weight: 600;
	text-decoration: none;
}

.app-sidebar a:hover,
.app-sidebar a.active,
.app-sidebar a[aria-current] {
	background: rgb(255 255 255 / 10%);
	color: var(--colour-accent);
}

.app-sidebar button {
	margin-top: var(--space-4);
}

/* The signed-in identity, pinned above Sign out at the sidebar foot. */
.app-sidebar__user {
	margin-top: auto;
	color: rgb(255 255 255 / 70%);
	font-size: 0.8rem;
	word-break: break-all;
}

.app-sidebar .btn {
	background: rgb(255 255 255 / 10%);
	color: #ffffff;
}

.app-sidebar .btn:hover {
	background: rgb(255 255 255 / 18%);
}

.app-main {
	padding: var(--space-6);
	overflow-x: auto;
}

.page-title {
	font-size: 1.4rem;
	font-weight: 700;
	color: var(--colour-heading);
	margin: 0 0 var(--space-4);
}


/* Group headings between dashboard sections (Growth / Engagement /
   Fundraising). */
.section-title {
	font-size: 1.1rem;
	font-weight: 600;
	color: var(--colour-heading);
	margin: var(--space-6) 0 var(--space-4);
}


.card {
	background: var(--colour-page-raised);
	border: 1px solid var(--colour-border);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-card);
	padding: var(--space-4);
}

.card + .card {
	margin-top: var(--space-4);
}

.stat-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: var(--space-4);
	margin-bottom: var(--space-6);
}

.stat-tile {
	background: var(--colour-page-raised);
	border: 1px solid var(--colour-border);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-card);
	padding: var(--space-4);
	transition:
		transform 150ms ease,
		box-shadow 150ms ease;
}

.stat-tile:hover {
	transform: translateY(-2px);
	box-shadow: 0 6px 14px var(--colour-shadow);
}

.stat-tile__label {
	color: var(--colour-ink-muted);
	font-size: 0.8rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

.stat-tile__value {
	font-size: 1.6rem;
	font-weight: 700;
	color: var(--colour-accent);
	margin-top: var(--space-1);
}

table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.9rem;
}

th,
td {
	text-align: left;
	padding: var(--space-2) var(--space-3);
	border-bottom: 1px solid var(--colour-border);
	white-space: nowrap;
}

th {
	color: var(--colour-ink-muted);
	font-weight: 600;
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

.table-scroll {
	overflow-x: auto;
	background: var(--colour-page-raised);
	border: 1px solid var(--colour-border);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-card);
}

/* Buttons, inputs — the mobile shapes: 10px radius, filled, borderless. */

.btn {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	background: var(--colour-page-sunken);
	border: none;
	border-radius: var(--radius-md);
	color: var(--colour-accent-muted);
	font-weight: 700;
	padding: var(--space-2) var(--space-3);
	cursor: pointer;
}


.btn:hover {
	filter: brightness(0.96);
}

.btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.btn-primary {
	background: var(--colour-accent);
	color: #ffffff;
}

.btn-primary:hover {
	filter: brightness(1.05);
}

.btn-danger {
	background: var(--colour-danger);
	color: var(--colour-on-danger);
}

.input {
	background: var(--colour-page-raised);
	border: 1px solid var(--colour-field-border);
	border-radius: var(--radius-md);
	color: var(--colour-ink);
	padding: var(--space-2) var(--space-3);
}

.input:focus {
	outline: 2px solid var(--colour-accent);
	outline-offset: -1px;
}

/* Badges — the mobile grammar: guppieGreen fill with darkViolet
   content for success, brand-consistent fills for the other tones. */

.badge {
	display: inline-block;
	padding: 0.15rem 0.6rem;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 700;
	background: var(--colour-page-sunken);
	color: var(--colour-heading);
}


.badge-success {
	background: var(--colour-impact);
	color: var(--colour-nav-bar);
}

/* Dark-violet text like .badge-success — white on mustard is ~1.9:1
   contrast, unreadable. */
.badge-warning {
	background: var(--colour-prize);
	color: var(--colour-nav-bar);
}

.badge-danger {
	background: var(--colour-danger);
	color: var(--colour-on-danger);
}

/* Flash notices (`components/flash.rs`) — mutation feedback shown as
   a fixed overlay: success auto-dismisses, errors stay until
   dismissed. */

.flash {
	position: fixed;
	top: var(--space-4);
	right: var(--space-4);
	z-index: 100;
	display: flex;
	align-items: center;
	gap: var(--space-3);
	max-width: 420px;
	padding: var(--space-3) var(--space-4);
	border-radius: var(--radius-md);
	box-shadow: 0 6px 14px var(--colour-shadow);
	font-weight: 600;
}

.flash-success {
	background: var(--colour-impact);
	color: var(--colour-nav-bar);
}

.flash-error {
	background: var(--colour-danger);
	color: var(--colour-on-danger);
}

.flash-dismiss {
	background: none;
	border: none;
	cursor: pointer;
	color: inherit;
	font-size: 1.1rem;
	font-weight: 700;
	line-height: 1;
	padding: 0;
}

.error-banner {
	background: color-mix(in srgb, var(--colour-danger) 12%, var(--colour-page-raised));
	border: 1px solid var(--colour-danger);
	color: var(--colour-ink);
	border-radius: var(--radius-md);
	padding: var(--space-3);
	margin-bottom: var(--space-4);
}

/* Login shell — the one full-bleed screen that gets the mobile app's
   auth look in full: purple→pink gradient, white heading, floating
   white inputs, uppercase pink CTA. The `.card` the login form also
   carries is neutralised here rather than restructuring the markup. */

.login-shell {
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-4);
	background: var(--gradient-auth);
}

.login-shell .card {
	background: transparent;
	border: none;
	box-shadow: none;
}

.login-card {
	width: 320px;
	max-width: 100%;
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

.login-card .page-title {
	color: #ffffff;
	text-align: center;
	font-size: 1.8rem;
	margin-bottom: var(--space-4);
}

.login-card .field label {
	color: rgb(255 255 255 / 85%);
}

.login-card .input {
	background: #ffffff;
	color: var(--colour-ink);
	border: none;
	box-shadow: 0 4px 12px rgb(0 0 0 / 20%);
}

.login-card .input::placeholder {
	color: #808080;
}

.login-card .btn-primary {
	width: 100%;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	justify-content: center;
	margin-top: var(--space-2);
}

.field {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

.field label {
	font-size: 0.8rem;
	font-weight: 600;
	color: var(--colour-ink-muted);
}

.toolbar {
	display: flex;
	gap: var(--space-2);
	align-items: center;
	margin-bottom: var(--space-4);
	flex-wrap: wrap;
}

.pagination {
	display: flex;
	gap: var(--space-2);
	align-items: center;
	margin-top: var(--space-4);
}

.muted {
	color: var(--colour-ink-muted);
}

/* The shared "no results" table row (`components/empty_row.rs`). */
.empty-row {
	text-align: center;
	padding: var(--space-6) var(--space-3);
	white-space: normal;
}

/* Dashboard charts — hand-rolled inline SVG (`components/charts.rs`),
   no charting dependency. Brand colours only; the hover/focus states
   below drive the hand-authored interactivity (guide line, point /
   bar highlight, tooltip) in tandem with the Leptos signals that
   toggle these classes. */

.chart {
	width: 100%;
	height: auto;
	overflow: visible;
}

.chart-caption {
	color: var(--colour-ink-muted);
	font-size: 0.8rem;
	margin-top: var(--space-2);
}

.chart-gridline {
	stroke: var(--colour-border);
	stroke-width: 1;
}

.chart-axis-label {
	fill: var(--colour-ink-muted);
	font-size: 9px;
	font-family: var(--font-sans);
}

.chart-area-fill {
	fill: var(--colour-accent);
	fill-opacity: 16%;
}

.chart-line {
	stroke: var(--colour-accent);
	stroke-width: 2.5;
	stroke-linejoin: round;
	stroke-linecap: round;
}

.chart-point {
	fill: var(--colour-page-raised);
	stroke: var(--colour-accent);
	stroke-width: 2;
	transition: r 120ms ease;
}

.chart-point--active {
	fill: var(--colour-accent);
	stroke: var(--colour-nav-bar);
}

.chart-hit-target {
	fill: transparent;
	cursor: pointer;
}

.chart-guide {
	stroke: var(--colour-nav-idle);
	stroke-width: 1;
	stroke-dasharray: 4 3;
}

.chart-tooltip rect {
	fill: var(--colour-nav-bar);
	filter: drop-shadow(0 2px 4px var(--colour-shadow));
}

.chart-tooltip text {
	fill: #ffffff;
	font-size: 11px;
	font-weight: 600;
	font-family: var(--font-sans);
}

.chart-bar-row {
	cursor: pointer;
}

.chart-bar-hit {
	fill: transparent;
}

.chart-bar-label {
	fill: var(--colour-ink);
	font-size: 11px;
	font-family: var(--font-sans);
}

.chart-bar {
	fill: var(--colour-accent);
	transition:
		fill 120ms ease,
		filter 120ms ease;
}

.chart-bar--active {
	fill: var(--colour-accent);
	filter: drop-shadow(0 0 4px var(--colour-shadow));
}

.chart-bar-value {
	fill: var(--colour-ink-muted);
	font-size: 10.5px;
	font-weight: 600;
	font-family: var(--font-sans);
}

/* User-location map (`pages/map.rs`) — the same hand-rolled inline
   SVG approach as the dashboard charts, with a zoom/pan stage: the
   vendored country outlines sit inside a scaled <g>, while the
   tooltip reuses the .chart-tooltip vocabulary outside it. */

.map-toolbar {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: var(--space-2);
	margin-bottom: var(--space-3);
	flex-wrap: wrap;
}

.map-toolbar__group {
	display: flex;
	gap: var(--space-1);
}

.map-toggle--active {
	background: var(--colour-accent);
	border-color: var(--colour-accent);
	color: var(--colour-on-accent);
}

.map-toggle--active:hover {
	background: var(--colour-accent);
}

.map-zoom-btn {
	min-width: 2.25rem;
	font-weight: 700;
}

.map-stage {
	width: 100%;
	height: auto;
	background: var(--colour-page-sunken);
	border: 1px solid var(--colour-border);
	border-radius: var(--radius-md);
	cursor: grab;
	touch-action: none;
	user-select: none;
}

.map-stage--dragging {
	cursor: grabbing;
}

.map-country {
	fill: var(--colour-page-raised);
	stroke: var(--colour-border);
	stroke-width: 0.75;
}

.map-cluster {
	fill: var(--colour-accent);
	fill-opacity: 55%;
	stroke: var(--colour-accent);
	stroke-width: 1.5;
	cursor: pointer;
	transition: fill-opacity 120ms ease;
}

.map-cluster:hover {
	fill-opacity: 80%;
}

.map-region {
	fill: var(--colour-nav-idle);
	fill-opacity: 18%;
	stroke: var(--colour-nav-idle);
	stroke-width: 1.5;
	stroke-dasharray: 3 2;
	cursor: pointer;
	transition: fill-opacity 120ms ease;
}

.map-region:hover {
	fill-opacity: 40%;
}

.map-legend {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	color: var(--colour-ink-muted);
	font-size: 0.8rem;
	margin-top: var(--space-2);
}

.map-legend__scale {
	width: 68px;
	height: 12px;
	flex: none;
}

.map-legend__swatch {
	stroke: var(--colour-border);
	stroke-width: 0.5;
}

/* Media — compact table thumbnails (content listings) and the
   moderation review panel's inline hls.js player
   (`components/video_player.rs`). */

.thumb {
	width: 44px;
	height: 44px;
	object-fit: cover;
	border-radius: var(--radius-sm);
	background: var(--colour-page-sunken);
	display: block;
}

.thumb-placeholder {
	width: 44px;
	height: 44px;
	border-radius: var(--radius-sm);
	background: var(--colour-page-sunken);
	border: 1px dashed var(--colour-border);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--colour-ink-muted);
	font-size: 0.65rem;
	text-align: center;
}

.video-frame {
	display: block;
	width: 100%;
	max-width: 360px;
	max-height: 60vh;
	/* Overridden inline (`style=("aspect-ratio", …)`) once the media's
	   real width/height are known; this is the pre-metadata / unknown-
	   dimensions fallback — portrait 9:16 is the common case. */
	aspect-ratio: 9 / 16;
	margin: 0 auto var(--space-2);
	background: #000;
	border-radius: var(--radius-md);
	object-fit: contain;
}

.video-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	max-width: 360px;
	min-height: 180px;
	margin: 0 auto var(--space-2);
	background: var(--colour-page-sunken);
	border: 1px dashed var(--colour-border);
	border-radius: var(--radius-md);
}
