/* Arabesque Labs design system. Generated by scripts/build.mjs: don't edit; change tokens/ and css/. */
@layer al.tokens, al.base, al.layout, al.components, al.utilities;
@layer al.tokens {
/* Arabesque Labs design system: tokens. Generated by scripts/build.mjs from tokens/tokens.json: don't edit. */

:root {
  color-scheme: light;
  --al-color-sand-74: oklch(73.5% 0.012 100);
  --al-color-sand-82: oklch(82% 0.006 100);
  --al-color-sand-88: oklch(87.8% 0.006 100);
  --al-color-sand-92: oklch(92.5% 0.006 100);
  --al-color-sand-93: oklch(92.8% 0.006 100);
  --al-color-sand-95: oklch(95.1% 0.005 100);
  --al-color-sand-98: oklch(98.2% 0.003 100);
  --al-color-cloud-21: oklch(20.8% 0.042 266);
  --al-color-cloud-45: oklch(44.6% 0.043 257);
  --al-color-cloud-87: oklch(86.9% 0.022 252);
  --al-color-cloud-93: oklch(92.9% 0.013 255);
  --al-color-cloud-96: oklch(96.8% 0.007 248);
  --al-color-cloud-98: oklch(98.4% 0.003 248);
  --al-color-cloud-99: oklch(99.4% 0.002 248);
  --al-color-slate-18: oklch(18.2% 0.017 265);
  --al-color-slate-21: oklch(21.2% 0.018 265);
  --al-color-slate-24: oklch(24.2% 0.018 265);
  --al-color-slate-25: oklch(24.5% 0.028 265);
  --al-color-slate-45: oklch(44.5% 0.022 265);
  --al-color-slate-56: oklch(56% 0.016 265);
  --al-color-slate-62: oklch(62% 0.012 265);
  --al-color-teal-46: oklch(45.5% 0.079 176);
  --al-color-teal-51: oklch(51% 0.095 183);
  --al-color-teal-83: oklch(82.5% 0.105 168);
  --al-color-red-52: oklch(52% 0.19 27);
  --al-color-red-54: oklch(54% 0.21 27);
  --al-color-red-68: oklch(68% 0.17 25);
  --al-color-amber-49: oklch(49% 0.105 75);
  --al-color-amber-70: oklch(70% 0.12 85);
  --al-color-amber-80: oklch(80% 0.12 85);
  --al-color-blue-48: oklch(48% 0.1 250);
  --al-color-blue-53: oklch(53% 0.19 262);
  --al-color-blue-80: oklch(80% 0.08 250);
  --al-color-chart-green-light: #007a5c;
  --al-color-chart-orange-light: #c26300;
  --al-color-chart-blue-light: #3d73b6;
  --al-color-chart-raspberry-light: #b8336a;
  --al-color-chart-green-dark: #3ba888;
  --al-color-chart-orange-dark: #ca7e31;
  --al-color-chart-blue-dark: #5188cd;
  --al-color-chart-raspberry-dark: #d75b8f;
  --al-color-white: oklch(100% 0 0);
  --al-color-black: oklch(0% 0 0);
  --al-color-bg: var(--al-color-cloud-98);
  --al-color-surface: var(--al-color-white);
  --al-color-surface-sunk: var(--al-color-cloud-96);
  --al-color-text: var(--al-color-cloud-21);
  --al-color-text-muted: var(--al-color-cloud-45);
  --al-color-line: var(--al-color-cloud-93);
  --al-color-line-strong: oklch(20.8% 0.042 266 / 0.4);
  --al-color-control: var(--al-color-slate-62);
  --al-color-accent: var(--al-color-teal-51);
  --al-color-on-accent: var(--al-color-white);
  --al-color-accent-wash: oklch(60% 0.11 183 / 0.12);
  --al-color-focus: var(--al-color-teal-51);
  --al-color-danger: var(--al-color-red-54);
  --al-color-danger-wash: oklch(63% 0.22 27 / 0.1);
  --al-color-warning: var(--al-color-amber-49);
  --al-color-warning-wash: oklch(86% 0.16 92 / 0.3);
  --al-color-success: var(--al-color-teal-51);
  --al-color-success-wash: oklch(60% 0.11 183 / 0.12);
  --al-color-info: var(--al-color-blue-53);
  --al-color-info-wash: oklch(62% 0.19 262 / 0.1);
  --al-color-backdrop: oklch(20.8% 0.042 266 / 0.45);
  --al-color-inverse: var(--al-color-cloud-21);
  --al-color-on-inverse: var(--al-color-cloud-98);
  --al-color-chart-0: var(--al-color-cloud-87);
  --al-color-chart-1: var(--al-color-chart-green-light);
  --al-color-chart-2: var(--al-color-chart-orange-light);
  --al-color-chart-3: var(--al-color-chart-blue-light);
  --al-color-chart-4: var(--al-color-chart-raspberry-light);
  --al-color-tint-teal-bg: #CCFBF1;
  --al-color-tint-teal-ink: #115E59;
  --al-color-tint-green-bg: #DCFCE7;
  --al-color-tint-green-ink: #166534;
  --al-color-tint-yellow-bg: #FEF9C3;
  --al-color-tint-yellow-ink: #854D0E;
  --al-color-tint-orange-bg: #FFEDD5;
  --al-color-tint-orange-ink: #9A3412;
  --al-color-tint-pink-bg: #FCE7F3;
  --al-color-tint-pink-ink: #9D174D;
  --al-color-tint-purple-bg: #F3E8FF;
  --al-color-tint-purple-ink: #6B21A8;
  --al-color-tint-blue-bg: #DBEAFE;
  --al-color-tint-blue-ink: #1E40AF;
  --al-color-tint-cyan-bg: #CFFAFE;
  --al-color-tint-cyan-ink: #155E75;
  --al-color-pattern-line: oklch(51% 0.095 183 / 0.24);
  --al-color-pattern-scaffold: oklch(51% 0.095 183 / 0.1);
  --al-font-sans: "Jost", "IBM Plex Sans Arabic", "Noto Sans Arabic", "SF Arabic", "Geeza Pro", ui-sans-serif, system-ui, sans-serif;
  --al-font-display: "Jost", "Kufam", "Noto Kufi Arabic", "IBM Plex Sans Arabic", ui-sans-serif, system-ui, sans-serif;
  --al-font-serif: "Newsreader", "Amiri", "Noto Naskh Arabic", "Scheherazade New", "Georgia", serif;
  --al-font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", "Menlo", monospace;
  --al-script-scale: 1;
  --al-text-xs: calc(0.75rem * var(--al-script-scale));
  --al-text-sm: calc(0.8125rem * var(--al-script-scale));
  --al-text-base: calc(0.9375rem * var(--al-script-scale));
  --al-text-md: calc(1.0625rem * var(--al-script-scale));
  --al-text-lg: calc(1.25rem * var(--al-script-scale));
  --al-text-xl: calc(1.5rem * var(--al-script-scale));
  --al-text-2xl: calc(clamp(1.75rem, 1.5rem + 1vw, 2.25rem) * var(--al-script-scale));
  --al-text-3xl: calc(clamp(2rem, 1.5rem + 2vw, 3rem) * var(--al-script-scale));
  --al-text-display: calc(clamp(2.5rem, 1.6rem + 3.6vw, 4.75rem) * var(--al-script-scale));
  --al-leading-tight: 1.1;
  --al-leading-heading: 1.2;
  --al-leading-ui: 1.45;
  --al-leading-prose: 1.6;
  --al-weight-regular: 400;
  --al-weight-medium: 500;
  --al-weight-semibold: 600;
  --al-tracking-tight: -0.015em;
  --al-tracking-none: 0;
  --al-tracking-wide: 0.05em;
  --al-space-0: 0;
  --al-space-1: 0.25rem;
  --al-space-2: 0.5rem;
  --al-space-3: 0.75rem;
  --al-space-4: 1rem;
  --al-space-5: 1.25rem;
  --al-space-6: 1.5rem;
  --al-space-8: 2rem;
  --al-space-12: 3rem;
  --al-space-16: 4rem;
  --al-space-px: 1px;
  --al-space-0-5: 0.125rem;
  --al-space-1-5: 0.375rem;
  --al-space-section: clamp(3rem, 2rem + 6vw, 9rem);
  --al-space-gutter: clamp(1rem, 0.5rem + 3vw, 4rem);
  --al-radius-sm: 0.125rem;
  --al-radius-md: 0.25rem;
  --al-radius-full: 999px;
  --al-border-hairline: 1px;
  --al-border-focus: 2px;
  --al-border-focus-offset: 2px;
  --al-size-control-sm: 2rem;
  --al-size-control-md: 2.5rem;
  --al-size-target-min: 1.5rem;
  --al-size-target-touch: 2.75rem;
  --al-size-icon: 1.125rem;
  --al-size-measure: 65ch;
  --al-size-page: 75rem;
  --al-size-dialog-sm: 30rem;
  --al-size-dialog-md: 48rem;
  --al-size-sidebar: 14rem;
  --al-size-sidebar-collapsed: 3.75rem;
  --al-duration-fast: 150ms;
  --al-duration-base: 250ms;
  --al-duration-slow: 400ms;
  --al-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --al-ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --al-z-raised: 1;
  --al-z-sticky: 10;
  --al-z-overlay: 40;
  --al-z-toast: 100;
  --al-breakpoint-sm: 40rem;
  --al-breakpoint-md: 52rem;
  --al-breakpoint-lg: 64rem;
  --al-pattern-side: 32px;
  --al-pattern-stroke: 1.25px;
  --al-pattern-contact-angle: 67.5deg;
  --al-button-height: var(--al-size-control-md);
  --al-button-height-sm: var(--al-size-control-sm);
  --al-button-padding-inline: var(--al-space-4);
  --al-button-radius: var(--al-radius-sm);
  --al-field-height: var(--al-size-control-md);
  --al-field-padding-inline: var(--al-space-3);
  --al-field-radius: var(--al-radius-sm);
  --al-focus-ring: 2px solid var(--al-color-focus);
}

/* Dark: by the system's setting, unless light is chosen; or when chosen. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --al-color-bg: var(--al-color-slate-18);
    --al-color-surface: var(--al-color-slate-21);
    --al-color-surface-sunk: var(--al-color-slate-24);
    --al-color-text: var(--al-color-sand-93);
    --al-color-text-muted: var(--al-color-sand-74);
    --al-color-line: oklch(100% 0 0 / 0.12);
    --al-color-line-strong: oklch(100% 0 0 / 0.4);
    --al-color-control: var(--al-color-slate-62);
    --al-color-accent: var(--al-color-teal-83);
    --al-color-on-accent: var(--al-color-slate-18);
    --al-color-accent-wash: oklch(82.5% 0.105 168 / 0.12);
    --al-color-focus: var(--al-color-teal-83);
    --al-color-danger: var(--al-color-red-68);
    --al-color-danger-wash: oklch(68% 0.17 25 / 0.14);
    --al-color-warning: var(--al-color-amber-80);
    --al-color-warning-wash: oklch(80% 0.12 85 / 0.12);
    --al-color-success: var(--al-color-teal-83);
    --al-color-success-wash: oklch(82.5% 0.105 168 / 0.12);
    --al-color-info: var(--al-color-blue-80);
    --al-color-info-wash: oklch(80% 0.08 250 / 0.12);
    --al-color-backdrop: oklch(0% 0 0 / 0.6);
    --al-color-inverse: var(--al-color-sand-93);
    --al-color-on-inverse: var(--al-color-slate-18);
    --al-color-chart-0: oklch(100% 0 0 / 0.2);
    --al-color-chart-1: var(--al-color-chart-green-dark);
    --al-color-chart-2: var(--al-color-chart-orange-dark);
    --al-color-chart-3: var(--al-color-chart-blue-dark);
    --al-color-chart-4: var(--al-color-chart-raspberry-dark);
    --al-color-tint-teal-bg: oklch(70% 0.13 183 / 0.18);
    --al-color-tint-teal-ink: #5EEAD4;
    --al-color-tint-green-bg: oklch(72% 0.17 150 / 0.18);
    --al-color-tint-green-ink: #86EFAC;
    --al-color-tint-yellow-bg: oklch(85% 0.17 95 / 0.18);
    --al-color-tint-yellow-ink: #FDE047;
    --al-color-tint-orange-bg: oklch(75% 0.16 55 / 0.18);
    --al-color-tint-orange-ink: #FDBA74;
    --al-color-tint-pink-bg: oklch(72% 0.17 350 / 0.18);
    --al-color-tint-pink-ink: #F9A8D4;
    --al-color-tint-purple-bg: oklch(70% 0.17 305 / 0.2);
    --al-color-tint-purple-ink: #D8B4FE;
    --al-color-tint-blue-bg: oklch(68% 0.16 262 / 0.2);
    --al-color-tint-blue-ink: #93C5FD;
    --al-color-tint-cyan-bg: oklch(75% 0.13 215 / 0.18);
    --al-color-tint-cyan-ink: #67E8F9;
    --al-color-pattern-line: oklch(100% 0 0 / 0.16);
    --al-color-pattern-scaffold: oklch(100% 0 0 / 0.08);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --al-color-bg: var(--al-color-slate-18);
  --al-color-surface: var(--al-color-slate-21);
  --al-color-surface-sunk: var(--al-color-slate-24);
  --al-color-text: var(--al-color-sand-93);
  --al-color-text-muted: var(--al-color-sand-74);
  --al-color-line: oklch(100% 0 0 / 0.12);
  --al-color-line-strong: oklch(100% 0 0 / 0.4);
  --al-color-control: var(--al-color-slate-62);
  --al-color-accent: var(--al-color-teal-83);
  --al-color-on-accent: var(--al-color-slate-18);
  --al-color-accent-wash: oklch(82.5% 0.105 168 / 0.12);
  --al-color-focus: var(--al-color-teal-83);
  --al-color-danger: var(--al-color-red-68);
  --al-color-danger-wash: oklch(68% 0.17 25 / 0.14);
  --al-color-warning: var(--al-color-amber-80);
  --al-color-warning-wash: oklch(80% 0.12 85 / 0.12);
  --al-color-success: var(--al-color-teal-83);
  --al-color-success-wash: oklch(82.5% 0.105 168 / 0.12);
  --al-color-info: var(--al-color-blue-80);
  --al-color-info-wash: oklch(80% 0.08 250 / 0.12);
  --al-color-backdrop: oklch(0% 0 0 / 0.6);
  --al-color-inverse: var(--al-color-sand-93);
  --al-color-on-inverse: var(--al-color-slate-18);
  --al-color-chart-0: oklch(100% 0 0 / 0.2);
  --al-color-chart-1: var(--al-color-chart-green-dark);
  --al-color-chart-2: var(--al-color-chart-orange-dark);
  --al-color-chart-3: var(--al-color-chart-blue-dark);
  --al-color-chart-4: var(--al-color-chart-raspberry-dark);
  --al-color-tint-teal-bg: oklch(70% 0.13 183 / 0.18);
  --al-color-tint-teal-ink: #5EEAD4;
  --al-color-tint-green-bg: oklch(72% 0.17 150 / 0.18);
  --al-color-tint-green-ink: #86EFAC;
  --al-color-tint-yellow-bg: oklch(85% 0.17 95 / 0.18);
  --al-color-tint-yellow-ink: #FDE047;
  --al-color-tint-orange-bg: oklch(75% 0.16 55 / 0.18);
  --al-color-tint-orange-ink: #FDBA74;
  --al-color-tint-pink-bg: oklch(72% 0.17 350 / 0.18);
  --al-color-tint-pink-ink: #F9A8D4;
  --al-color-tint-purple-bg: oklch(70% 0.17 305 / 0.2);
  --al-color-tint-purple-ink: #D8B4FE;
  --al-color-tint-blue-bg: oklch(68% 0.16 262 / 0.2);
  --al-color-tint-blue-ink: #93C5FD;
  --al-color-tint-cyan-bg: oklch(75% 0.13 215 / 0.18);
  --al-color-tint-cyan-ink: #67E8F9;
  --al-color-pattern-line: oklch(100% 0 0 / 0.16);
  --al-color-pattern-scaffold: oklch(100% 0 0 / 0.08);
}

/* Arabic: a little larger, more line height, no letter-spacing. The sizes
   are declared again here, so they're worked out with Arabic's scale even
   inside a page in another language (var() resolves where it's declared). */
:lang(ar) {
  --al-script-scale: 1.08;
  --al-leading-tight: 1.35;
  --al-leading-heading: 1.45;
  --al-leading-ui: 1.65;
  --al-leading-prose: 1.9;
  --al-tracking-tight: 0;
  --al-tracking-wide: 0;
  --al-text-xs: calc(0.75rem * var(--al-script-scale));
  --al-text-sm: calc(0.8125rem * var(--al-script-scale));
  --al-text-base: calc(0.9375rem * var(--al-script-scale));
  --al-text-md: calc(1.0625rem * var(--al-script-scale));
  --al-text-lg: calc(1.25rem * var(--al-script-scale));
  --al-text-xl: calc(1.5rem * var(--al-script-scale));
  --al-text-2xl: calc(clamp(1.75rem, 1.5rem + 1vw, 2.25rem) * var(--al-script-scale));
  --al-text-3xl: calc(clamp(2rem, 1.5rem + 2vw, 3rem) * var(--al-script-scale));
  --al-text-display: calc(clamp(2.5rem, 1.6rem + 3.6vw, 4.75rem) * var(--al-script-scale));
}

}
@layer al.base {
/* css/base.css */
/* Base: a small reset (after modern-normalize), the page, text, links,
   focus, and the system-wide rules for reduced motion and forced colours.
   Every value comes from a token. */

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

html {
  font-family: var(--al-font-sans);
  font-size: 100%;
  line-height: var(--al-leading-ui);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  tab-size: 4;
  color: var(--al-color-text);
  background: var(--al-color-bg);
  hanging-punctuation: first last;
}

body {
  margin: 0;
  font-size: var(--al-text-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- text -------------------------------------------------------------------------------- */

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  font-family: var(--al-font-display);
  font-weight: var(--al-weight-regular);
  line-height: var(--al-leading-heading);
  letter-spacing: var(--al-tracking-tight);
  text-wrap: balance;
}
h1 { font-size: var(--al-text-3xl); }
h2 { font-size: var(--al-text-xl); }
h3 { font-size: var(--al-text-lg); font-weight: var(--al-weight-medium); letter-spacing: var(--al-tracking-none); }
h4, h5, h6 { font-size: var(--al-text-base); font-weight: var(--al-weight-medium); letter-spacing: var(--al-tracking-none); }

p, ul, ol, dl, figure, blockquote, pre { margin: 0; }
p { text-wrap: pretty; }
small { font-size: var(--al-text-sm); }
strong, b { font-weight: var(--al-weight-semibold); }

code, kbd, samp, pre { font-family: var(--al-font-mono); font-size: 0.92em; }
pre { overflow-x: auto; }

hr { border: 0; border-block-start: var(--al-border-hairline) solid var(--al-color-line); margin: var(--al-space-6) 0; }

/* Arabic: the tokens already drop tracking and add line height; nothing is
   ever set in capitals, and Latin product names inside keep their tracking. */
:lang(ar) { letter-spacing: 0; }

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

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--al-color-line-strong);
  text-underline-offset: 0.2em;
  transition: color var(--al-duration-fast) var(--al-ease-standard), text-decoration-color var(--al-duration-fast) var(--al-ease-standard);
}
a:hover { color: var(--al-color-accent); text-decoration-color: currentColor; }

