/* =========================================================================
   Design System — component layer
   =========================================================================

   Every rule in this file references a custom property and nothing else.
   There is not one literal colour, radius, shadow or spacing value below
   (outside the reset), which is why this file is identical for every theme
   and can be cached indefinitely: switching from Atlas to Graphite changes
   the generated token block in <head>, never this stylesheet.

   If you find yourself wanting to add a hex code here, the token is missing.
   Add it to config/design.php instead.

   The written rationale is in DESIGN-GUIDE.md.

   Contents
     01  Fonts
     02  Reset & base
     03  Typography
     04  App shell
     05  Sidebar & topbar
     06  Page header
     07  Cards & surfaces
     08  Buttons
     09  Form controls
     10  Badges & pills
     11  Tables
     12  Stats & metrics
     13  Modal, drawer, popover
     14  Tabs & segmented control
     15  Alerts & toasts
     16  Avatar
     17  Empty & loading states
     18  Charts
     19  Maps
     20  Boards & timelines
     21  Utilities
     22  Motion & accessibility
   ========================================================================= */


/* 01 · Fonts ============================================================== */

@font-face {
    font-family: 'Inter Variable';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/assets/fonts/inter-variable.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+2074, U+20AC, U+2122,
        U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Source Serif Variable';
    font-style: normal;
    font-weight: 200 900;
    font-display: swap;
    src: url('/assets/fonts/source-serif-variable.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+2074, U+20AC, U+2122,
        U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* 02 · Reset & base ======================================================= */

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

html {
    font-size: var(--root-size);
    -webkit-text-size-adjust: 100%;
    scrollbar-gutter: stable;
}

body {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: var(--leading-base);
    letter-spacing: var(--tracking-base);
    color: var(--text-primary);
    background: var(--surface-page);
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-variant-ligatures: contextual common-ligatures;
    text-rendering: optimizeLegibility;
}

/* Tabular figures everywhere numbers are compared to each other. Proportional
   digits make a column of numbers ragged, which is the sort of small wrongness
   that makes an interface feel unconsidered. */
table, .stat-value, .metric, .tabular {
    font-variant-numeric: tabular-nums;
}

img, svg, video, canvas {
    display: block;
    max-width: 100%;
}

/* The `hidden` attribute has to beat whatever display a class sets.
   The UA rule for it is a bare `[hidden] { display: none }`, so any component
   class with `display: flex` silently wins and the element stays on screen —
   which for something like a revealed secret is not a cosmetic bug. */
[hidden] {
    display: none !important;
}

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

a {
    color: var(--text-brand);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

hr {
    border: 0;
    border-top: var(--border-width) solid var(--border-subtle);
}

::selection {
    background: var(--surface-selected);
    color: var(--text-primary);
}

/* One focus treatment for the whole product. Never remove it without
   replacing it — keyboard users have no other way to know where they are. */
:focus-visible {
    outline: 2px solid var(--border-brand);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

:focus:not(:focus-visible) {
    outline: none;
}

.ui-icon {
    flex: none;
    stroke: currentColor;
}


/* 03 · Typography ========================================================= */

.display {
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    line-height: var(--leading-3xl);
    letter-spacing: var(--tracking-display);
    font-weight: var(--weight-display);
    color: var(--text-primary);
}

.title {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    line-height: var(--leading-2xl);
    letter-spacing: var(--tracking-display);
    font-weight: var(--weight-display);
}

.heading {
    font-size: var(--text-lg);
    line-height: var(--leading-lg);
    letter-spacing: var(--tracking-lg);
    font-weight: var(--weight-heading);
}

.subheading {
    font-size: var(--text-md);
    line-height: var(--leading-md);
    font-weight: var(--weight-heading);
}

/* The small label above a value or a group. `--eyebrow-transform` is what
   makes Graphite shout in uppercase and Atlas stay sentence case. */
.eyebrow {
    font-size: var(--text-xs);
    line-height: var(--leading-xs);
    font-weight: var(--weight-ui);
    letter-spacing: var(--eyebrow-tracking);
    text-transform: var(--eyebrow-transform);
    color: var(--text-tertiary);
}

.body-sm {
    font-size: var(--text-sm);
    line-height: var(--leading-sm);
}

.muted { color: var(--text-secondary); }
.subtle { color: var(--text-tertiary); }
.mono { font-family: var(--font-mono); font-size: var(--text-xs); }

/* Long-form text gets a measure. Past about 75 characters the eye loses the
   start of the next line. */
.prose { max-width: 68ch; }
.prose p + p { margin-top: var(--space-3); }


/* 04 · App shell ========================================================== */

.app-shell {
    display: flex;
    min-height: 100vh;
    align-items: stretch;
}

.app-main {
    flex: 1;
    min-width: 0; /* lets tables scroll instead of stretching the layout */
    display: flex;
    flex-direction: column;
}

.app-content {
    flex: 1;
    padding: var(--page-pad-y) var(--page-pad-x) var(--space-16);
    width: 100%;
}

.app-content > .content-width {
    max-width: var(--content-max);
    margin-inline: auto;
    width: 100%;
}

/* Topbar-nav themes centre their content; sidebar themes let it run wide,
   because the sidebar already provides the left margin. */
[data-nav="topbar"] .app-content > .content-width {
    max-width: var(--content-max);
}

.section + .section {
    margin-top: var(--section-gap);
}


/* 05 · Sidebar & topbar =================================================== */

.sidebar {
    width: var(--sidebar-w);
    flex: none;
    background: var(--surface-chrome);
    border-right: var(--border-width) solid var(--border-chrome);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    z-index: var(--z-sticky);
}

.sidebar-head {
    height: var(--topbar-h);
    display: flex;
    align-items: center;
    gap: var(--space-2-5);
    padding-inline: var(--space-4);
    flex: none;
}

.sidebar-body {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: var(--space-2) var(--space-3) var(--space-4);
}

.sidebar-foot {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
    border-top: var(--border-width) solid var(--border-chrome);
}

/* Anything sitting on the chrome takes its colours from the chrome, not from
   the page. Without this a brand-tinted avatar disappears against a dark
   sidebar — near-black initials on a near-black disc. */
.sidebar .avatar,
.topbar .avatar {
    background: var(--surface-chrome-selected);
    color: var(--text-on-chrome-active);
}

.sidebar .segmented {
    background: var(--surface-chrome-hover);
    border-color: var(--border-chrome);
}

.sidebar .segmented button {
    color: var(--text-on-chrome-muted);
    flex: 1;
    justify-content: center;
}

.sidebar .segmented button:hover { color: var(--text-on-chrome-active); }

.sidebar .segmented button[aria-pressed="true"] {
    background: var(--surface-chrome);
    color: var(--text-on-chrome-active);
}

.nav-group + .nav-group {
    margin-top: var(--space-5);
}

.nav-group-label {
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-on-chrome-muted);
    padding: 0 var(--space-2-5) var(--space-1-5);
}

.nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-2-5);
    height: var(--control-height-md);
    /* Also used as a <button> in the user menu — without this the UA default
       border shows and it reads as a form input rather than a nav row. */
    border: 0;
    background: transparent;
    cursor: pointer;
    width: 100%;
    text-align: left;
    padding-inline: var(--space-2-5);
    border-radius: var(--radius-sm);
    color: var(--text-on-chrome);
    font-size: var(--text-base);
    font-weight: var(--weight-ui);
    text-decoration: none;
    transition: background var(--duration-fast) var(--ease-standard),
                color var(--duration-fast) var(--ease-standard);
    position: relative;
}

.nav-item + .nav-item { margin-top: 1px; }

.nav-item:hover {
    background: var(--surface-chrome-hover);
    color: var(--text-on-chrome-active);
    text-decoration: none;
}

/* The active row uses a chrome-specific token, not --surface-selected: a dark
   sidebar needs a light translucent highlight while a selected table row in the
   content area still needs the brand tint. They are two different jobs. */
.nav-item[aria-current="page"] {
    background: var(--surface-chrome-selected);
    color: var(--text-on-chrome-active);
    font-weight: var(--weight-semibold);
}

.nav-item .ui-icon {
    color: var(--text-on-chrome-muted);
    transition: color var(--duration-fast) var(--ease-standard);
}

.nav-item:hover .ui-icon,
.nav-item[aria-current="page"] .ui-icon {
    color: currentColor;
}

.nav-item-badge {
    margin-left: auto;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    color: var(--text-on-chrome-muted);
}

/* Topbar ---------------------------------------------------------------- */

.topbar {
    height: var(--topbar-h);
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-inline: var(--page-pad-x);
    background: var(--surface-chrome);
    border-bottom: var(--border-width) solid var(--border-chrome);
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
}

.topbar-inner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
}