/* ---- media, tables, forms ---------------------------------------------------------------- */

img, video, canvas { display: block; max-inline-size: 100%; block-size: auto; }
svg { flex: none; }
table { border-collapse: collapse; border-spacing: 0; }
button, input, select, textarea { font: inherit; color: inherit; margin: 0; letter-spacing: inherit; }
button, [role="button"] { cursor: pointer; }
:disabled, [aria-disabled="true"] { cursor: not-allowed; }
[hidden] { display: none !important; }

/* ---- focus and selection ----------------------------------------------------------------- */

/* One focus style everywhere: an outline (never a box-shadow, which forced
   colours removes), offset from the edge, only for keyboard focus. */
:focus-visible {
  outline: var(--al-border-focus) solid var(--al-color-focus);
  outline-offset: var(--al-border-focus-offset);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: color-mix(in oklch, var(--al-color-accent) 22%, transparent); }

/* ---- motion ------------------------------------------------------------------------------ */

/* Reduced motion: nothing moves; changes still happen, instantly. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- forced colours ---------------------------------------------------------------------- */

/* Windows high contrast and other forced palettes: the system's colours
   win; keep every boundary visible as a border. Components add their own. */
@media (forced-colors: active) {
  :focus-visible { outline-color: Highlight; }
}

/* More contrast, when asked for: secondary text becomes body text,
   hairlines become control borders, and focus is drawn thicker. Each
   token resolves in its own theme, so this holds in light and dark. */
@media (prefers-contrast: more) {
  :root {
    --al-color-text-muted: var(--al-color-text);
    --al-color-line: var(--al-color-control);
    --al-color-line-strong: var(--al-color-text);
    --al-border-focus: 3px;
  }
}

/* Less transparency, when asked for (from Apple's HIG, "Reduce
   transparency"): translucent grounds become solid. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --al-color-backdrop: color-mix(in oklch, var(--al-color-text) 70%, var(--al-color-bg));
    --al-color-accent-wash: color-mix(in oklch, var(--al-color-accent) 12%, var(--al-color-surface));
  }
}

}

@layer al.layout {
/* css/layout.css */
/* Layout primitives: compose pages from a few rules rather than one-off
   margins. All of them use logical properties, so they mirror with dir. */

/* A page's content column: centred, capped, with a fluid side gutter. */
.al-page {
  inline-size: 100%;
  max-inline-size: calc(var(--al-size-page) + 2 * var(--al-space-gutter));
  margin-inline: auto;
  padding-inline: var(--al-space-gutter);
}

/* A website section: generous, fluid space above and below, a hairline between. */
.al-section { padding-block: var(--al-space-section); }
.al-section + .al-section { border-block-start: var(--al-border-hairline) solid var(--al-color-line); }

/* Stack: children one under another, evenly spaced (--al-stack-gap). */
.al-stack { display: flex; flex-direction: column; gap: var(--al-stack-gap, var(--al-space-4)); }
.al-stack--tight { --al-stack-gap: var(--al-space-2); }
.al-stack--loose { --al-stack-gap: var(--al-space-8); }

/* Cluster: children in a row that wraps, aligned to the start. */
.al-cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--al-cluster-gap, var(--al-space-3)); }
.al-cluster--end { justify-content: flex-end; }
.al-cluster--between { justify-content: space-between; }

/* Grid: as many columns of at least --al-grid-min as fit. */
.al-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(var(--al-grid-min, 16rem), 100%), 1fr));
  gap: var(--al-grid-gap, var(--al-space-4));
}

/* Split: a narrow and a wide column, stacked on small screens. */
.al-split {
  display: grid;
  grid-template-columns: minmax(16rem, 22rem) minmax(0, 1fr);
  gap: var(--al-space-8);
  align-items: start;
}
@media (max-width: 52rem) { .al-split { grid-template-columns: minmax(0, 1fr); } }

/* Reading width. */
.al-measure { max-inline-size: var(--al-size-measure); }

/* The app shell: a sidebar and the main column; the sidebar becomes a bar
   along the top on small screens (see components/sidebar.css). */
.al-shell {
  display: grid;
  grid-template-columns: var(--al-size-sidebar) minmax(0, 1fr);
  min-block-size: 100dvh;
}
.al-shell[data-sidebar="collapsed"] { grid-template-columns: var(--al-size-sidebar-collapsed) minmax(0, 1fr); }
.al-shell > main { min-inline-size: 0; padding: var(--al-space-8) var(--al-space-gutter); }
@media (max-width: 52rem) {
  .al-shell, .al-shell[data-sidebar="collapsed"] { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .al-shell > main { padding: var(--al-space-4); }
}

}

@layer al.components {
/* css/components/badge.css */
/* Badge: a short status beside something ("Active", "Experimental", "3").
   Sentence case, never capitals; its meaning in words, not colour alone.
   Tag: a label people add and remove; its colour is a dot, beside the name. */
.al-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1);
  min-block-size: 1.375rem;
  padding-inline: var(--al-space-2);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface-sunk);
  color: var(--al-color-text);
  font-size: var(--al-text-xs);
  font-weight: var(--al-weight-medium);
  line-height: 1;
  white-space: nowrap;
  vertical-align: middle;
}
.al-badge--accent { background: var(--al-color-accent-wash); color: var(--al-color-accent); }
.al-badge--success { background: var(--al-color-success-wash); color: var(--al-color-success); }
.al-badge--warning { background: var(--al-color-warning-wash); color: var(--al-color-warning); }
.al-badge--danger { background: var(--al-color-danger-wash); color: var(--al-color-danger); }
.al-badge--info { background: var(--al-color-info-wash); color: var(--al-color-info); }
.al-badge--outline { background: transparent; box-shadow: inset 0 0 0 1px var(--al-color-line-strong); }
.al-badge--count { font-variant-numeric: tabular-nums; min-inline-size: 1.375rem; justify-content: center; }

.al-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1-5);
  min-block-size: var(--al-size-target-min);
  padding-inline: var(--al-space-2);
  border: var(--al-border-hairline) solid var(--al-color-line);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  font-size: var(--al-text-sm);
}
.al-tag::before { content: ""; inline-size: 0.5rem; block-size: 0.5rem; border-radius: 50%; background: var(--al-tag-color, var(--al-color-chart-0)); flex: none; }
.al-tag__remove { all: unset; display: inline-grid; place-items: center; inline-size: var(--al-size-target-min); block-size: var(--al-size-target-min); margin-inline-end: calc(-1 * var(--al-space-2)); color: var(--al-color-text-muted); cursor: pointer; border-radius: var(--al-radius-sm); }
.al-tag__remove:hover { color: var(--al-color-danger); }
.al-tag__remove:focus-visible { outline: var(--al-border-focus) solid var(--al-color-focus); }

@media (forced-colors: active) { .al-badge { border: 1px solid CanvasText; } .al-tag::before { forced-color-adjust: none; } }

}

@layer al.components {
/* css/components/brand.css */
/* Brand: the mark (one tile of the pattern, in a square frame) beside the
   wordmark. The mark is always one colour (currentColor), never redrawn,
   never on a busy ground; its clear space is a quarter of its size. */
.al-lockup {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--al-color-text);
  text-decoration: none;
  font-family: var(--al-font-display);
  font-weight: var(--al-weight-medium);
  font-size: var(--al-text-lg);
  line-height: 1;
}
.al-lockup:hover { color: var(--al-color-text); }
.al-mark { inline-size: 1.5em; block-size: 1.5em; color: currentColor; flex: none; }
/* The wordmark's tracking is Latin's; in Arabic text it's still the Latin
   name, so it keeps it (lang="en" on the name). */
.al-wordmark { letter-spacing: 0.05em; }
.al-lockup__product { color: var(--al-color-text-muted); font-weight: var(--al-weight-regular); }
.al-lockup__product::before { content: "·"; margin-inline: 0.4em; }

}

@layer al.components {
/* css/components/breadcrumbs.css */
/* Back link and breadcrumbs (from GOV.UK): a way back, at the top of the
   page, before the title. A back link goes to the previous step of a flow;
   breadcrumbs show where a page sits in a hierarchy. Not both. Their
   chevrons point back the way text reads, so they mirror in right-to-left. */
.al-back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1);
  min-block-size: var(--al-size-target-min);
  color: var(--al-color-text);
  font-size: var(--al-text-sm);
  text-underline-offset: 0.2em;
}
.al-back-link::before {
  content: "";
  inline-size: 0.4em;
  block-size: 0.4em;
  border-block-end: 1.5px solid currentColor;
  border-inline-start: 1.5px solid currentColor;
  transform: rotate(45deg);
}
[dir="rtl"] .al-back-link::before { transform: rotate(-45deg); }
.al-breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--al-space-1) 0; margin: 0; padding: 0; list-style: none; font-size: var(--al-text-sm); }
.al-breadcrumbs li { display: inline-flex; align-items: center; min-block-size: var(--al-size-target-min); }
.al-breadcrumbs li + li::before {
  content: "";
  inline-size: 0.35em;
  block-size: 0.35em;
  margin-inline: var(--al-space-2);
  border-block-start: 1.5px solid var(--al-color-text-muted);
  border-inline-end: 1.5px solid var(--al-color-text-muted);
  transform: rotate(45deg);
}
[dir="rtl"] .al-breadcrumbs li + li::before { transform: rotate(-45deg); }
.al-breadcrumbs a { color: var(--al-color-text); text-underline-offset: 0.2em; }
.al-breadcrumbs [aria-current="page"] { color: var(--al-color-text-muted); }

}

@layer al.components {
/* css/components/bulk-bar.css */
/* Bulk action bar: once things are selected, what can be done to all of
   them, with how many are selected and a way to clear. Sticks to the end
   of the view; every action names its count ("Archive 3"). */
.al-bulk-bar {
  position: sticky;
  inset-block-end: var(--al-space-4);
  z-index: var(--al-z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--al-space-2) var(--al-space-4);
  padding: var(--al-space-2) var(--al-space-3) var(--al-space-2) var(--al-space-4);
  border-radius: var(--al-radius-md);
  background: var(--al-color-inverse);
  color: var(--al-color-on-inverse);
}
.al-bulk-bar__count { font-weight: var(--al-weight-semibold); font-variant-numeric: tabular-nums; margin-inline-end: auto; }
.al-bulk-bar .al-button { --_fg: var(--al-color-on-inverse); --_border: color-mix(in oklch, var(--al-color-on-inverse) 45%, transparent); --_hover-bg: color-mix(in oklch, var(--al-color-on-inverse) 12%, transparent); }
.al-bulk-bar .al-button:hover { border-color: var(--al-color-on-inverse); }
.al-bulk-bar .al-button--quiet { --_border: transparent; }
@media (forced-colors: active) { .al-bulk-bar { border: 1px solid CanvasText; } }

}

@layer al.components {
/* css/components/button.css */
/* Button: five kinds. One primary per view or dialog; the label names the
   action and its object ("Save offer", "Archive 3 processes"), never "OK".
   Busy (aria-busy) and unavailable (aria-disabled / disabled) look
   different, so "working on it" isn't mistaken for "not allowed". */
.al-button {
  --_bg: transparent;
  --_fg: var(--al-color-text);
  --_border: var(--al-color-control);
  --_hover-bg: var(--al-color-surface-sunk);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--al-space-2);
  min-block-size: var(--al-button-height);
  min-inline-size: var(--al-size-target-min);
  padding-block: 0;
  padding-inline: var(--al-button-padding-inline);
  border: var(--al-border-hairline) solid var(--_border);
  border-radius: var(--al-button-radius);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--al-font-sans);
  font-size: var(--al-text-base);
  font-weight: var(--al-weight-medium);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--al-duration-fast) var(--al-ease-standard),
    border-color var(--al-duration-fast) var(--al-ease-standard),
    color var(--al-duration-fast) var(--al-ease-standard);
}
.al-button:hover { background: var(--_hover-bg); border-color: var(--al-color-text); color: var(--_fg); }
.al-button:active { transform: translateY(1px); }

/* Primary: the one action the view is for. */
.al-button--primary {
  --_bg: var(--al-color-accent);
  --_fg: var(--al-color-on-accent);
  --_border: var(--al-color-accent);
  --_hover-bg: color-mix(in oklch, var(--al-color-accent) 86%, var(--al-color-text));
}
.al-button--primary:hover { border-color: var(--_hover-bg); }

/* Quiet: a text action, for secondary and repeated actions (Edit, Add a
   step, Clear). Still a button, and still a 24px target at least. */
.al-button--quiet {
  --_border: transparent;
  --_fg: var(--al-color-accent);
  --_hover-bg: transparent;
  min-block-size: var(--al-size-target-min);
  padding-inline: var(--al-space-1);
  font-weight: var(--al-weight-medium);
}
.al-button--quiet:hover { border-color: transparent; text-decoration: underline; text-underline-offset: 0.2em; }

/* Danger: destroys something; the label says what. As a secondary button, a
   quiet one, or (the last step of a confirmation) a filled one. */
.al-button--danger { --_fg: var(--al-color-danger); --_border: color-mix(in oklch, var(--al-color-danger) 60%, transparent); }
.al-button--danger:hover { border-color: var(--al-color-danger); }
.al-button--danger.al-button--quiet { --_border: transparent; }
.al-button--danger.al-button--primary {
  --_bg: var(--al-color-danger);
  --_fg: var(--al-color-on-accent);
  --_border: var(--al-color-danger);
  --_hover-bg: color-mix(in oklch, var(--al-color-danger) 86%, var(--al-color-text));
}

/* Icon: square, borderless until hovered; always with an aria-label. */
.al-button--icon {
  --_border: transparent;
  inline-size: var(--al-button-height);
  padding-inline: 0;
}

.al-button--sm {
  min-block-size: var(--al-button-height-sm);
  padding-inline: var(--al-space-3);
  font-size: var(--al-text-sm);
}
.al-button--sm.al-button--icon { inline-size: var(--al-button-height-sm); }
.al-button--sm.al-button--quiet { min-block-size: var(--al-size-target-min); padding-inline: var(--al-space-1); }

/* Unavailable: dimmed, explained nearby (or in aria-describedby). Prefer
   aria-disabled to disabled, so it stays focusable and its reason is read. */
.al-button:disabled,
.al-button[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}
.al-button:disabled:hover,
.al-button[aria-disabled="true"]:hover { background: var(--_bg); border-color: var(--_border); text-decoration: none; }

/* Busy: the label stays; a spinner follows it; it can't be pressed twice. */
.al-button[aria-busy="true"] { cursor: progress; pointer-events: none; }
.al-button[aria-busy="true"]::after {
  content: "";
  inline-size: 0.9em;
  block-size: 0.9em;
  border: 2px solid currentColor;
  border-inline-end-color: transparent;
  border-radius: 50%;
  animation: al-spin 0.8s linear infinite;
}

/* Bigger targets for fingers. */
@media (pointer: coarse) {
  .al-button { min-block-size: var(--al-size-target-touch); }
  .al-button--icon { inline-size: var(--al-size-target-touch); }
}

@media (forced-colors: active) {
  .al-button { border-color: ButtonText; }
  .al-button--quiet, .al-button--icon { border-color: transparent; text-decoration: underline; }
  .al-button--primary { border-width: 2px; }
}

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

}