.topbar-nav {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-left: var(--space-6);
}

.topbar-nav .nav-item {
    height: var(--control-height-sm);
}

.topbar-spacer { flex: 1; }

/* Brand mark ------------------------------------------------------------ */

.brand {
    display: flex;
    align-items: center;
    gap: var(--space-2-5);
    color: var(--text-on-chrome-active);
    text-decoration: none;
    min-width: 0;
}

.brand:hover { text-decoration: none; }

.brand-mark {
    width: 28px;
    height: 28px;
    flex: none;
    display: grid;
    place-items: center;
    background: var(--action-primary-bg);
    color: var(--action-primary-fg);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: 0.01em;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.brand-mark img { width: 100%; height: 100%; object-fit: contain; }

[data-mark="circle"] .brand-mark { border-radius: var(--radius-full); }
[data-mark="square"] .brand-mark { border-radius: var(--radius-xs); }
[data-mark="squircle"] .brand-mark { border-radius: var(--radius-sm); }

.brand-name {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-md);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.brand-logo-full { height: 26px; width: auto; }


/* 06 · Page header ======================================================== */

.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--section-gap);
}

.page-header-text { min-width: 0; }

.page-header h1 {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    line-height: var(--leading-2xl);
    letter-spacing: var(--tracking-display);
    font-weight: var(--weight-display);
    color: var(--text-primary);
}

.page-header p {
    margin-top: var(--space-1);
    color: var(--text-secondary);
    font-size: var(--text-base);
}

.page-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
}

.breadcrumb {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    margin-bottom: var(--space-1-5);
}

.breadcrumb a { color: var(--text-tertiary); }
.breadcrumb a:hover { color: var(--text-secondary); }


/* 07 · Cards & surfaces =================================================== */

.card {
    background: var(--surface-raised);
    border: var(--border-width) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

/* No hover lift on a static card. A card that moves when the pointer passes
   over it implies it can be clicked; if it can't, the movement is a lie. */
a.card,
button.card,
.card--interactive {
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-standard),
                box-shadow var(--duration-fast) var(--ease-standard),
                transform var(--duration-fast) var(--ease-standard);
    text-align: left;
    display: block;
    width: 100%;
    color: inherit;
}

a.card:hover,
button.card:hover,
.card--interactive:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-md);
    transform: translateY(var(--lift));
    text-decoration: none;
}

.card-body { padding: var(--card-pad); }
.card-body--tight { padding: var(--card-pad-tight); }

.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--card-pad-tight) var(--card-pad);
    border-bottom: var(--border-width) solid var(--border-subtle);
    min-height: var(--control-height-lg);
}

.card-head h2,
.card-head h3 {
    font-size: var(--text-md);
    font-weight: var(--weight-heading);
    letter-spacing: var(--tracking-md);
}

.card-head-sub {
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    margin-top: 1px;
}

.card-foot {
    padding: var(--card-pad-tight) var(--card-pad);
    border-top: var(--border-width) solid var(--border-subtle);
    background: var(--surface-sunken);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

.panel {
    background: var(--surface-sunken);
    border: var(--border-width) solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: var(--card-pad-tight);
}

.divider {
    height: var(--border-width);
    background: var(--border-subtle);
    border: 0;
    margin-block: var(--space-4);
}


/* 08 · Buttons ============================================================ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: var(--control-height-md);
    padding-inline: var(--control-pad-x-md);
    border-radius: var(--radius-sm);
    border: var(--border-width) solid transparent;
    font-size: var(--text-base);
    font-weight: var(--weight-ui);
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    user-select: none;
    transition: background var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard),
                color var(--duration-fast) var(--ease-standard);
}

.btn:hover { text-decoration: none; }

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* Primary. One per view, on the thing you most want done. Two primaries is
   the same as none — the eye has nowhere to land. */
.btn--primary {
    background: var(--action-primary-bg);
    color: var(--action-primary-fg);
}
.btn--primary:hover { background: var(--action-primary-bg-hover); }
.btn--primary:active { background: var(--action-primary-bg-active); }

.btn--secondary {
    background: var(--action-secondary-bg);
    color: var(--action-secondary-fg);
    border-color: var(--action-secondary-border);
    box-shadow: var(--shadow-xs);
}
.btn--secondary:hover { background: var(--action-secondary-bg-hover); border-color: var(--border-strong); }
.btn--secondary:active { background: var(--action-secondary-bg-active); }

.btn--ghost {
    background: transparent;
    color: var(--action-ghost-fg);
}
.btn--ghost:hover { background: var(--action-ghost-bg-hover); color: var(--text-primary); }

.btn--danger {
    background: var(--action-danger-bg);
    color: var(--action-danger-fg);
}
.btn--danger:hover { background: var(--action-danger-bg-hover); }

.btn--link {
    background: transparent;
    color: var(--text-brand);
    height: auto;
    padding: 0;
    border: 0;
}
.btn--link:hover { text-decoration: underline; text-underline-offset: 2px; }

.btn--sm {
    height: var(--control-height-sm);
    padding-inline: var(--control-pad-x-sm);
    font-size: var(--text-sm);
    gap: var(--space-1-5);
}

.btn--lg {
    height: var(--control-height-lg);
    padding-inline: var(--control-pad-x-lg);
    font-size: var(--text-md);
}

/* Square icon-only button. Needs an aria-label — an icon alone is not a name. */
.btn--icon {
    width: var(--control-height-md);
    padding: 0;
}
.btn--icon.btn--sm { width: var(--control-height-sm); }
.btn--icon.btn--lg { width: var(--control-height-lg); }

.btn--block { width: 100%; }

.btn-group {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}


/* 09 · Form controls ====================================================== */

.field + .field { margin-top: var(--field-gap); }

.field-label {
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--weight-ui);
    color: var(--text-primary);
    margin-bottom: var(--space-1-5);
}

/* Sentence case, not uppercase. Uppercase labels are harder to scan and give
   a form the look of a tax return. */
.field-hint {
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    margin-top: var(--space-1-5);
}

.field-error {
    font-size: var(--text-sm);
    color: var(--text-danger);
    margin-top: var(--space-1-5);
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
}

.field-required {
    color: var(--text-danger);
    margin-left: 2px;
}

.input,
.select,
.textarea {
    display: block;
    width: 100%;
    height: var(--control-height-md);
    padding-inline: var(--control-pad-x-sm);
    background: var(--surface-inset);
    color: var(--text-primary);
    border: var(--border-width) solid var(--action-secondary-border);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
    transition: border-color var(--duration-fast) var(--ease-standard),
                box-shadow var(--duration-fast) var(--ease-standard);
}

.textarea {
    height: auto;
    min-height: calc(var(--control-height-md) * 2.4);
    padding-block: var(--space-2);
    resize: vertical;
    line-height: var(--leading-base);
}