@layer al.components {
/* css/components/card.css */
/* Card: a bordered surface that groups one thing. Panel: a section of a
   page marked by a rule above, not a box (most groupings need only this).
   Stat: a figure with its label and a note. No shadows: borders and
   surface steps carry depth. */
.al-card {
  display: flex;
  flex-direction: column;
  gap: var(--al-space-4);
  padding: var(--al-space-5);
  border: var(--al-border-hairline) solid var(--al-color-line);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  min-inline-size: 0;
}
.al-card__header { display: flex; flex-direction: column; gap: var(--al-space-1); }
.al-card__header h2, .al-card__header h3 { font-size: var(--al-text-lg); font-weight: var(--al-weight-medium); letter-spacing: var(--al-tracking-none); }
.al-card__description { font-size: var(--al-text-sm); color: var(--al-color-text-muted); }
.al-card--interactive { transition: border-color var(--al-duration-fast) var(--al-ease-standard); }
.al-card--interactive:hover { border-color: var(--al-color-control); }
.al-card--selected { border-color: var(--al-color-accent); background: color-mix(in oklch, var(--al-color-accent-wash) 60%, var(--al-color-surface)); }

.al-panel { display: flex; flex-direction: column; gap: var(--al-space-3); padding-block-start: var(--al-space-4); border-block-start: var(--al-border-hairline) solid var(--al-color-line-strong); }
.al-panel > h2 { font-size: var(--al-text-lg); font-weight: var(--al-weight-medium); letter-spacing: var(--al-tracking-none); }

.al-stat { display: flex; flex-direction: column; gap: var(--al-space-1); padding: var(--al-space-4); border: var(--al-border-hairline) solid var(--al-color-line); border-radius: var(--al-radius-sm); background: var(--al-color-surface); }
.al-stat__label { font-size: var(--al-text-sm); color: var(--al-color-text-muted); }
.al-stat__value { font-family: var(--al-font-display); font-size: var(--al-text-2xl); line-height: var(--al-leading-tight); font-variant-numeric: tabular-nums; }
.al-stat__note { font-size: var(--al-text-sm); color: var(--al-color-text-muted); }

@media (forced-colors: active) { .al-card, .al-stat { border-color: CanvasText; } }

}

@layer al.components {
/* css/components/character-count.css */
/* Character count (from GOV.UK): a limit on a text field, said as what's
   left ("You have 12 characters remaining") and, once over, by how much
   ("You have 8 characters too many"). Typing past it is allowed (so a
   paste isn't cut), and the field shows the error. js/count.js updates
   the visible text and, after a pause, a polite live region. */
.al-character-count__message { font-size: var(--al-text-sm); color: var(--al-color-text-muted); font-variant-numeric: tabular-nums; }
.al-character-count__message[data-over] { color: var(--al-color-danger); font-weight: var(--al-weight-medium); }

}

@layer al.components {
/* css/components/chart.css */
/* Charts, in plain HTML: thin marks in the chart colours (a fixed order,
   checked for colour blindness), a 2px gap between marks, one value axis,
   a key, a tooltip on every mark (focusable), and the numbers also in a
   table. Columns (grouped or stacked, with a marker and a target line),
   a split bar (parts of a whole) and a bullet chart (each against a
   target). In right-to-left, categories and time run with the reading
   direction and bars grow from the start side; numbers stay as written. */
.al-c0 { background: var(--al-color-chart-0); }
.al-c1 { background: var(--al-color-chart-1); }
.al-c2 { background: var(--al-color-chart-2); }
.al-c3 { background: var(--al-color-chart-3); }
.al-c4 { background: var(--al-color-chart-4); }
[class*="al-c"]:is(.al-chart__bar, .al-chart__seg, .al-split-bar__part, .al-bullet__bar, .al-swatch) { forced-color-adjust: none; }

.al-chart { display: flex; flex-direction: column; gap: var(--al-space-2); padding-inline-start: 3rem; }
.al-chart__plot { position: relative; block-size: 12rem; }
.al-chart__grid { position: absolute; inset-inline: 0; border-block-start: var(--al-border-hairline) solid var(--al-color-line); }
.al-chart__grid > span { position: absolute; inset-inline-start: -3rem; inset-block-start: -0.6em; inline-size: 2.6rem; text-align: end; font-size: var(--al-text-xs); color: var(--al-color-text-muted); font-variant-numeric: tabular-nums; }
.al-chart__slots { position: absolute; inset: 0; display: flex; gap: var(--al-space-4); }
.al-chart__slot { position: relative; flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 2px; }
.al-chart__bar { inline-size: min(28px, 100%); min-inline-size: 6px; border-start-start-radius: var(--al-radius-md); border-start-end-radius: var(--al-radius-md); }
.al-chart__stack { display: flex; flex-direction: column-reverse; inline-size: min(44px, 70%); block-size: 100%; }
.al-chart__seg { display: block; border-block-start: 2px solid var(--al-color-surface); }
.al-chart__seg:last-child { border-start-start-radius: var(--al-radius-md); border-start-end-radius: var(--al-radius-md); }
.al-chart__marker { position: absolute; inline-size: min(60px, 90%); border-block-start: 3px solid var(--al-color-chart-1); transform: translateY(50%); }
.al-chart__target { position: absolute; inset-inline: 0; border-block-start: 2px dashed var(--al-color-text); opacity: 0.75; }
.al-chart__axis { display: flex; gap: var(--al-space-4); }
.al-chart__axis > span { flex: 1; text-align: center; font-size: var(--al-text-xs); color: var(--al-color-text-muted); }
.al-chart [tabindex]:hover, .al-chart [tabindex]:focus-visible,
.al-split-bar [tabindex]:hover, .al-split-bar [tabindex]:focus-visible,
.al-bullet [tabindex]:hover, .al-bullet [tabindex]:focus-visible { filter: brightness(1.1); }

.al-key { display: flex; flex-wrap: wrap; gap: var(--al-space-1) var(--al-space-4); font-size: var(--al-text-sm); }
.al-key__item { display: inline-flex; align-items: center; gap: var(--al-space-1-5); }
.al-swatch { inline-size: 0.625rem; block-size: 0.625rem; border-radius: 2px; flex: none; }
.al-swatch--line { block-size: 0; inline-size: 0.875rem; border-block-start: 3px solid var(--al-color-chart-1); background: none; border-radius: 0; }
.al-swatch--target { block-size: 0; inline-size: 0.875rem; border-block-start: 2px dashed var(--al-color-text); background: none; border-radius: 0; }

.al-split-bar { display: flex; flex-direction: column; gap: var(--al-space-2); }
.al-split-bar__track { display: flex; gap: 2px; block-size: 1rem; }
.al-split-bar__part { display: block; min-inline-size: 2px; }
.al-split-bar__part:first-child { border-start-start-radius: var(--al-radius-md); border-end-start-radius: var(--al-radius-md); }
.al-split-bar__part:last-child { border-start-end-radius: var(--al-radius-md); border-end-end-radius: var(--al-radius-md); }

.al-bullet { display: grid; grid-template-columns: minmax(6rem, 10rem) minmax(0, 1fr) auto; gap: var(--al-space-2) var(--al-space-3); align-items: center; font-size: var(--al-text-sm); }
.al-bullet__row { display: contents; }
.al-bullet__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-bullet__track { position: relative; block-size: 1.125rem; border-radius: var(--al-radius-md); background: color-mix(in oklch, var(--al-color-chart-0) 45%, transparent); }
.al-bullet__bar { position: absolute; inset-block: 3px; inset-inline-start: 0; border-start-end-radius: var(--al-radius-md); border-end-end-radius: var(--al-radius-md); }
.al-bullet__target { position: absolute; inset-block: -4px; border-inline-start: 2px dashed var(--al-color-text); }
.al-bullet__value { text-align: end; white-space: nowrap; font-variant-numeric: tabular-nums; }
.al-bullet > .al-key { grid-column: 1 / -1; }
.al-more { color: var(--al-color-success); }
.al-less { color: var(--al-color-danger); }
@media (max-width: 40rem) {
  .al-bullet { grid-template-columns: minmax(0, 1fr) auto; }
  .al-bullet__label { grid-column: 1 / -1; }
}

/* Every chart has a title and a one-sentence summary of what it shows
   (from Apple's HIG): the takeaway first, in words, so the chart is
   understood without seeing it. Axis and grid labels are aria-hidden;
   marks carry their values with context ("£12,480 in March, Direct"). */
.al-chart__title { font-size: var(--al-text-sm); font-weight: var(--al-weight-medium); }
.al-chart__summary { font-size: var(--al-text-sm); color: var(--al-color-text-muted); max-inline-size: var(--al-size-measure); }

}

@layer al.components {
/* css/components/choice.css */
/* Choices: checkboxes and radios are native inputs in the accent colour,
   with a 24px row so the label is the target; a switch is a checkbox with
   role="switch" for settings that apply at once. */
.al-choice {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--al-space-2);
  min-block-size: var(--al-size-target-min);
  padding-block: var(--al-space-0-5);
  cursor: pointer;
  font-size: var(--al-text-base);
}
.al-choice > input:is([type="checkbox"], [type="radio"]):not(.al-switch) {
  inline-size: 1.125rem;
  block-size: 1.125rem;
  margin: 0.15em 0 0;
  flex: none;
  accent-color: var(--al-color-accent);
  cursor: pointer;
}
.al-choice__text { display: flex; flex-direction: column; gap: var(--al-space-0-5); }
.al-choice__hint { font-size: var(--al-text-sm); color: var(--al-color-text-muted); }
.al-choice:has(input:disabled) { opacity: 0.5; cursor: not-allowed; }
.al-choices { display: flex; flex-direction: column; gap: var(--al-space-1); }
.al-choices--inline { flex-direction: row; flex-wrap: wrap; gap: var(--al-space-1) var(--al-space-5); }

/* Switch: on or off, now. */
.al-switch {
  appearance: none;
  position: relative;
  inline-size: 2.25rem;
  block-size: 1.25rem;
  margin: 0.1em 0 0;
  flex: none;
  border: var(--al-border-hairline) solid var(--al-color-control);
  border-radius: var(--al-radius-full);
  background: var(--al-color-surface-sunk);
  cursor: pointer;
  transition: background-color var(--al-duration-fast) var(--al-ease-standard);
}
.al-switch::before {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 0.125rem;
  inline-size: 0.875rem;
  block-size: 0.875rem;
  border-radius: 50%;
  background: var(--al-color-text-muted);
  transform: translateY(-50%);
  transition: inset-inline-start var(--al-duration-fast) var(--al-ease-standard), background-color var(--al-duration-fast) var(--al-ease-standard);
}
.al-switch:checked { background: var(--al-color-accent); border-color: var(--al-color-accent); }
.al-switch:checked::before { inset-inline-start: calc(100% - 1rem); background: var(--al-color-on-accent); }

@media (forced-colors: active) {
  .al-switch { border-color: ButtonText; }
  .al-switch::before { background: ButtonText; forced-color-adjust: none; }
  .al-switch:checked { background: Highlight; }
  .al-switch:checked::before { background: HighlightText; }
}

}