.input::placeholder,
.textarea::placeholder { color: var(--text-placeholder); }

.input:hover,
.select:hover,
.textarea:hover { border-color: var(--border-strong); }

/* The ring is a box-shadow rather than an outline so it follows the corner
   radius exactly. */
.input:focus,
.select:focus,
.textarea:focus {
    outline: none;
    border-color: var(--border-brand);
    box-shadow: 0 0 0 3px var(--focus-ring);
}

.input:disabled,
.select:disabled,
.textarea:disabled {
    background: var(--surface-disabled);
    color: var(--text-disabled);
    cursor: not-allowed;
}

.input[aria-invalid="true"],
.textarea[aria-invalid="true"] { border-color: var(--border-danger); }

.select {
    appearance: none;
    padding-right: var(--space-8);
    /* Chevron drawn with a gradient pair so it needs no image request and
       tints with the text colour. */
    cursor: pointer;
}

/* The caret is drawn with a mask rather than a background image, so its
   colour comes from a token and one rule covers both colour modes. The data
   URI is the Lucide chevron-down geometry — a shape, not a design value, which
   is why it is allowed to be a literal here. */
.select-wrap {
    position: relative;
    display: block;
}

.select-wrap::after {
    content: '';
    position: absolute;
    right: var(--space-3);
    top: 50%;
    width: 14px;
    height: 14px;
    transform: translateY(-50%);
    pointer-events: none;
    background-color: var(--text-tertiary);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}

.input-group {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.input-affix {
    position: relative;
    display: flex;
    align-items: center;
}

.input-affix .ui-icon {
    position: absolute;
    left: var(--space-2-5);
    color: var(--text-tertiary);
    pointer-events: none;
}

.input-affix .input { padding-left: var(--space-9); }

/* Two fields on one row, for fields that are a pair — a start date and an end
   date, a contact and the email that belongs to them. Splitting a pair across
   two rows makes the reader work out that they are related.

   auto-fit rather than a fixed two columns, so the pair collapses to one
   column before either field gets too narrow to type into. */
.form-grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4);
}

/* Colour input, styled to look like part of the system rather than an OS
   widget bolted on. */
.color-field {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.color-swatch-input {
    width: var(--control-height-md);
    height: var(--control-height-md);
    flex: none;
    padding: 0;
    border: var(--border-width) solid var(--action-secondary-border);
    border-radius: var(--radius-sm);
    background: none;
    cursor: pointer;
    overflow: hidden;
}
.color-swatch-input::-webkit-color-swatch-wrapper { padding: 2px; }
.color-swatch-input::-webkit-color-swatch { border: 0; border-radius: calc(var(--radius-sm) - 2px); }
.color-swatch-input::-moz-color-swatch { border: 0; border-radius: calc(var(--radius-sm) - 2px); }

.color-field .input {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    text-transform: uppercase;
}

/* Checkbox & radio ------------------------------------------------------ */

.check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2-5);
    cursor: pointer;
    font-size: var(--text-base);
}

.check input[type="checkbox"],
.check input[type="radio"] {
    appearance: none;
    width: 16px;
    height: 16px;
    margin-top: 2px;
    flex: none;
    border: var(--border-width) solid var(--border-strong);
    border-radius: var(--radius-xs);
    background: var(--surface-inset);
    cursor: pointer;
    display: grid;
    place-content: center;
    transition: background var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard);
}

.check input[type="radio"] { border-radius: var(--radius-full); }

.check input:checked {
    background: var(--action-primary-bg);
    border-color: var(--action-primary-bg);
}

.check input[type="checkbox"]:checked::after {
    content: '';
    width: 9px;
    height: 5px;
    border: 2px solid var(--action-primary-fg);
    border-top: 0;
    border-right: 0;
    transform: rotate(-45deg) translate(1px, -1px);
}

.check input[type="radio"]:checked::after {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--action-primary-fg);
}

.check-text { min-width: 0; }
.check-title { font-weight: var(--weight-ui); }
.check-hint { font-size: var(--text-sm); color: var(--text-tertiary); }

/* Switch ---------------------------------------------------------------- */

.switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2-5);
    cursor: pointer;
}

.switch input {
    appearance: none;
    width: 34px;
    height: 20px;
    flex: none;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    position: relative;
    cursor: pointer;
    transition: background var(--duration-base) var(--ease-standard);
}

.switch input::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: var(--radius-full);
    background: var(--surface-knob);
    box-shadow: var(--shadow-xs);
    transition: transform var(--duration-base) var(--ease-standard);
}

.switch input:checked { background: var(--action-primary-bg); }
.switch input:checked::after { transform: translateX(14px); }


/* 10 · Badges & pills ===================================================== */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    height: 20px;
    padding-inline: var(--space-2);
    border-radius: var(--radius-xs);
    font-size: var(--text-xs);
    font-weight: var(--weight-ui);
    line-height: 1;
    white-space: nowrap;
    background: var(--surface-neutral-subtle);
    color: var(--text-secondary);
}

.badge--brand   { background: var(--surface-brand-subtle);   color: var(--text-brand); }
.badge--success { background: var(--surface-success-subtle); color: var(--text-success); }
.badge--warning { background: var(--surface-warning-subtle); color: var(--text-warning); }
.badge--danger  { background: var(--surface-danger-subtle);  color: var(--text-danger); }
.badge--info    { background: var(--surface-info-subtle);    color: var(--text-info); }

.badge--outline {
    background: transparent;
    border: var(--border-width) solid var(--border-default);
    color: var(--text-secondary);
}

.badge--pill { border-radius: var(--radius-full); padding-inline: var(--space-2-5); }

/* A status is a coloured dot plus a word. The dot alone would leave anyone who
   can't distinguish the hues with no information at all. */
.status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.status::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: currentColor;
    flex: none;
}

.status--success { color: var(--text-success); }
.status--warning { color: var(--text-warning); }
.status--danger  { color: var(--text-danger); }
.status--neutral { color: var(--text-tertiary); }
.status--info    { color: var(--text-info); }
.status--brand   { color: var(--text-brand); }


/* 11 · Tables ============================================================= */

.table-wrap {
    width: 100%;
    overflow-x: auto;
    border-radius: var(--radius-lg);
}

.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--text-base);
}

.table thead th {
    text-align: left;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    background: var(--surface-sunken);
    padding: var(--space-2) var(--space-3);
    border-bottom: var(--border-width) solid var(--border-default);
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 1;
}

.table tbody td {
    padding: var(--table-row-pad-y) var(--space-3);
    border-bottom: var(--border-width) solid var(--border-subtle);
    vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: 0; }

.table tbody tr:hover td { background: var(--surface-hover); }

.table tbody tr[aria-selected="true"] td { background: var(--surface-selected); }

/* Numbers right-align so their digits stack; text stays left. */
.table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.table .col-tight { width: 1%; white-space: nowrap; }

.table--flush thead th:first-child,
.table--flush tbody td:first-child { padding-left: var(--card-pad); }
.table--flush thead th:last-child,
.table--flush tbody td:last-child { padding-right: var(--card-pad); }

/* The bar above a list — search, filters, sort.

   It sits inside the card, above the table, rather than floating above the
   card: the controls belong to the list they filter, and a gap between them
   makes that relationship something the reader has to infer. */
.list-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3) var(--card-pad);
    border-bottom: var(--border-width) solid var(--border-subtle);
}

/* Search takes the room left over; the selects keep their natural width.
   Wrapping to its own line on a narrow screen is the point of the basis. */
.list-toolbar-search { flex: 1 1 200px; min-width: 0; }

/* Pagination -------------------------------------------------------------

   Filed with tables because a list is what it paginates. The count sits on
   the left and the controls on the right: "how much is there" and "how do I
   see more" are different questions, and answering them in one cluster makes
   the reader untangle them. */
.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3) var(--card-pad);
    border-top: var(--border-width) solid var(--border-subtle);
}

.pagination-summary {
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
}

.pagination-pages {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.pagination-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--control-height-sm);
    height: var(--control-height-sm);
    padding-inline: var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
    text-decoration: none;
}

.pagination-link:hover {
    background: var(--surface-hover);
    color: var(--text-primary);
}

.pagination-link[aria-current="page"] {
    background: var(--surface-selected);
    color: var(--text-primary);
    font-weight: var(--weight-semibold);
}

/* Rendered as a span, not a disabled link: there is no page to go to, so
   there should be nothing to focus or click. */
.pagination-link[aria-disabled="true"] {
    color: var(--text-disabled);
    pointer-events: none;
}

.pagination-gap {
    padding-inline: var(--space-1);
    color: var(--text-tertiary);
}


/* 12 · Stats & metrics ==================================================== */

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--grid-gap);
}

.stat {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--card-pad);
}

.stat-label {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    font-size: var(--text-sm);
    font-weight: var(--weight-ui);
    color: var(--text-secondary);
}

.stat-label .ui-icon { color: var(--text-tertiary); }

/* Big, plain, and in the body font. A metric set in a gradient is unreadable
   at a glance, which defeats the only thing a metric is for. */
.stat-value {
    font-size: var(--text-3xl);
    line-height: 1.1;
    letter-spacing: var(--tracking-display);
    font-weight: var(--weight-display);
    color: var(--text-primary);
}

.stat-meta {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    font-size: var(--text-sm);
    color: var(--text-tertiary);
}

.stat-delta {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-weight: var(--weight-ui);
}

.stat-delta--up { color: var(--text-success); }
.stat-delta--down { color: var(--text-danger); }


/* 13 · Modal, drawer, popover ============================================= */

/* Built on <dialog>, so focus trapping, Esc-to-close and inertness of the
   page behind come from the platform instead of from JavaScript that will
   eventually get one of them wrong. */
.modal {
    padding: 0;
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-xl);
    background: var(--surface-overlay);
    color: var(--text-primary);
    box-shadow: var(--shadow-xl);
    width: min(560px, calc(100vw - var(--space-8)));
    max-height: min(85vh, 760px);
    overflow: visible;
}

.modal::backdrop {
    background: var(--surface-backdrop);
    backdrop-filter: blur(1px);
}

.modal[open] {
    animation: modal-in var(--duration-base) var(--ease-entrance);
}

.modal[open]::backdrop {
    animation: fade-in var(--duration-base) var(--ease-entrance);
}

@keyframes modal-in {
    from { opacity: 0; transform: translateY(6px) scale(0.99); }
    to   { opacity: 1; transform: none; }
}

@keyframes fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.modal--sm { width: min(420px, calc(100vw - var(--space-8))); }
.modal--lg { width: min(760px, calc(100vw - var(--space-8))); }
.modal--xl { width: min(1040px, calc(100vw - var(--space-8))); }

.modal-inner {
    display: flex;
    flex-direction: column;
    max-height: inherit;
}

.modal-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--card-pad) var(--card-pad) var(--space-3);
    flex: none;
}

.modal-title {
    font-size: var(--text-lg);
    font-weight: var(--weight-heading);
    letter-spacing: var(--tracking-lg);
}

.modal-sub {
    margin-top: var(--space-1);
    font-size: var(--text-base);
    color: var(--text-secondary);
}

.modal-body {
    padding: 0 var(--card-pad) var(--card-pad);
    overflow-y: auto;
    flex: 1;
}

/* The footer is where the action is, so it gets a border and a tinted ground
   to separate it from the content that scrolls under it. */
.modal-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--card-pad-tight) var(--card-pad);
    border-top: var(--border-width) solid var(--border-subtle);
    background: var(--surface-sunken);
    border-radius: 0 0 var(--radius-xl) var(--radius-xl);
    flex: none;
}

.modal-foot--split { justify-content: space-between; }

.drawer {
    width: min(480px, 100vw);
    max-width: none;
    height: 100vh;
    max-height: 100vh;
    margin: 0 0 0 auto;
    border-radius: 0;
    border-width: 0 0 0 var(--border-width);
}

.drawer[open] { animation: drawer-in var(--duration-slow) var(--ease-entrance); }

@keyframes drawer-in {
    from { transform: translateX(12px); opacity: 0; }
    to   { transform: none; opacity: 1; }
}

/* Popover / dropdown menu ------------------------------------------------ */

.menu {
    position: absolute;
    min-width: 200px;
    padding: var(--space-1);
    background: var(--surface-overlay);
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    z-index: var(--z-popover);
}

.menu[hidden] { display: none; }

.menu-item {
    display: flex;
    align-items: center;
    gap: var(--space-2-5);
    width: 100%;
    height: var(--control-height-sm);
    padding-inline: var(--space-2-5);
    border: 0;
    border-radius: var(--radius-xs);
    background: transparent;
    color: var(--text-primary);
    font-size: var(--text-base);
    text-align: left;
    cursor: pointer;
    text-decoration: none;
}

.menu-item:hover { background: var(--surface-hover); text-decoration: none; }
.menu-item .ui-icon { color: var(--text-tertiary); }
.menu-item--danger { color: var(--text-danger); }
.menu-item--danger .ui-icon { color: currentColor; }

.menu-label {
    padding: var(--space-2) var(--space-2-5) var(--space-1);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-tertiary);
}

.menu-sep {
    height: var(--border-width);
    background: var(--border-subtle);
    margin: var(--space-1) 0;
}

.dropdown { position: relative; }
.dropdown > .menu { top: calc(100% + var(--space-1)); right: 0; }


/* 14 · Tabs & segmented control =========================================== */

.tabs {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    border-bottom: var(--border-width) solid var(--border-subtle);
    margin-bottom: var(--section-gap);
    overflow-x: auto;
}

.tab {
    position: relative;
    padding: var(--space-2-5) 0;
    font-size: var(--text-base);
    font-weight: var(--weight-ui);
    color: var(--text-secondary);
    background: none;
    border: 0;
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
}

.tab:hover { color: var(--text-primary); text-decoration: none; }

.tab[aria-selected="true"] {
    color: var(--text-primary);
    font-weight: var(--weight-semibold);
}

/* The underline is inset by the border width so it sits on the divider, not
   below it. */
.tab[aria-selected="true"]::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(var(--border-width) * -1);
    height: 2px;
    background: var(--action-primary-bg);
    border-radius: var(--radius-full) var(--radius-full) 0 0;
}

.segmented {
    display: inline-flex;
    padding: 2px;
    gap: 2px;
    background: var(--surface-sunken);
    border: var(--border-width) solid var(--border-subtle);
    border-radius: var(--radius-sm);
}

.segmented button,
.segmented label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5);
    height: calc(var(--control-height-sm) - 4px);
    padding-inline: var(--space-2-5);
    border: 0;
    border-radius: calc(var(--radius-sm) - 2px);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-ui);
    cursor: pointer;
    white-space: nowrap;
}

.segmented button:hover,
.segmented label:hover { color: var(--text-primary); }

.segmented button[aria-pressed="true"],
.segmented input:checked + label,
.segmented .is-active {
    background: var(--surface-raised);
    color: var(--text-primary);
    box-shadow: var(--shadow-xs);
}

.segmented input { position: absolute; opacity: 0; pointer-events: none; }


/* 15 · Alerts & toasts ==================================================== */

.alert {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2-5);
    padding: var(--space-3) var(--space-3-5);
    border-radius: var(--radius-md);
    border: var(--border-width) solid transparent;
    font-size: var(--text-base);
}