@layer al.components {
/* css/components/confirmation.css */
/* Confirmation (from GOV.UK): the end of a flow that sends or creates
   something: a clear title saying it's done, the reference people may
   need, then what happens next (in the page, under it). The one large,
   coloured block in the system; one per page. */
.al-confirmation {
  display: flex;
  flex-direction: column;
  gap: var(--al-space-2);
  padding: var(--al-space-8) var(--al-space-6);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-accent);
  color: var(--al-color-on-accent);
  text-align: center;
}
.al-confirmation__title { font-size: var(--al-text-2xl); line-height: var(--al-leading-heading); color: inherit; }
.al-confirmation__body { font-size: var(--al-text-lg); }
.al-confirmation__body strong { display: block; font-weight: var(--al-weight-semibold); font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
@media (forced-colors: active) { .al-confirmation { border: 2px solid CanvasText; } }

}

@layer al.components {
/* css/components/date-input.css */
/* Date input (from GOV.UK): day, month and year as three short number
   fields, for dates people know (a birthday, a start date on a document).
   A calendar picker is for choosing a date near today; the native
   <input type=date> remains for that. Fields keep their order in
   right-to-left, as the date is written. */
.al-date-input { display: flex; flex-wrap: wrap; gap: var(--al-space-3); }
.al-date-input .al-field { flex: none; }
.al-date-input .al-input { inline-size: 3.5rem; }
.al-date-input__year .al-input { inline-size: 5rem; }

}

@layer al.components {
/* css/components/dialog.css */
/* Dialog: a native <dialog> opened with showModal(), for a decision that
   needs the person's attention now. Title, body, then actions on the
   trailing side: the primary last, Cancel before it. Escape and Cancel
   close it; focus returns to what opened it.
   Confirmation is only for what can't be undone (and says why); anything
   reversible is done at once, with Undo in a toast instead. */
.al-dialog {
  inline-size: min(var(--al-size-dialog-sm), calc(100vw - 2 * var(--al-space-4)));
  max-block-size: min(85dvh, 48rem);
  padding: 0;
  border: var(--al-border-hairline) solid var(--al-color-line);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  color: var(--al-color-text);
}
.al-dialog--md { inline-size: min(var(--al-size-dialog-md), calc(100vw - 2 * var(--al-space-4))); }
.al-dialog::backdrop { background: var(--al-color-backdrop); }
.al-dialog[open] { animation: al-dialog-in var(--al-duration-base) var(--al-ease-standard); }
.al-dialog > form, .al-dialog__inner { display: flex; flex-direction: column; gap: var(--al-space-4); padding: var(--al-space-6); }
.al-dialog__title { font-size: var(--al-text-xl); }
.al-dialog__body { display: flex; flex-direction: column; gap: var(--al-space-4); }
.al-dialog__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--al-space-3); padding-block-start: var(--al-space-2); }
@keyframes al-dialog-in { from { opacity: 0; transform: translateY(0.5rem) scale(0.99); } }
@media (forced-colors: active) { .al-dialog { border-color: CanvasText; } }

/* On small screens a dialog is a sheet (from Apple's HIG): anchored to the
   bottom, full width, within thumb's reach. Its actions stack full-width,
   with the way out (Cancel, first in the markup) at the bottom, nearest
   the thumb, as iOS does: a destructive action is never the easy tap. */
@media (max-width: 40rem) {
  .al-dialog, .al-dialog--md {
    inline-size: 100%;
    max-inline-size: 100%;
    margin-block: auto 0;
    margin-inline: 0;
    max-block-size: 90dvh;
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    border-inline: 0;
    border-block-end: 0;
  }
  .al-dialog__actions { flex-direction: column-reverse; align-items: stretch; }
  .al-dialog[open] { animation-name: al-sheet-in; }
}
@keyframes al-sheet-in { from { transform: translateY(1.5rem); opacity: 0; } }

/* Leaving with unsaved changes (from Apple's HIG: confirm before closing a
   modal view that would lose what people made): not a second dialog, but
   a bar inside this one, offering to keep editing or discard.
   js/dialog.js shows it when Escape or Cancel would lose changes. */
.al-dialog__discard {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--al-space-2) var(--al-space-3);
  padding: var(--al-space-3) var(--al-space-4);
  border-inline-start: 3px solid var(--al-color-warning);
  background: var(--al-color-warning-wash);
  font-size: var(--al-text-sm);
}
.al-dialog__discard[hidden] { display: none; }
.al-dialog__discard > p { margin: 0; margin-inline-end: auto; font-weight: var(--al-weight-medium); }

}

@layer al.components {
/* css/components/disclosure.css */
/* Disclosure: a native <details>, for what's secondary. Its summary can
   show what's chosen inside ("Plan 2", "5%, salary sacrifice"), so it
   doesn't need opening to be checked. The chevron points the way text
   reads, and down when open. */
.al-disclosure { border-block-start: var(--al-border-hairline) solid var(--al-color-line); }
.al-disclosure > summary {
  display: flex;
  align-items: baseline;
  gap: var(--al-space-2);
  min-block-size: var(--al-size-target-min);
  padding-block: var(--al-space-2);
  list-style: none;
  cursor: pointer;
  font-weight: var(--al-weight-medium);
}
.al-disclosure > summary::-webkit-details-marker { display: none; }
.al-disclosure > summary::before {
  content: "";
  flex: none;
  inline-size: 0.45em;
  block-size: 0.45em;
  margin-inline-end: 0.15em;
  border-block-end: 1.5px solid currentColor;
  border-inline-end: 1.5px solid currentColor;
  transform: translateY(-0.1em) rotate(-45deg);
  transition: transform var(--al-duration-fast) var(--al-ease-standard);
}
[dir="rtl"] .al-disclosure > summary::before { transform: translateY(-0.1em) rotate(45deg); }
.al-disclosure[open] > summary::before { transform: translateY(-0.25em) rotate(45deg); }
[dir="rtl"] .al-disclosure[open] > summary::before { transform: translateY(-0.25em) rotate(-45deg); }
.al-disclosure__value {
  margin-inline-start: auto;
  color: var(--al-color-text-muted);
  font-weight: var(--al-weight-regular);
  font-size: var(--al-text-sm);
  text-align: end;
}
.al-disclosure__body { padding-block: var(--al-space-2) var(--al-space-4); display: flex; flex-direction: column; gap: var(--al-space-3); }
.al-disclosure:hover > summary { color: var(--al-color-accent); }
.al-disclosure:hover > summary .al-disclosure__value { color: var(--al-color-text-muted); }

}

@layer al.components {
/* css/components/empty-state.css */
/* Empty state: why there's nothing here, and the next action to take. Never
   a dead end. */
.al-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--al-space-3);
  padding: var(--al-space-8) var(--al-space-5);
  border: var(--al-border-hairline) dashed var(--al-color-line-strong);
  border-radius: var(--al-radius-sm);
}
.al-empty__title { font-size: var(--al-text-lg); font-weight: var(--al-weight-medium); letter-spacing: var(--al-tracking-none); }
.al-empty__text { color: var(--al-color-text-muted); max-inline-size: var(--al-size-measure); }

}

@layer al.components {
/* css/components/error-summary.css */
/* Error summary (from GOV.UK): when a form is submitted with errors, a box
   at the top of the main content lists every error, in the order of the
   fields, each a link that moves focus to its field. It takes focus when it
   appears, and the page's title gains "Error: " so screen readers say so at
   once. Messages are worded exactly as the ones beside the fields. Shown
   even for one error. js/validate.js does the wiring. */
.al-error-summary {
  display: flex;
  flex-direction: column;
  gap: var(--al-space-2);
  padding: var(--al-space-4) var(--al-space-5);
  border: var(--al-border-focus) solid var(--al-color-danger);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
}
.al-error-summary:focus { outline: var(--al-border-focus) solid var(--al-color-focus); outline-offset: var(--al-border-focus-offset); }
.al-error-summary__title { display: flex; align-items: center; gap: var(--al-space-2); font-size: var(--al-text-lg); font-weight: var(--al-weight-medium); color: var(--al-color-text); letter-spacing: var(--al-tracking-none); }
.al-error-summary__title > .al-icon { color: var(--al-color-danger); }
.al-error-summary__list { margin: 0; padding-inline-start: var(--al-space-5); display: flex; flex-direction: column; gap: var(--al-space-1); }
.al-error-summary__list a { color: var(--al-color-danger); font-weight: var(--al-weight-medium); text-underline-offset: 0.2em; }
.al-error-summary__list a:hover { text-decoration-thickness: 2px; }
@media (forced-colors: active) { .al-error-summary { border-color: Mark; } }

}

@layer al.components {
/* css/components/field.css */
/* Field: a label, the control, a hint, and an error, in that order. The
   label is always visible (a placeholder is an example, never the label);
   optional fields say so, rather than marking required ones (GOV.UK). The
   error says what's wrong and how to fix it, and is tied to the control
   with aria-describedby; the control gets aria-invalid. */
.al-field { display: flex; flex-direction: column; gap: var(--al-space-1-5); min-inline-size: 0; }

.al-label { font-size: var(--al-text-sm); font-weight: var(--al-weight-medium); color: var(--al-color-text); }
.al-label__optional { font-weight: var(--al-weight-regular); color: var(--al-color-text-muted); }
.al-hint { font-size: var(--al-text-sm); color: var(--al-color-text-muted); line-height: var(--al-leading-ui); }
.al-error {
  display: flex; gap: var(--al-space-1-5); align-items: flex-start;
  font-size: var(--al-text-sm); font-weight: var(--al-weight-medium); color: var(--al-color-danger);
}

.al-input, .al-select, .al-textarea {
  inline-size: 100%;
  min-inline-size: 0;
  min-block-size: var(--al-field-height);
  padding-block: var(--al-space-2);
  padding-inline: var(--al-field-padding-inline);
  border: var(--al-border-hairline) solid var(--al-color-control);
  border-radius: var(--al-field-radius);
  background: var(--al-color-surface);
  color: var(--al-color-text);
  font-size: var(--al-text-base);
  line-height: var(--al-leading-ui);
  transition: border-color var(--al-duration-fast) var(--al-ease-standard);
}
.al-textarea { resize: vertical; min-block-size: 6rem; }
.al-input::placeholder, .al-textarea::placeholder { color: var(--al-color-text-muted); opacity: 1; }
.al-input:hover, .al-select:hover, .al-textarea:hover { border-color: var(--al-color-text); }
/* Focus: an outline and the border in the accent, however the field was
   reached (a field being typed into always shows it; date inputs focus
   their parts, which :focus-visible misses). */
.al-input:focus, .al-input:focus-within, .al-select:focus, .al-textarea:focus {
  border-color: var(--al-color-focus);
  outline: var(--al-border-focus) solid var(--al-color-focus);
  outline-offset: 0;
}
.al-input[aria-invalid="true"], .al-select[aria-invalid="true"], .al-textarea[aria-invalid="true"] {
  border-color: var(--al-color-danger);
  border-inline-start-width: 3px;
}
.al-input:disabled, .al-select:disabled, .al-textarea:disabled { opacity: 0.5; cursor: not-allowed; }
.al-input[readonly] { background: var(--al-color-surface-sunk); }

/* Numbers and money: right-aligned in their own direction, tabular. */
.al-input--number { font-variant-numeric: tabular-nums; }
.al-input[type="date"], .al-input--number { direction: ltr; text-align: start; }
:lang(ar) .al-input--number, :lang(ar) .al-input[type="date"] { text-align: end; }

/* Affix: a currency, unit or period beside the value ("£", "a year"). */
.al-affix { display: flex; align-items: stretch; min-inline-size: 0; }
.al-affix > .al-input { border-radius: 0; flex: 1; }
.al-affix > :first-child { border-start-start-radius: var(--al-field-radius); border-end-start-radius: var(--al-field-radius); }
.al-affix > :last-child { border-start-end-radius: var(--al-field-radius); border-end-end-radius: var(--al-field-radius); }
.al-affix__text {
  display: inline-flex; align-items: center;
  padding-inline: var(--al-space-3);
  border: var(--al-border-hairline) solid var(--al-color-control);
  background: var(--al-color-surface-sunk);
  color: var(--al-color-text-muted);
  font-size: var(--al-text-sm);
  white-space: nowrap;
}
.al-affix > .al-affix__text:first-child { border-inline-end: 0; }
.al-affix > .al-affix__text:last-child { border-inline-start: 0; }
.al-affix > .al-select { inline-size: auto; flex: none; border-radius: 0; }

/* A group of fields under one question. */
.al-fieldset { border: 0; margin: 0; padding: 0; min-inline-size: 0; display: flex; flex-direction: column; gap: var(--al-space-3); }
.al-fieldset > legend { padding: 0; margin-block-end: var(--al-space-1); font-size: var(--al-text-sm); font-weight: var(--al-weight-medium); }

/* Changed but not saved: a mark on the field, in words as well as colour. */
.al-field[data-changed] .al-input, .al-field[data-changed] .al-select, .al-field[data-changed] .al-textarea {
  border-inline-start: 3px solid var(--al-color-warning);
}
.al-field__changed { font-size: var(--al-text-xs); color: var(--al-color-warning); font-weight: var(--al-weight-medium); }

@media (pointer: coarse) { .al-input, .al-select { min-block-size: var(--al-size-target-touch); } }
@media (forced-colors: active) {
  .al-input, .al-select, .al-textarea, .al-affix__text { border-color: FieldText; }
  .al-input[aria-invalid="true"], .al-select[aria-invalid="true"], .al-textarea[aria-invalid="true"] { border-color: Mark; }
}

}