.alert .ui-icon { margin-top: 1px; flex: none; }
.alert-title { font-weight: var(--weight-semibold); }
.alert-body { min-width: 0; }
.alert-body p + p { margin-top: var(--space-1); }

.alert--info    { background: var(--surface-info-subtle);    color: var(--text-info); }
.alert--success { background: var(--surface-success-subtle); color: var(--text-success); }
.alert--warning { background: var(--surface-warning-subtle); color: var(--text-warning); }
.alert--danger  { background: var(--surface-danger-subtle);  color: var(--text-danger); }
.alert--neutral { background: var(--surface-sunken); color: var(--text-secondary); border-color: var(--border-subtle); }

.toast-region {
    position: fixed;
    bottom: var(--space-5);
    right: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    z-index: var(--z-toast);
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    gap: var(--space-2-5);
    min-width: 280px;
    max-width: 400px;
    padding: var(--space-3) var(--space-3-5);
    background: var(--surface-overlay);
    border: var(--border-width) solid var(--border-default);
    border-left: 3px solid var(--action-primary-bg);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-base);
    pointer-events: auto;
    animation: toast-in var(--duration-slow) var(--ease-entrance);
}

.toast--success { border-left-color: var(--text-success); }
.toast--danger  { border-left-color: var(--text-danger); }
.toast--warning { border-left-color: var(--text-warning); }

@keyframes toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}


/* 16 · Avatar ============================================================= */

.avatar {
    width: 28px;
    height: 28px;
    flex: none;
    border-radius: var(--radius-full);
    background: var(--surface-brand-subtle);
    color: var(--text-brand);
    display: grid;
    place-items: center;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    overflow: hidden;
    user-select: none;
}

.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 22px; height: 22px; font-size: var(--text-2xs); }
.avatar--lg { width: 40px; height: 40px; font-size: var(--text-base); }

.avatar-group { display: flex; }
.avatar-group .avatar + .avatar {
    margin-left: -8px;
    box-shadow: 0 0 0 2px var(--surface-raised);
}


/* 17 · Empty & loading states ============================================= */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--space-12) var(--space-6);
    gap: var(--space-2);
}

.empty-icon {
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-md);
    background: var(--surface-sunken);
    color: var(--text-tertiary);
    margin-bottom: var(--space-2);
}

.empty h3 {
    font-size: var(--text-md);
    font-weight: var(--weight-heading);
}

.empty p {
    color: var(--text-secondary);
    font-size: var(--text-base);
    max-width: 42ch;
}

.empty-actions { margin-top: var(--space-3); }

/* A skeleton shaped like the content it stands in for, so the layout does not
   jump when the data lands. */
.skeleton {
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 0%,
        var(--surface-hover) 50%,
        var(--surface-sunken) 100%
    );
    background-size: 200% 100%;
    animation: shimmer 1.4s ease-in-out infinite;
    border-radius: var(--radius-xs);
}

@keyframes shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

.skeleton--text { height: 0.75em; margin-block: 0.25em; }
.skeleton--title { height: 1.5em; width: 40%; }
.skeleton--block { height: 100%; width: 100%; }

.spinner {
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-default);
    border-top-color: var(--action-primary-bg);
    border-radius: var(--radius-full);
    animation: spin 0.7s linear infinite;
}

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


/* 18 · Charts ============================================================= */

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

.chart-frame {
    position: relative;
    padding: var(--card-pad-tight) var(--card-pad) var(--card-pad);
    flex: 1;
    min-height: 0;
}

.chart-canvas {
    position: relative;
    width: 100%;
    height: 300px;
}

.chart-canvas--tall { height: 380px; }
.chart-canvas--short { height: 220px; }

/* Sparklines and win/loss strips: tall enough to carry a shape, short enough
   to sit on a line of text or in a table cell without stretching the row. */
.chart-canvas--micro { height: 40px; }
.chart-canvas--inline { height: 24px; width: 88px; display: inline-block; vertical-align: middle; }

/* A legend built in HTML rather than by the chart library, so it uses the same
   type ramp and the same swatch shape as the rest of the product. */
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) var(--card-pad) 0;
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.chart-legend-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5);
}

.chart-legend-swatch {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-xs);
    flex: none;
}

.chart-grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(440px, 100%), 1fr));
    gap: var(--grid-gap);
}

/* Three across for a gallery of small examples. Collapses to two and then to
   one on its own, so no page needs a media query to use it. */
.chart-grid-3 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: var(--grid-gap);
}

/* --- Meters -------------------------------------------------------------
   Labelled tracks: a list of "43 of 60" rows, where one column is drawn
   instead of written. Built from elements rather than a canvas so the labels
   stay selectable, searchable and legible to a screen reader — and so the
   whole thing costs nothing to render. */

.chart-meters {
    display: grid;
    /* Rows keep their own height instead of spreading to fill a taller box:
       four meters in a 300px card should be four meters, not four meters with
       70px of nothing between them. */
    align-content: start;
    gap: var(--space-3-5);
    padding: var(--card-pad-tight) var(--card-pad) var(--card-pad);
}

.chart-meter-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-1-5);
    font-size: var(--text-sm);
}

.chart-meter-label { color: var(--text-secondary); }

.chart-meter-value {
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

/* The unfilled part of the track uses the gridline colour rather than a
   sunken surface: on a dark theme `--surface-sunken` and the page behind it
   are the same value, and a track you cannot see is a bar chart with no
   scale. `--chart-grid` is the token for exactly this — quiet scaffolding
   that stays visible in both modes. */
.chart-meter-track {
    position: relative;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--chart-grid);
    overflow: hidden;
}

.chart-meter-fill {
    display: block;
    height: 100%;
    border-radius: var(--radius-full);
    transition: width var(--duration-slow) var(--ease-standard);
}

/* The target on a bullet chart: a rule you cross, not a quantity that
   accumulates, so it is drawn across the track rather than along it. */
.chart-meter-target {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    margin-left: -1px;
    background: var(--text-primary);
}

/* --- Heatmap ------------------------------------------------------------
   A table, because it is one: rows, columns and a value at each crossing. The
   cell colours are written as inline styles by charts.js, which is also the
   only reason there is no literal colour anywhere below. */

.chart-heatmap {
    padding: var(--card-pad-tight) var(--card-pad) var(--card-pad);
    overflow-x: auto;
}

/* Sized by its cells and centred, not stretched to the card. A heatmap read
   by colour wants cells of roughly one shape; a three-column one stretched to
   full width stops being a grid and starts looking like a bar chart. Wider
   ones overflow into the container's own scroll. */
.heatmap {
    width: auto;
    margin-inline: auto;
    border-collapse: separate;
    border-spacing: 3px;
    font-size: var(--text-sm);
}

.heatmap th {
    font-weight: var(--weight-medium);
    color: var(--text-tertiary);
    text-align: center;
    white-space: nowrap;
    padding: 0 var(--space-1);
}

.heatmap tbody th {
    text-align: right;
    padding-right: var(--space-2);
}

.heatmap td { padding: 0; }

.heatmap-cell {
    display: grid;
    place-items: center;
    min-width: 34px;
    height: 30px;
    border-radius: var(--radius-xs);
    background: var(--surface-sunken);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
}


/* 19 · Maps ===============================================================
   Leaflet, brought into the design system.

   Every rule below is an override of something the library ships, and they
   are all the same override: replace a value baked into leaflet.css with a
   token. That is the entire integration — Leaflet renders SVG paths and HTML
   markers into the DOM, so styling it is styling the page. There is no theme
   object to keep in step and nothing to rebuild when the mode flips.

   The rules are grouped by what they restyle rather than by selector, because
   the library's class names are not ours and reading them in its order is
   reading somebody else's file. */