@layer al.components {
/* css/components/footer.css */
/* Footer: who made it, where to go next, the legal line. Quiet, and the same
   on every page. */
.al-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--al-space-4) var(--al-space-8);
  padding-block: var(--al-space-8);
  border-block-start: var(--al-border-hairline) solid var(--al-color-line);
  color: var(--al-color-text-muted);
  font-size: var(--al-text-sm);
}
.al-footer__links { display: flex; flex-wrap: wrap; gap: var(--al-space-2) var(--al-space-5); list-style: none; margin: 0; padding: 0; }
.al-footer__links a { color: inherit; text-decoration: none; }
.al-footer__links a:hover { color: var(--al-color-accent); text-decoration: underline; }
.al-footer__legal { flex-basis: 100%; font-size: var(--al-text-xs); }

}

@layer al.components {
/* css/components/icon.css */
/* Icon: line icons on a 24-unit grid, 1.5 stroke, square ends, drawn in
   the text's colour. Decorative icons are aria-hidden; an icon alone in a
   control gets its name from the control (aria-label). */
.al-icon {
  inline-size: var(--al-size-icon);
  block-size: var(--al-size-icon);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
  vertical-align: -0.2em;
  flex: none;
}
.al-icon--sm { inline-size: 1rem; block-size: 1rem; }
.al-icon--lg { inline-size: 1.5rem; block-size: 1.5rem; }

}

@layer al.components {
/* css/components/kbd.css */
/* Keyboard keys, and the list of shortcuts (opened with "?"). Shortcuts are
   single keys only when focus isn't in a text field, and can be turned off
   (WCAG 2.1.4). */
.al-kbd {
  display: inline-block;
  min-inline-size: 1.5em;
  padding: 0.05em 0.4em;
  border: var(--al-border-hairline) solid var(--al-color-line-strong);
  border-block-end-width: 2px;
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  font-family: var(--al-font-mono);
  font-size: var(--al-text-xs);
  line-height: 1.5;
  text-align: center;
  white-space: nowrap;
}
.al-shortcuts { display: grid; grid-template-columns: auto 1fr; gap: var(--al-space-2) var(--al-space-5); align-items: baseline; margin: 0; }
.al-shortcuts dt { display: flex; gap: var(--al-space-1); }
.al-shortcuts dd { margin: 0; }

}

@layer al.components {
/* css/components/menu.css */
/* Menu: actions on one thing, behind a button ("More", "Move to…"), in a
   native popover. Arrow keys move between items, Escape closes and returns
   focus (js/menu.js, the ARIA APG menu button pattern). Destructive items
   last, after a separator. Positioned next to its button. */
.al-menu {
  margin: 0;
  min-inline-size: 12rem;
  max-inline-size: 20rem;
  padding: var(--al-space-1);
  border: var(--al-border-hairline) solid var(--al-color-line);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  color: var(--al-color-text);
  inset: auto;
}
.al-menu__item {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--al-space-2);
  inline-size: 100%;
  min-block-size: var(--al-size-control-sm);
  padding-inline: var(--al-space-3);
  border-radius: var(--al-radius-sm);
  cursor: pointer;
  text-align: start;
}
.al-menu__item:hover, .al-menu__item:focus-visible { background: var(--al-color-surface-sunk); outline: none; }
.al-menu__item:focus-visible { box-shadow: inset 0 0 0 var(--al-border-focus) var(--al-color-focus); }
.al-menu__item[aria-checked="true"]::after { content: "✓"; margin-inline-start: auto; color: var(--al-color-accent); }
.al-menu__item--danger { color: var(--al-color-danger); }
.al-menu__separator { block-size: 1px; margin-block: var(--al-space-1); background: var(--al-color-line); border: 0; }
.al-menu__label { padding: var(--al-space-1-5) var(--al-space-3) var(--al-space-1); font-size: var(--al-text-xs); color: var(--al-color-text-muted); }
@media (forced-colors: active) {
  .al-menu { border-color: CanvasText; }
  .al-menu__item:focus-visible { outline: 2px solid Highlight; box-shadow: none; }
}

}

@layer al.components {
/* css/components/nav.css */
/* Tabs and section navigation. As links between pages, mark the current one
   with aria-current="page"; as in-page tabs, role="tablist" with
   aria-selected and arrow-key movement (js/tabs.js, the ARIA APG pattern).
   They look the same: the chosen one is underlined in the accent. */
.al-tabs { display: flex; gap: var(--al-space-1); border-block-end: var(--al-border-hairline) solid var(--al-color-line); overflow-x: auto; scrollbar-width: thin; }
.al-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1-5);
  min-block-size: var(--al-size-control-md);
  padding-inline: var(--al-space-3);
  border: 0;
  background: none;
  color: var(--al-color-text-muted);
  font-weight: var(--al-weight-medium);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.al-tab:hover { color: var(--al-color-text); }
.al-tab[aria-current="page"], .al-tab[aria-selected="true"] { color: var(--al-color-text); }
.al-tab[aria-current="page"]::after, .al-tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  inset-inline: var(--al-space-2);
  inset-block-end: -1px;
  block-size: 2px;
  background: var(--al-color-accent);
}
.al-tab__count { font-size: var(--al-text-xs); color: var(--al-color-text-muted); font-variant-numeric: tabular-nums; }
.al-tabpanel { padding-block-start: var(--al-space-5); }

@media (forced-colors: active) {
  .al-tab[aria-current="page"]::after, .al-tab[aria-selected="true"]::after { background: Highlight; forced-color-adjust: none; }
}

}

@layer al.components {
/* css/components/notice.css */
/* Notice: a message in the page. Four kinds, each with an icon and a title
   word, so the kind isn't told by colour alone: info (neutral), success
   (done), warning (needs attention), danger (an error; only for errors).
   role="status" for news, role="alert" only for errors that block.
   Side-effect note: what else an action will change, said before it's
   taken ("This also removes it from 2 offers"). */
.al-notice {
  --_status: var(--al-color-info);
  --_wash: var(--al-color-info-wash);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--al-space-1) var(--al-space-3);
  align-items: start;
  padding: var(--al-space-3) var(--al-space-4);
  border-inline-start: 3px solid var(--_status);
  border-radius: var(--al-radius-sm);
  background: var(--_wash);
}
.al-notice > .al-icon { color: var(--_status); margin-block-start: 0.15em; }
.al-notice__body { display: flex; flex-direction: column; gap: var(--al-space-1); min-inline-size: 0; }
.al-notice__title { font-weight: var(--al-weight-semibold); }
.al-notice__actions { display: flex; flex-wrap: wrap; gap: var(--al-space-2) var(--al-space-4); margin-block-start: var(--al-space-1); }
.al-notice--success { --_status: var(--al-color-success); --_wash: var(--al-color-success-wash); }
.al-notice--warning { --_status: var(--al-color-warning); --_wash: var(--al-color-warning-wash); }
.al-notice--danger { --_status: var(--al-color-danger); --_wash: var(--al-color-danger-wash); }

.al-side-effect {
  display: flex;
  gap: var(--al-space-2);
  align-items: flex-start;
  padding-inline-start: var(--al-space-3);
  border-inline-start: 2px solid var(--al-color-line-strong);
  color: var(--al-color-text-muted);
  font-size: var(--al-text-sm);
}
.al-side-effect strong { color: var(--al-color-text); font-weight: var(--al-weight-medium); }

@media (forced-colors: active) { .al-notice { border: 1px solid CanvasText; border-inline-start-width: 3px; } }

}

@layer al.components {
/* css/components/page-head.css */
/* Page head: the page's title, then (on the trailing side) what applies to
   the whole page: its settings, views and primary action. */
.al-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--al-space-3) var(--al-space-4);
  padding-block-end: var(--al-space-4);
  margin-block-end: var(--al-space-6);
  border-block-end: var(--al-border-hairline) solid var(--al-color-line);
}
.al-page-head > h1 { font-size: var(--al-text-3xl); }
.al-page-head__end { margin-inline-start: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--al-space-3); }
.al-page-head__lede { flex-basis: 100%; color: var(--al-color-text-muted); max-inline-size: var(--al-size-measure); }
@media (max-width: 52rem) { .al-page-head__end { margin-inline-start: 0; inline-size: 100%; } }

}

@layer al.components {
/* css/components/pattern.css */
/* Pattern: the tessellation of the mark's tile, as background texture only
   (js/pattern.js draws it as SVG). It fades out behind text: from the
   trailing side towards the start, mirrored in right-to-left. Never behind
   body copy at full strength; aria-hidden; still under reduced motion. */
.al-pattern { position: relative; isolation: isolate; overflow: hidden; }
.al-pattern__art {
  position: absolute;
  inset: 0;
  z-index: -1;
  color: var(--al-color-pattern-line);
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0 40%, black 80%);
  mask-image: linear-gradient(to right, transparent 0 40%, black 80%);
}
[dir="rtl"] .al-pattern__art {
  -webkit-mask-image: linear-gradient(to left, transparent 0 40%, black 80%);
  mask-image: linear-gradient(to left, transparent 0 40%, black 80%);
}
.al-pattern__art svg { inline-size: 100%; block-size: 100%; display: block; }
.al-pattern--full .al-pattern__art { -webkit-mask-image: none; mask-image: none; }
@media (max-width: 52rem) {
  .al-pattern__art, [dir="rtl"] .al-pattern__art {
    -webkit-mask-image: linear-gradient(to bottom, black 0 30%, transparent 70%);
    mask-image: linear-gradient(to bottom, black 0 30%, transparent 70%);
  }
}
@media (forced-colors: active) { .al-pattern__art { display: none; } }

}

@layer al.components {
/* css/components/phase-banner.css */
/* Phase banner (from GOV.UK): under the header, says the product (or a
   part of it) is new or changing, and asks for feedback. The badge names
   the phase (Alpha, Beta, Preview, Experimental); the sentence says what
   that means for the person. */
.al-phase-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--al-space-1) var(--al-space-3);
  padding-block: var(--al-space-2);
  border-block-end: var(--al-border-hairline) solid var(--al-color-line);
  font-size: var(--al-text-sm);
}
.al-phase-banner__text { color: var(--al-color-text-muted); }

}

@layer al.components {
/* css/components/progress.css */
/* Loading, where it happens: a spinner in the control or region that's
   busy, a skeleton where content will appear, a bar for progress that can
   be measured. Busy regions get aria-busy="true". Without motion, the
   spinner is a still ring and skeletons don't shimmer. */
.al-spinner {
  display: inline-block;
  inline-size: 1em;
  block-size: 1em;
  border: 2px solid currentColor;
  border-inline-end-color: transparent;
  border-radius: 50%;
  vertical-align: -0.15em;
  animation: al-spin 0.8s linear infinite;
}
.al-skeleton {
  display: block;
  block-size: 1em;
  border-radius: var(--al-radius-sm);
  background: linear-gradient(90deg, var(--al-color-surface-sunk) 30%, color-mix(in oklch, var(--al-color-surface-sunk) 50%, var(--al-color-surface)) 50%, var(--al-color-surface-sunk) 70%);
  background-size: 300% 100%;
  animation: al-shimmer 1.4s ease-in-out infinite;
}
.al-progress {
  appearance: none;
  inline-size: 100%;
  block-size: 0.5rem;
  border: 0;
  border-radius: var(--al-radius-full);
  background: var(--al-color-surface-sunk);
  overflow: hidden;
}
.al-progress::-webkit-progress-bar { background: var(--al-color-surface-sunk); }
.al-progress::-webkit-progress-value { background: var(--al-color-accent); border-radius: var(--al-radius-full); }
.al-progress::-moz-progress-bar { background: var(--al-color-accent); }
@keyframes al-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@keyframes al-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .al-spinner { border-inline-end-color: currentColor; border-style: dotted; } }
@media (forced-colors: active) { .al-progress { border: 1px solid CanvasText; } }

}

@layer al.components {
/* css/components/save-state.css */
/* Save state: whether what's been changed is kept. Unsaved → Saving… →
   Saved, or Couldn't save with Retry; always in words, beside what it's
   about, and in a polite live region (role="status").
   Stale: a figure or region that no longer matches its inputs (being worked
   out again, or failed): marked, not silently replaced. */
.al-save-state {
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1-5);
  min-block-size: var(--al-size-target-min);
  font-size: var(--al-text-sm);
  color: var(--al-color-text-muted);
}
.al-save-state::before { content: ""; inline-size: 0.5rem; block-size: 0.5rem; border-radius: 50%; flex: none; background: currentColor; }
.al-save-state[data-state="unsaved"] { color: var(--al-color-warning); }
.al-save-state[data-state="saving"]::before {
  inline-size: 0.8em; block-size: 0.8em; background: none;
  border: 2px solid currentColor; border-inline-end-color: transparent;
  animation: al-spin 0.8s linear infinite;
}
.al-save-state[data-state="saved"] { color: var(--al-color-success); }
.al-save-state[data-state="saved"]::before { background: none; inline-size: 0.35em; block-size: 0.7em; border-radius: 0; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg) translateY(-0.1em); }
.al-save-state[data-state="failed"] { color: var(--al-color-danger); }

.al-stale { position: relative; opacity: 0.55; transition: opacity var(--al-duration-fast) var(--al-ease-standard); }
.al-stale-note {
  display: inline-flex;
  align-items: center;
  gap: var(--al-space-1-5);
  font-size: var(--al-text-sm);
  color: var(--al-color-text-muted);
}
@keyframes al-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .al-save-state[data-state="saving"]::before { border-inline-end-color: currentColor; border-style: dotted; } }

/* Rolled back: an optimistic change (shown at once, before the server
   agreed) that failed, put back where it was, saying what didn't happen,
   why, and offering Retry. It stays until the person acts or retries. */
.al-rolled-back {
  border-inline-start: var(--al-border-focus) solid var(--al-color-danger);
  background: var(--al-color-danger-wash);
  padding-inline-start: var(--al-space-3);
}
.al-rollback-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--al-space-1-5) var(--al-space-3);
  margin: 0;
  font-size: var(--al-text-sm);
  color: var(--al-color-danger);
}
.al-rollback-note > .al-icon { flex: none; }
@media (forced-colors: active) { .al-rolled-back { border-inline-start-color: Mark; } }

}

@layer al.components {
/* css/components/search.css */
/* Search field: an input with its icon and a Clear button once there's
   text. role="search" on its form; results update as people type, with
   the count announced politely. "/" focuses it (see kbd). */
.al-search { position: relative; display: flex; align-items: center; min-inline-size: 0; }
.al-search > .al-icon { position: absolute; inset-inline-start: var(--al-space-3); color: var(--al-color-text-muted); pointer-events: none; }
.al-search > .al-input { padding-inline-start: calc(var(--al-space-3) * 2 + var(--al-size-icon)); padding-inline-end: var(--al-size-control-sm); }
.al-search > .al-input::-webkit-search-cancel-button { display: none; }
.al-search__clear {
  all: unset;
  position: absolute;
  inset-inline-end: var(--al-space-1);
  display: inline-grid;
  place-items: center;
  inline-size: var(--al-size-control-sm);
  block-size: var(--al-size-control-sm);
  border-radius: var(--al-radius-sm);
  color: var(--al-color-text-muted);
  cursor: pointer;
}
.al-search__clear:hover { color: var(--al-color-text); }
.al-search__clear:focus-visible { outline: var(--al-border-focus) solid var(--al-color-focus); }
.al-search > .al-input:placeholder-shown + .al-search__clear { display: none; }

}

@layer al.components {
/* css/components/segmented.css */
/* Segmented control: one of two to five short options, all visible. Made of
   radios (a choice that's part of a form) or buttons with aria-pressed (a
   view switch); either way, one is always on. */
.al-segmented {
  display: inline-flex;
  border: var(--al-border-hairline) solid var(--al-color-control);
  border-radius: var(--al-radius-sm);
  background: var(--al-color-surface);
  padding: 0;
  margin: 0;
  min-inline-size: 0;
  align-self: flex-start;
  max-inline-size: 100%;
}
.al-segmented--fill { display: flex; align-self: stretch; }
.al-segmented--fill > * { flex: 1; }
.al-segmented > label, .al-segmented > button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--al-size-control-sm);
  padding-inline: var(--al-space-3);
  border: 0;
  background: transparent;
  color: var(--al-color-text-muted);
  font-size: var(--al-text-sm);
  font-weight: var(--al-weight-medium);
  cursor: pointer;
  white-space: nowrap;
}
.al-segmented > * + * { border-inline-start: var(--al-border-hairline) solid var(--al-color-control); }
.al-segmented > label > input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.al-segmented > label:hover, .al-segmented > button:hover { color: var(--al-color-text); background: var(--al-color-surface-sunk); }
.al-segmented > label:has(input:checked), .al-segmented > button[aria-pressed="true"] {
  background: var(--al-color-text);
  color: var(--al-color-bg);
}
.al-segmented > label:has(input:focus-visible) { outline: var(--al-border-focus) solid var(--al-color-focus); outline-offset: 2px; z-index: 1; }

@media (forced-colors: active) {
  .al-segmented { border-color: ButtonText; }
  .al-segmented > label:has(input:checked), .al-segmented > button[aria-pressed="true"] { background: Highlight; color: HighlightText; forced-color-adjust: none; }
}

}