.map {
    width: 100%;
    height: 320px;
    background: var(--map-water);

    /* Leaflet's panes run to a z-index of 800 and its controls to 1000, all
       set in leaflet.css. A stacking context here keeps that ladder inside
       the map, where a modal or a dropdown will still land on top of it. It
       is `isolation` rather than a z-index of our own so this file keeps its
       promise not to write one. */
    isolation: isolate;
}

.map--short { height: 220px; }
.map--tall { height: 460px; }

/* Filed with charts because it is the same job: a flush card, a padded frame
   and a canvas that fills it. */
.map-frame {
    position: relative;
    flex: 1;
    min-height: 0;
}

/* The line under a map that says how many records could not be drawn. Not
   optional furniture — a map missing seven of eighteen customers looks
   exactly like a map showing all of them, and this is the only thing on the
   screen that can tell the difference. */
.map-note {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    padding: var(--space-3) var(--card-pad);
    border-top: var(--border-width) solid var(--border-subtle);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

/* Every rule that restyles a Leaflet class is scoped `.map.leaflet-container`
   — both classes are on the same element, so it costs one selector and buys
   two things. It is a specificity floor: the library has rules of its own at
   two classes (`.leaflet-container .leaflet-control-attribution`,
   `.leaflet-touch .leaflet-bar a`) which would otherwise beat a plain
   `.leaflet-bar` here and win silently on touch devices only, which is the
   worst kind of bug to find. And it scopes the restyling to *our* maps, so a
   page that mounts a Leaflet instance some other way is left alone. */

.map.leaflet-container {
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    background: var(--map-water);
}

.map.leaflet-container a { color: var(--text-brand); }

/* --- The basemap ---------------------------------------------------------
   Leaflet writes `fill` and `stroke` onto each path as SVG presentation
   attributes, which any stylesheet rule outranks. That is why the layer can
   be handed a class and no colours at all, and why the world re-themes in the
   same repaint as the rest of the page. */

.map-land {
    fill: var(--map-land);
    fill-opacity: 1;
    stroke: var(--map-land-border);
    stroke-width: 1;
}

/* Inland water, cut back out of the land. The same colour as the sea, because
   it is the same substance and a lake in a different blue reads as a mistake. */
.map-lake {
    fill: var(--map-water);
    fill-opacity: 1;
    stroke: var(--map-land-border);
    stroke-width: 0.5;
}

/* States, provinces, counties. Hairline, lighter than a national border and
   never dashed: a dashed line on a map means "disputed" to anyone who reads
   maps, and Wisconsin is not disputed. The weight difference is what stops a
   map of the United States reading as fifty countries. */
.map-subdivision {
    fill: none;
    stroke: var(--map-subdivision-border);
    stroke-width: 1;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* A region carrying a value. The fill is computed — it is the one thing on a
   map this design system cannot express as a token, because it encodes a
   quantity rather than a role — so only the stroke is set here. */
.map-region {
    stroke: var(--map-land-border);
    stroke-width: 1;
    transition: fill-opacity var(--duration-fast) var(--ease-standard);
}

.map-region:hover { fill-opacity: 0.82; }

/* --- Place names ---------------------------------------------------------
   Cities and towns. Background, not content: they are here so a reader can
   tell which part of the world they are looking at without reading the pins,
   and the moment they compete with a pin for attention they have stopped
   doing that job. Hence a 3px dot, the smallest step on the type ramp, and
   the tertiary ink. */

.map-place {
    position: absolute;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    /* Anchored at the coordinate, so the dot sits on the place and the name
       runs off to the right of it. */
    transform: translate(-2px, -50%);
    white-space: nowrap;
}

.map-place-dot {
    width: 3px;
    height: 3px;
    border-radius: var(--radius-full);
    background: var(--map-place);
    flex: none;
}

.map-place-label {
    font-size: var(--text-2xs);
    line-height: 1;
    color: var(--map-place-label);
    /* The one place this design system reaches for a halo. A label crossing
       a coastline is unreadable against either side of it, and a halo is what
       every printed map has used for a century. It is drawn from the water
       colour so it disappears into whichever surface it lands on. */
    paint-order: stroke;
    -webkit-text-stroke: 3px var(--map-water);
}

/* Capitals earn a slightly stronger mark — a ring rather than a dot — because
   an earlier label alone does not say *why* the name appeared first. */
.map-place--capital .map-place-dot {
    width: 5px;
    height: 5px;
    background: transparent;
    border: var(--border-width) solid var(--map-place);
}

.map-place--capital .map-place-label { color: var(--map-place); }

/* --- Pins ----------------------------------------------------------------
   Colour rides on `color` so the shape and the dot both inherit it, which is
   the same trick `.status` uses and the reason the two agree about what
   "warning" looks like. */

.map-pin {
    position: relative;
    display: block;
    width: 20px;
    height: 20px;
    color: var(--action-primary-bg);
}

.map-pin::before {
    content: '';
    position: absolute;
    inset: 0;
    background: currentColor;
    border: var(--border-width) solid var(--surface-raised);
    border-radius: var(--radius-full) var(--radius-full) var(--radius-full) 0;
    transform: rotate(-45deg);
    box-shadow: var(--shadow-sm);
}

/* The second channel. A tone carried by hue alone is invisible to roughly one
   reader in twelve, and two pins of different meaning sitting side by side is
   exactly the case where that matters. */
.map-pin-dot {
    position: absolute;
    top: 6px;
    left: 6px;
    width: 8px;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--surface-raised);
}

.map-pin--brand   { color: var(--action-primary-bg); }
.map-pin--neutral { color: var(--text-tertiary); }
.map-pin--success { color: var(--text-success); }
.map-pin--warning { color: var(--text-warning); }
.map-pin--danger  { color: var(--text-danger); }
.map-pin--info    { color: var(--text-info); }

.map-pin.is-selected::before {
    border-color: var(--focus-ring);
    box-shadow: var(--shadow-md);
}

/* Leaflet moves focus to a marker on tab. The ring has to come from the
   design system's one focus treatment, not from a browser outline that
   ignores the rotation and boxes the square instead of the pin. */
.map.leaflet-container .leaflet-marker-icon:focus-visible {
    outline: none;
}

.map.leaflet-container .leaflet-marker-icon:focus-visible .map-pin::before {
    box-shadow: 0 0 0 3px var(--focus-ring);
}

/* --- Popups & tooltips --------------------------------------------------- */

.map.leaflet-container .leaflet-popup-content-wrapper {
    background: var(--surface-overlay);
    color: var(--text-primary);
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.map.leaflet-container .leaflet-popup-content {
    margin: var(--card-pad-tight);
    font-size: var(--text-sm);
    line-height: var(--leading-base);
}

.map.leaflet-container .leaflet-popup-tip {
    background: var(--surface-overlay);
    border: var(--border-width) solid var(--border-default);
    box-shadow: none;
}

.map.leaflet-container a.leaflet-popup-close-button {
    color: var(--text-tertiary);
    padding: var(--space-2) var(--space-2) 0 0;
}

.map.leaflet-container a.leaflet-popup-close-button:hover {
    color: var(--text-primary);
    background: transparent;
}

.map-popup-title {
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
}

.map-popup-sub,
.map-popup-address {
    color: var(--text-secondary);
    margin-top: var(--space-1);
}

.map-popup-address { white-space: pre-line; }

.map-popup-meta {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-1) var(--space-3);
    margin: var(--space-2) 0 0;
    font-size: var(--text-xs);
}

.map-popup-meta dt { color: var(--text-tertiary); }
.map-popup-meta dd { margin: 0; color: var(--text-primary); font-variant-numeric: tabular-nums; }

.map.leaflet-container .leaflet-tooltip.map-tooltip {
    background: var(--text-primary);
    color: var(--surface-raised);
    border: none;
    border-radius: var(--radius-xs);
    box-shadow: var(--shadow-md);
    padding: var(--space-1-5) var(--space-2);
    font-size: var(--text-xs);
}

.map.leaflet-container .leaflet-tooltip.map-tooltip::before { display: none; }

/* --- Controls ------------------------------------------------------------
   The zoom buttons and the attribution line are the two places a stock
   Leaflet map most obviously stops looking like the product it is in. */

.map.leaflet-container .leaflet-bar {
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
}

.map.leaflet-container .leaflet-bar a,
.map.leaflet-container .leaflet-bar a:hover {
    background: var(--surface-raised);
    color: var(--text-secondary);
    border-bottom-color: var(--border-subtle);
    width: var(--control-height-sm);
    height: var(--control-height-sm);
    line-height: var(--control-height-sm);
    font-weight: var(--weight-normal);
}

.map.leaflet-container .leaflet-bar a:hover { background: var(--surface-hover); color: var(--text-primary); }

.map.leaflet-container .leaflet-bar a:first-child { border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.map.leaflet-container .leaflet-bar a:last-child { border-radius: 0 0 var(--radius-sm) var(--radius-sm); }

.map.leaflet-container .leaflet-bar a.leaflet-disabled {
    background: var(--surface-disabled);
    color: var(--text-disabled);
}

.map.leaflet-container .leaflet-control-attribution {
    background: var(--surface-raised);
    color: var(--text-tertiary);
    border-radius: var(--radius-xs) 0 0 0;
    padding: 2px var(--space-1-5);
    font-size: var(--text-2xs);
}

.map.leaflet-container .leaflet-control-attribution a { color: var(--text-tertiary); }
.map.leaflet-container .leaflet-control-attribution a:hover { color: var(--text-secondary); }

/* --- Legend --------------------------------------------------------------
   HTML rather than anything the library would draw, for the same reason the
   chart legends are: it has to use the product's type ramp. */

.map-legend {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2) var(--space-2-5);
    background: var(--surface-raised);
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

.map-legend-title {
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
}

.map-legend-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5);
}