@layer al.components {
/* css/components/sidebar.css */
/* Sidebar: the app's sections, in a column; collapsible to icons, with each
   name kept for screen readers and shown on hover. Under the md breakpoint
   it becomes a scrolling bar along the top. */
.al-sidebar {
  position: sticky;
  inset-block-start: 0;
  block-size: 100dvh;
  display: flex;
  flex-direction: column;
  gap: var(--al-space-6);
  padding: var(--al-space-5) var(--al-space-3);
  background: var(--al-color-surface-sunk);
  border-inline-end: var(--al-border-hairline) solid var(--al-color-line);
  overflow-y: auto;
}
.al-sidebar__nav { display: flex; flex-direction: column; gap: var(--al-space-0-5); }
.al-sidebar__link {
  display: flex;
  align-items: center;
  gap: var(--al-space-3);
  min-block-size: var(--al-size-control-sm);
  padding-inline: var(--al-space-2);
  border-inline-start: 2px solid transparent;
  border-radius: var(--al-radius-sm);
  color: var(--al-color-text-muted);
  text-decoration: none;
  white-space: nowrap;
}
/* A button among the links (Collapse) looks like them. */
button.al-sidebar__link { inline-size: 100%; border-block: 0; border-inline-end: 0; background: none; font: inherit; cursor: pointer; text-align: start; }
.al-sidebar__link:hover { color: var(--al-color-text); background: color-mix(in oklch, var(--al-color-surface) 60%, transparent); }
.al-sidebar__link[aria-current="page"] {
  color: var(--al-color-text);
  background: var(--al-color-surface);
  border-inline-start-color: var(--al-color-accent);
  font-weight: var(--al-weight-medium);
}
.al-sidebar__link[aria-current="page"] .al-icon { color: var(--al-color-accent); }
.al-sidebar__foot { margin-block-start: auto; display: flex; flex-direction: column; gap: var(--al-space-1); padding-block-start: var(--al-space-3); border-block-start: var(--al-border-hairline) solid var(--al-color-line); }

/* Collapsed: icons only; names stay for screen readers, and as tooltips. */
[data-sidebar="collapsed"] .al-sidebar { padding-inline: var(--al-space-2); }
[data-sidebar="collapsed"] .al-sidebar__link { justify-content: center; padding-inline: 0; }
[data-sidebar="collapsed"] .al-sidebar__label { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

@media (max-width: 52rem) {
  .al-sidebar { position: sticky; z-index: var(--al-z-sticky); block-size: auto; flex-direction: row; align-items: center; padding: var(--al-space-2) var(--al-space-3); border-inline-end: 0; border-block-end: var(--al-border-hairline) solid var(--al-color-line); overflow-x: auto; overflow-y: hidden; }
  .al-sidebar__nav { flex-direction: row; }
  .al-sidebar__foot { margin: 0; padding: 0; border: 0; flex-direction: row; }
  .al-sidebar__link { border-inline-start: 0; }
  [data-sidebar="collapsed"] .al-sidebar__label { position: static; inline-size: auto; block-size: auto; clip-path: none; }
  .al-sidebar__collapse { display: none; }
}
@media (forced-colors: active) { .al-sidebar__link[aria-current="page"] { border-inline-start-color: Highlight; } }

}

@layer al.components {
/* css/components/summary-list.css */
/* Summary list (from GOV.UK): facts as key–value rows, each with its own
   action ("Change"), for checking answers before submitting and for any
   read view of a record. The action's hidden text names what it changes
   ("Change<span class=al-visually-hidden> name</span>"). A <dl>. */
.al-summary-list { margin: 0; display: flex; flex-direction: column; }
.al-summary-list__row {
  display: grid;
  grid-template-columns: minmax(8rem, 30%) minmax(0, 1fr) auto;
  gap: var(--al-space-1) var(--al-space-5);
  align-items: baseline;
  padding-block: var(--al-space-3);
  border-block-end: var(--al-border-hairline) solid var(--al-color-line);
}
.al-summary-list__key { margin: 0; font-weight: var(--al-weight-medium); }
.al-summary-list__value { margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
.al-summary-list__value > * + * { margin-block-start: var(--al-space-1); }
.al-summary-list__actions { margin: 0; display: flex; gap: var(--al-space-3); justify-content: flex-end; }
.al-summary-list__row--no-actions { grid-template-columns: minmax(8rem, 30%) minmax(0, 1fr); }
@media (max-width: 40rem) {
  .al-summary-list__row, .al-summary-list__row--no-actions { grid-template-columns: minmax(0, 1fr) auto; }
  .al-summary-list__key { grid-column: 1 / -1; }
}

}

@layer al.components {
/* css/components/table.css */
/* Table: for comparing values. Numbers line up (tabular, end-aligned), the
   total row stands out, aside rows recede. A wide table scrolls inside its
   own region, which is focusable and named so the keyboard can scroll it. */
.al-table-wrap { overflow-x: auto; max-inline-size: 100%; }
.al-table-wrap:focus-visible { outline-offset: 2px; }
.al-table { inline-size: 100%; font-size: var(--al-text-base); }
.al-table caption { text-align: start; font-weight: var(--al-weight-medium); padding-block-end: var(--al-space-2); }
.al-table th, .al-table td {
  padding: var(--al-space-2) var(--al-space-3);
  border-block-end: var(--al-border-hairline) solid var(--al-color-line);
  text-align: start;
  vertical-align: baseline;
}
.al-table thead th {
  font-size: var(--al-text-xs);
  font-weight: var(--al-weight-medium);
  color: var(--al-color-text-muted);
  border-block-end-color: var(--al-color-line-strong);
  white-space: nowrap;
}
.al-table tbody th { font-weight: var(--al-weight-regular); }
.al-table .al-num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.al-table tr.is-sub > * { font-weight: var(--al-weight-medium); }
.al-table tr.is-total > * { font-weight: var(--al-weight-semibold); background: var(--al-color-accent-wash); border-block-start: var(--al-border-hairline) solid var(--al-color-line-strong); }
.al-table tr.is-aside > * { color: var(--al-color-text-muted); font-size: var(--al-text-sm); }
.al-table--interactive tbody tr:hover > * { background: var(--al-color-surface-sunk); }
.al-table tbody tr[aria-selected="true"] > * { background: var(--al-color-accent-wash); }
.al-table--compact th, .al-table--compact td { padding-block: var(--al-space-1-5); }

/* A sortable column's header is a button; aria-sort on the th says how. */
.al-table th > button { all: unset; display: inline-flex; gap: var(--al-space-1); align-items: center; cursor: pointer; min-block-size: var(--al-size-target-min); }
.al-table th > button:focus-visible { outline: var(--al-border-focus) solid var(--al-color-focus); outline-offset: 2px; }
.al-table th[aria-sort] > button::after { content: ""; border: 0.3em solid transparent; border-block-start-color: currentColor; margin-block-start: 0.3em; }
.al-table th[aria-sort="ascending"] > button::after { border-block-start-color: transparent; border-block-end-color: currentColor; margin-block: -0.3em 0; }

@media (forced-colors: active) { .al-table tr.is-total > * { border-block-start-color: CanvasText; } }

}

@layer al.components {
/* css/components/task-list.css */
/* Task list (from GOV.UK): the parts of a longer job (set-up, onboarding,
   an application), each with its status in words, so people can do them
   in any order and see what's left. A task that can't start yet says
   why, and isn't a link. */
.al-task-list { margin: 0; padding: 0; list-style: none; border-block-start: var(--al-border-hairline) solid var(--al-color-line); }
.al-task-list__item {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--al-space-1) var(--al-space-4);
  align-items: center;
  padding-block: var(--al-space-3);
  border-block-end: var(--al-border-hairline) solid var(--al-color-line);
}
.al-task-list__name { font-weight: var(--al-weight-medium); }
.al-task-list__name a { color: var(--al-color-accent); text-underline-offset: 0.2em; }
/* The whole row is the link's target. */
.al-task-list__name a::after { content: ""; position: absolute; inset: 0; }
.al-task-list__item:has(a):hover { background: var(--al-color-surface-sunk); }
.al-task-list__hint { grid-column: 1; font-size: var(--al-text-sm); color: var(--al-color-text-muted); }
.al-task-list__status { grid-row: 1 / span 2; grid-column: 2; }

}

@layer al.components {
/* css/components/tint.css */
/* Tints: eight bright, pastel colours (teal, green, yellow, orange, pink,
   purple, blue, cyan), each a ground with an ink readable on it. They add
   colour where colour means identity or emphasis (a tag, a highlighted
   phrase, an avatar), never status: errors, warnings and success keep
   their own colours, icons and words. Choose with data-tint. */
[data-tint="teal"]   { --al-tint-bg: var(--al-color-tint-teal-bg);   --al-tint-ink: var(--al-color-tint-teal-ink); }
[data-tint="green"]  { --al-tint-bg: var(--al-color-tint-green-bg);  --al-tint-ink: var(--al-color-tint-green-ink); }
[data-tint="yellow"] { --al-tint-bg: var(--al-color-tint-yellow-bg); --al-tint-ink: var(--al-color-tint-yellow-ink); }
[data-tint="orange"] { --al-tint-bg: var(--al-color-tint-orange-bg); --al-tint-ink: var(--al-color-tint-orange-ink); }
[data-tint="pink"]   { --al-tint-bg: var(--al-color-tint-pink-bg);   --al-tint-ink: var(--al-color-tint-pink-ink); }
[data-tint="purple"] { --al-tint-bg: var(--al-color-tint-purple-bg); --al-tint-ink: var(--al-color-tint-purple-ink); }
[data-tint="blue"]   { --al-tint-bg: var(--al-color-tint-blue-bg);   --al-tint-ink: var(--al-color-tint-blue-ink); }
[data-tint="cyan"]   { --al-tint-bg: var(--al-color-tint-cyan-bg);   --al-tint-ink: var(--al-color-tint-cyan-ink); }

/* A highlighted phrase in running text: <mark class="al-highlight">. */
.al-highlight {
  padding-inline: 0.25em;
  border-radius: var(--al-radius-sm);
  background: var(--al-tint-bg, var(--al-color-tint-yellow-bg));
  color: var(--al-tint-ink, var(--al-color-tint-yellow-ink));
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
a.al-highlight { text-decoration-color: currentColor; text-underline-offset: 0.2em; }

/* Tinted badges and tags. */
.al-badge[data-tint], .al-tag[data-tint] { background: var(--al-tint-bg); color: var(--al-tint-ink); border-color: transparent; }
.al-tag[data-tint]::before { background: currentColor; }

/* Avatar: initials on a tint, for a person or a workspace without a picture. */
.al-avatar {
  display: inline-grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 2rem;
  flex: none;
  border-radius: var(--al-radius-full);
  background: var(--al-tint-bg, var(--al-color-surface-sunk));
  color: var(--al-tint-ink, var(--al-color-text));
  font-size: var(--al-text-sm);
  font-weight: var(--al-weight-semibold);
  line-height: 1;
}
.al-avatar--lg { inline-size: 3rem; block-size: 3rem; font-size: var(--al-text-lg); }

@media (forced-colors: active) {
  .al-highlight { forced-color-adjust: none; background: Mark; color: MarkText; }
  .al-avatar { border: 1px solid CanvasText; }
}

}

@layer al.components {
/* css/components/toast.css */
/* Toast: says what just happened, briefly, where it doesn't block; offers
   Undo when the change can be undone. Lives in one polite live region
   (js/toast.js). Stays at least 6s with an action, 4s without; pauses
   while hovered or focused; never holds the only copy of an error. */
.al-toaster {
  position: fixed;
  inset-block-end: var(--al-space-5);
  inset-inline: var(--al-space-4);
  z-index: var(--al-z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--al-space-2);
  pointer-events: none;
}
.al-toast {
  display: flex;
  align-items: center;
  gap: var(--al-space-3);
  max-inline-size: min(36rem, 100%);
  padding-block: var(--al-space-2);
  padding-inline: var(--al-space-4) var(--al-space-2);
  border-radius: var(--al-radius-md);
  background: var(--al-color-inverse);
  color: var(--al-color-on-inverse);
  font-size: var(--al-text-base);
  pointer-events: auto;
  animation: al-toast-in var(--al-duration-base) var(--al-ease-standard);
}
.al-toast:not(:has(.al-toast__action)) { padding-inline-end: var(--al-space-4); }
.al-toast__action {
  all: unset;
  min-block-size: var(--al-size-target-min);
  padding-inline: var(--al-space-2);
  border-radius: var(--al-radius-sm);
  font-weight: var(--al-weight-semibold);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}
.al-toast__action:focus-visible { outline: var(--al-border-focus) solid currentColor; }
.al-toast[data-leaving] { opacity: 0; transition: opacity var(--al-duration-base) var(--al-ease-exit); }
@keyframes al-toast-in { from { opacity: 0; transform: translateY(0.5rem); } }
@media (forced-colors: active) { .al-toast { border: 1px solid CanvasText; } }

}

@layer al.components {
/* css/components/tooltip.css */
/* Tooltip: a short label for an icon or a mark, on hover and on focus, never
   holding anything essential. Escape hides it; it stays while the pointer
   is over it (WCAG 1.4.13). For anything longer, use a disclosure. */
.al-tooltip {
  position: fixed;
  z-index: var(--al-z-toast);
  max-inline-size: 18rem;
  margin: 0;
  padding: var(--al-space-1-5) var(--al-space-2);
  border: 0;
  border-radius: var(--al-radius-sm);
  background: var(--al-color-inverse);
  color: var(--al-color-on-inverse);
  font-size: var(--al-text-sm);
  line-height: var(--al-leading-ui);
  inset: auto;
}
.al-tooltip b { font-weight: var(--al-weight-semibold); }
@media (forced-colors: active) { .al-tooltip { border: 1px solid CanvasText; } }

}

@layer al.utilities {
/* css/utilities.css */
/* Utilities: few, and each does one thing. */

/* Read by screen readers, not shown. */
.al-visually-hidden:not(:focus):not(:active) {
  position: absolute !important;
  inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* The first thing a keyboard reaches: "Skip to content". */
.al-skip-link {
  position: absolute; inset-block-start: var(--al-space-2); inset-inline-start: var(--al-space-2);
  z-index: var(--al-z-toast);
  padding: var(--al-space-2) var(--al-space-4);
  background: var(--al-color-inverse); color: var(--al-color-on-inverse);
  border-radius: var(--al-radius-sm);
  transform: translateY(-200%);
}
.al-skip-link:focus { transform: none; }

/* Bidi: a left-to-right fragment (a number, money, a code, an address)
   kept whole inside right-to-left text; and any fragment isolated from
   what's around it. In markup, <bdi> does the same. */
.al-ltr { direction: ltr; unicode-bidi: isolate; }
.al-isolate { unicode-bidi: isolate; }
/* Text whose direction follows its own first strong letter (user content). */
.al-auto-dir { unicode-bidi: plaintext; }

/* Mirrored in right-to-left: arrows, chevrons, undo, progress. */
[dir="rtl"] .al-mirror { transform: scaleX(-1); }

.al-muted { color: var(--al-color-text-muted); }
.al-tabular { font-variant-numeric: tabular-nums; }
.al-nowrap { white-space: nowrap; }
.al-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-prose { font-family: var(--al-font-serif); font-size: var(--al-text-md); line-height: var(--al-leading-prose); max-inline-size: var(--al-size-measure); }
.al-prose > * + * { margin-block-start: 1em; }
.al-display { font-family: var(--al-font-display); font-size: var(--al-text-display); line-height: var(--al-leading-tight); letter-spacing: var(--al-tracking-tight); font-weight: var(--al-weight-regular); }
.al-lede { font-family: var(--al-font-serif); font-size: var(--al-text-lg); line-height: var(--al-leading-prose); color: var(--al-color-text-muted); max-inline-size: var(--al-size-measure); }

}