.map-legend-swatch {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-xs);
    flex: none;
}

.map-legend-swatch--brand   { background: var(--action-primary-bg); }
.map-legend-swatch--neutral { background: var(--text-tertiary); }
.map-legend-swatch--success { background: var(--text-success); }
.map-legend-swatch--warning { background: var(--text-warning); }
.map-legend-swatch--danger  { background: var(--text-danger); }
.map-legend-swatch--info    { background: var(--text-info); }


/* 20 · Boards & timelines =================================================
   A kanban board and a gantt chart: the two layouts every product reaches for
   once it has work to show rather than numbers to report. Neither is a chart —
   nobody reads a value off them — and both live in the design system anyway,
   because a board built inside one page's <style> block is a board that stops
   following the theme the first time somebody changes the brand colour.

   Their interaction vocabulary is the product's, not their own. That is the
   part that makes a board feel like the rest of the site rather than like a
   widget dropped into it:

     hover      --surface-hover     the tint a table row already uses
     selected   --surface-selected  the tint the active nav row already uses
     picked up  --border-brand + --surface-selected
     dragging   --surface-raised at --shadow-lg — the modal's own elevation
     focus      the global :focus-visible outline, untouched
     accent     --chart-1…12, in order, so a column and the chart beside it
                name the same thing in the same colour
     motion     --duration-fast / --ease-standard, and nothing animates a
                position — a card follows the pointer at pointer speed

   Every colour that varies by data is written as an inline style by charts.js,
   which is why there is not one literal colour below. */

/* --- Kanban ------------------------------------------------------------- */

.kanban { position: relative; }

/* The board scrolls sideways rather than squeezing: six columns at 90px each
   is not a board, it is a set of tooltips. */
.kanban-board {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--card-pad-tight) var(--card-pad) var(--card-pad);
    overflow-x: auto;
    scroll-snap-type: x proximity;
}

.kanban-col {
    flex: 0 0 auto;
    width: 248px;
    max-width: 80vw;
    display: flex;
    flex-direction: column;
    min-width: 0;
    scroll-snap-align: start;
}

.kanban-col-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-1) var(--space-2);
}

.kanban-col-dot {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-full);
    flex: none;
}

.kanban-col-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kanban-col-count {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
    flex: none;
}

/* A work-in-progress limit that has been passed is the one thing on a board
   that is genuinely wrong, so it gets the only semantic colour here. */
.kanban-col-count--over {
    color: var(--text-danger);
    font-weight: var(--weight-semibold);
}

/* The column's colour as a rule under its name, not a filled header. Six
   saturated headers in a row read as six alerts; a 2px rule says the same
   thing and leaves the cards as the loudest thing on the board. */
.kanban-col-rule {
    height: 2px;
    border-radius: var(--radius-full);
    flex: none;
}

.kanban-list {
    list-style: none;
    margin: 0;
    padding: var(--space-2) 0 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-height: 64px;
    border-radius: var(--radius-md);
    transition: background var(--duration-fast) var(--ease-standard);
}

/* While a card is in the air every list is a target, and says so. Without
   this the only feedback is the card itself, and a drop into the gap between
   two columns looks exactly like a drop into one. */
.kanban[data-kanban-dragging] .kanban-list { background: var(--surface-hover); }

.kanban-card {
    position: relative;
    display: flex;
    gap: var(--space-1-5);
    padding: var(--space-2) var(--space-2-5) var(--space-2) var(--space-1);
    background: var(--surface-raised);
    border: var(--border-width) solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-xs);
    cursor: grab;
    /* A card must not swallow a touch scroll — the grip below opts into that
       deliberately, and it is the only part of the card that does. */
    touch-action: manipulation;
    transition: background var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard),
                box-shadow var(--duration-fast) var(--ease-standard);
}

.kanban-card:hover {
    background: var(--surface-hover);
    border-color: var(--border-default);
    box-shadow: var(--shadow-sm);
}

/* Dots rather than an icon: the grip is drawn by the stylesheet so it inherits
   the text colour and costs no request. */
.kanban-grip {
    flex: none;
    width: 8px;
    align-self: stretch;
    color: var(--text-placeholder);
    background-image: radial-gradient(currentColor 1px, transparent 1px);
    background-size: 4px 4px;
    background-position: center;
    opacity: 0.55;
    cursor: grab;
    touch-action: none;
    transition: opacity var(--duration-fast) var(--ease-standard);
}

.kanban-card:hover .kanban-grip { opacity: 1; }

/* A board that is only there to be read. Nothing on it suggests otherwise —
   the grip is gone with the drag, and the cursor stops promising a grab. */
.kanban--static .kanban-card { cursor: default; }
.kanban--static .kanban-card:hover { box-shadow: var(--shadow-xs); }

.kanban-card-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5);
}

.kanban-card-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    line-height: var(--leading-sm);
    color: var(--text-primary);
}

.kanban-card-meta {
    font-size: var(--text-xs);
    color: var(--text-tertiary);
}

.kanban-card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.kanban-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.kanban-card-due {
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
    margin-left: auto;
}

/* The same track as a meter, at a card's scale. */
.kanban-card-progress {
    height: 4px;
    border-radius: var(--radius-full);
    background: var(--chart-grid);
    overflow: hidden;
}

.kanban-card-progress span {
    display: block;
    height: 100%;
    border-radius: var(--radius-full);
    transition: width var(--duration-slow) var(--ease-standard);
}

/* In the air. Fixed rather than absolute, because a card dragged inside a
   scrolling column is a card clipped by the first edge it meets, and
   pointer-events off so the board can see what is underneath it.

   Doubled class deliberately: a card is under the pointer for the whole drag,
   and a single class loses to `.kanban-card:hover` — which is translucent, so
   the card in your hand goes see-through over whatever it passes. */
.kanban-card.kanban-card--dragging {
    position: fixed;
    z-index: var(--z-popover);
    pointer-events: none;
    cursor: grabbing;
    background: var(--surface-raised);
    border-color: var(--border-brand);
    box-shadow: var(--shadow-lg);
    transform: rotate(1.5deg);
}

/* Picked up with the keyboard. The same state, said without the pointer, and
   doubled for the same reason: held has to beat hover. */
.kanban-card.kanban-card--held {
    background: var(--surface-selected);
    border-color: var(--border-brand);
    box-shadow: var(--shadow-md);
}

.kanban-drop {
    border: var(--border-width) dashed var(--border-brand);
    border-radius: var(--radius-md);
    background: var(--surface-brand-subtle);
}

.kanban-empty-slot {
    display: grid;
    place-items: center;
    padding: var(--space-4) var(--space-2);
    border: var(--border-width) dashed var(--border-subtle);
    border-radius: var(--radius-md);
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    text-align: center;
}

/* --- Gantt --------------------------------------------------------------
   Rows of work against one calendar. Built on a two-column grid — names, then
   a track — so the names stay put while the calendar scrolls, and so a bar is
   positioned as a percentage of the span rather than in pixels nobody can
   recompute when the card is resized.

   `--gantt-label-w` is the one knob a page may turn locally: a timeline of
   twelve-word phase names needs a wider name column than one of dates. */

.gantt { position: relative; }

.gantt-scroll {
    overflow-x: auto;
    padding: var(--card-pad-tight) var(--card-pad) var(--card-pad);
}

.gantt-frame { min-width: 520px; }

.gantt-head,
.gantt-row {
    display: grid;
    grid-template-columns: var(--gantt-label-w, 152px) minmax(0, 1fr);
    gap: var(--space-3);
    align-items: center;
}

.gantt-head {
    padding-bottom: var(--space-1);
    margin-bottom: var(--space-1);
    border-bottom: var(--border-width) solid var(--border-subtle);
}

.gantt-head-label {
    font-size: var(--text-2xs);
    text-transform: var(--eyebrow-transform);
    letter-spacing: var(--eyebrow-tracking);
    color: var(--text-tertiary);
}

/* Two lines' worth: the today flag above, the tick labels below it, so a
   marker landing on a date never sits on top of that date's label. */
.gantt-head-track {
    position: relative;
    height: 32px;
}

.gantt-tick {
    position: absolute;
    bottom: 0;
    transform: translateX(3px);
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.gantt-today-flag {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    color: var(--text-brand);
    white-space: nowrap;
}

.gantt-row {
    border-radius: var(--radius-sm);
    transition: background var(--duration-fast) var(--ease-standard);
}

.gantt-row:hover { background: var(--surface-hover); }
.gantt-row.is-selected { background: var(--surface-selected); }

.gantt-row-label {
    display: flex;
    flex-direction: column;
    min-width: 0;
    padding-left: var(--space-2);
}

.gantt-row-name {
    font-size: var(--text-sm);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gantt-row--group .gantt-row-name { font-weight: var(--weight-semibold); }

.gantt-row-meta {
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gantt-track {
    position: relative;
    height: 34px;
}

.gantt-lines {
    position: absolute;
    inset: 0;
}

.gantt-line {
    position: absolute;
    top: 0;
    bottom: 0;
    width: var(--border-width);
    background: var(--chart-grid);
}

/* Today is a rule you cross, drawn the same way the bullet chart draws its
   target — across the track rather than along it. */
.gantt-today {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    margin-left: -1px;
    background: var(--border-brand);
}

.gantt-bar {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    height: 22px;
    min-width: 8px;
    display: flex;
    align-items: center;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: box-shadow var(--duration-fast) var(--ease-standard);
}

.gantt-bar:hover { box-shadow: var(--shadow-sm); }
.gantt-row.is-selected .gantt-bar { box-shadow: var(--shadow-md); }

/* Progress is the same colour, stronger — not a second colour. Both ends stay
   light enough for --text-primary to sit on them in either mode, which is why
   the label can live inside the bar at all. */
.gantt-bar-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    transition: width var(--duration-slow) var(--ease-standard);
}

.gantt-bar-label {
    position: relative;
    padding-inline: var(--space-2);
    font-size: var(--text-2xs);
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A moment, not a duration: a diamond has no length to misread as one. */
.gantt-milestone {
    position: absolute;
    top: 50%;
    width: 11px;
    height: 11px;
    margin-left: -6px;
    border-radius: var(--radius-xs);
    transform: translateY(-50%) rotate(45deg);
    cursor: pointer;
}


/* 21 · Utilities ==========================================================
   A deliberately short list. Utilities are for one-off adjustments; anything
   used more than a few times should become a component class instead. */

.row { display: flex; align-items: center; gap: var(--space-2); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.row-wrap { flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: var(--stack-gap); }
.stack-tight { display: flex; flex-direction: column; gap: var(--space-1); }
.grow { flex: 1; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Keyboard users get to the content without tabbing the whole nav. */
.skip-link {
    position: absolute;
    left: var(--space-3);
    top: var(--space-3);
    z-index: var(--z-tooltip);
    transform: translateY(-200%);
    background: var(--surface-overlay);
    border: var(--border-width) solid var(--border-default);
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
    box-shadow: var(--shadow-md);
}

.skip-link:focus { transform: none; }


/* 22 · Motion & accessibility ============================================= */

@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;
    }
}

@media (prefers-contrast: more) {
    :root {
        --border-subtle: var(--border-strong);
        --border-default: var(--border-strong);
    }
}

/* Responsive ------------------------------------------------------------ */

@media (max-width: 1024px) {
    .sidebar {
        position: fixed;
        left: 0;
        top: 0;
        transform: translateX(-100%);
        transition: transform var(--duration-base) var(--ease-standard);
        box-shadow: var(--shadow-xl);
    }

    [data-sidebar="open"] .sidebar { transform: none; }

    .sidebar-scrim {
        position: fixed;
        inset: 0;
        background: var(--surface-backdrop);
        z-index: calc(var(--z-sticky) - 1);
        display: none;
    }

    [data-sidebar="open"] .sidebar-scrim { display: block; }
}

.sidebar-toggle {
    position: fixed;
    top: var(--space-3);
    left: var(--space-3);
    z-index: calc(var(--z-sticky) + 1);
    box-shadow: var(--shadow-md);
}

@media (max-width: 1024px) {
    /* Keep the floating button clear of the first line of content. */
    [data-nav="sidebar"] .app-content {
        padding-top: calc(var(--page-pad-y) + var(--space-10));
    }
}

@media (min-width: 1025px) {
    .sidebar-toggle { display: none; }
    .sidebar-scrim { display: none; }
}

@media (max-width: 640px) {
    .app-content { padding-inline: var(--space-4); }
    .topbar { padding-inline: var(--space-4); }
    .page-header { flex-direction: column; align-items: stretch; }
    .stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
    .toast-region { left: var(--space-4); right: var(--space-4); bottom: var(--space-4); }
    .toast { min-width: 0; max-width: none; }
    .modal { width: calc(100vw - var(--space-4)); }
}

/* design-lint-disable: print has no theme — paper is white, ink is black, and
   a token that resolves against the screen's colour mode would come out wrong
   on half of all printers. */
@media print {
    .sidebar, .topbar, .btn, .toast-region { display: none !important; }
    .app-content { padding: 0; }
    .card { border: 1px solid #ccc; box-shadow: none; }
}
/* design-lint-enable */
