/* ============================================================================
   Design tokens - DELIBERATELY COPIED from the desktop app, not shared.

   The values below are transcribed from app/app/Kodzonia.App/wwwroot/css/app.css:
   :root here mirrors the desktop's LIGHT brand block (--kdz-bg-light,
   --kdz-bg-white, --kdz-bg-muted, --kdz-border, --kdz-text-dark,
   --kdz-text-muted, --kdz-purple, --kdz-accent, --kdz-red, --kdz-green), and the
   override block below restores the panel's own already-approved DARK palette.

   WHY LIGHT IS THE BASE, and not merely one of two equal blocks: Light is the
   product default for an authenticated reader, so it is the palette that must
   survive a stylesheet that fails to cascade, a token that was forgotten in the
   override, and the split second before the override block is even parsed.

   THE DARK VALUES ARE UNCHANGED. They shipped and were reviewed against the
   panel's own screens; mirroring the desktop's LIGHT palette is not a licence to
   repaint them, so every dark value below is the one that was already here.

   The NAMES stay --wk-*, which is this stylesheet's own prefix and what every
   page already uses (.wk-card, .wk-field, .wk-input, .wk-button, .wk-table);
   the desktop names its tokens --kdz-*. Only the palette crossed over.

   FOLLOW-UP, NOT THIS PHASE: extracting a Razor Class Library shared by the MAUI
   app and this host. Until that exists these values are maintained BY HAND, and
   a colour changed in app/ does NOT change here. That is the accepted cost of
   shipping a panel that looks like the product before the design system is a
   package.

   COMPONENT RULES ARE WRITTEN ONCE. Every rule under the token blocks consumes
   var(--wk-*) and is never forked per theme; the themed selector appears exactly
   once in this file, and a unit test counts it. Two copies of a rule is how a
   control ends up restyled in one theme and forgotten in the other.

   NO ICONS. Emoji and glyph icons are banned repository-wide (there is an
   architecture test for it in the desktop app), IconCatalog is not referenced
   server-side, and nothing in this stylesheet emits a glyph. The only ornament
   is --wk-brand-gradient on the wordmark's tail.
   ============================================================================ */
:root {
    /* Brand - the logo gradient: violet -> blue -> cyan (--kdz-brand-gradient).
       Theme-independent: the identity is the same on both palettes. */
    --wk-brand-gradient: linear-gradient(135deg, #A855F7 0%, #3B82F6 50%, #06B6D4 100%);

    /* Surfaces (--kdz-bg-light / --kdz-bg-white / --kdz-bg-muted / --kdz-border). */
    --wk-bg: #F6F7FB;
    --wk-surface: #FFFFFF;
    --wk-surface-raised: #FFFFFF;
    --wk-surface-muted: #F0F2F8;
    --wk-sidebar-bg: #FFFFFF;
    --wk-border: #E3E7F0;

    /* Text (--kdz-text-dark / --kdz-text-muted). On the light palette the body
       text and the strongest heading share the darkest ink the desktop defines;
       the hierarchy between them is carried by SIZE and WEIGHT, which is how the
       desktop reads too, and inventing a darker value here would fork the copy. */
    --wk-text: #0E1320;
    --wk-text-strong: #0E1320;
    --wk-muted: #5B6478;

    /* Primary indigo (--kdz-purple) - links, focus, the active nav item. */
    --wk-purple: #5457E8;
    --wk-purple-hover: #4346C9;

    /* The focus halo, derived from the primary indigo of each palette. */
    --wk-focus-ring: rgba(84,87,232,.35);

    /* Action / CTA cyan (--kdz-accent). Still the accent HUE for tints and the hub
       card's non-default spine, but no longer the fill of a solid button - see
       --wk-button-primary below. */
    --wk-accent: #0891B2;
    --wk-accent-hover: #0E7490;

    /* SOLID ACTION SURFACES, TOKENISED PER THEME AND CHOSEN FOR THEIR OWN LABEL.
       The buttons used to be painted with --wk-accent, which was the ONE token whose
       value was identical in both palettes: a dark page therefore rendered a
       light-theme control, which is exactly why they read as pasted in from another
       system. Worse, white on #0891B2 measures 3.68:1 and white on #EF4444 measures
       3.76:1 - both BELOW the 4.5:1 that normal text requires, so the caption was
       failing the reader at the precise moment they had to act.

       The primary action now carries the brand indigo the wordmark and the sidebar's
       active indicator already use, so an action looks like it belongs to this product.
       Contrast against white: 7.10 and 7.76 here, 5.38 and 6.28 on Dark. Destructive
       actions keep red but at 4.83 and 5.74. A unit test recomputes every ratio from
       these hexes, so a later retint that dips under AA fails instead of shipping. */
    --wk-button-primary: #4346C9;
    --wk-button-primary-hover: #3E41BE;
    --wk-button-danger: #DC2626;
    --wk-button-danger-hover: #B91C1C;

    /* Status (--kdz-red / --kdz-green). */
    --wk-danger: #EF4444;
    --wk-danger-hover: #F05252;
    --wk-success: #22C55E;

    /* WARNING AND INFO FOREGROUNDS, added when the governance alert band needed a
       coloured EDGE and not just a wash. Until then only the -tint halves existed,
       because nothing painted a warning or info hue as a foreground - a border that
       referenced a non-existent token would simply render transparent. The hexes are
       the exact rgb the tints below already carry, so the pair cannot drift. */
    --wk-warning: #FBBF24;
    --wk-info: #0891B2;

    /* DESTRUCTIVE INK FOR SMALL TEXT, WHICH IS NOT THE DESTRUCTIVE HUE. --wk-danger is a
       BORDER and ICON colour: on this palette's white card it measures 3.76:1, below the
       4.5:1 normal text requires, so a field error painted with it fails exactly the
       reader it exists to warn. This token is the ink a sentence may use, and it forks
       per palette for the same reason the solid fills do - #DC2626 reads on white (4.83:1)
       and drowns on #11161F, where the lighter #EF4444 is the legible one. A unit test
       recomputes both ratios from these hexes, so a later retint fails instead of shipping.

       The error halo mirrors --wk-focus-ring above: same derivation from the palette's own
       status hue, same opacity split (a dark page needs the stronger wash to read as a halo). */
    --wk-danger-text: #DC2626;
    --wk-danger-focus-ring: rgba(239,68,68,.35);

    /* Subtle status washes for chips and status lines; the hues above stay the
       foreground, these are the backgrounds they sit on. */
    --wk-success-tint: rgba(34,197,94,.12);
    --wk-warning-tint: rgba(251,191,36,.16);
    --wk-danger-tint: rgba(239,68,68,.10);
    --wk-info-tint: rgba(8,145,178,.12);

    /* THEME-INDEPENDENT tokens live ONLY here, like --wk-brand-gradient above:
       radii and the monospace stack are the same on both palettes, so the dark
       override does not repeat them. */
    --wk-radius: 8px;
    --wk-radius-sm: 6px;
    --wk-radius-lg: 12px;
    --wk-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

/* The panel's own dark palette, RESTORED RATHER THAN REDESIGNED - see the header
   comment. This block redefines tokens and nothing else: a component rule here
   would be a second copy of a rule that already exists below. */
html[data-theme="dark"] {
    --wk-bg: #0A0D14;
    --wk-surface: #11161F;
    --wk-surface-raised: #171E2B;
    --wk-surface-muted: #0E131C;
    --wk-sidebar-bg: #0E131C;
    --wk-border: #222A3A;

    --wk-text: #E7EAF3;
    --wk-text-strong: #FFFFFF;
    --wk-muted: #8A93A8;

    --wk-purple: #6366F1;
    --wk-purple-hover: #7C7FF5;

    --wk-focus-ring: rgba(99,102,241,.45);

    --wk-accent: #0891B2;
    --wk-accent-hover: #0E7490;

    /* The dark half of the action surfaces, explained by the base block above. The
       indigo is the Light primary, which separates from a #11161F card instead of
       sinking into it, and hover DEEPENS rather than lifting - #6366F1 measures
       4.47 against white, missing AA by three hundredths, so the interaction state
       darkens to keep the same text readable. Ratios against white, 5.38 and 6.28. */
    --wk-button-primary: #5457E8;
    --wk-button-primary-hover: #4A4DD8;
    --wk-button-danger: #DC2626;
    --wk-button-danger-hover: #C81E1E;

    --wk-danger: #EF4444;
    --wk-danger-hover: #F05252;
    --wk-success: #4ADE80;

    /* The dark half LIFTS both hues, the same way --wk-success goes from #22C55E to
       #4ADE80 on this palette. The light-palette warning and info sink into a
       #11161F card, and an alert edge that cannot be seen is an alert nobody raised. */
    --wk-warning: #FCD34D;
    --wk-info: #22D3EE;

    /* The dark halves of the destructive ink and halo - see the base block. The LIGHTER red
       is the legible one here (4.56:1 on #11161F, against #DC2626's 3.77:1), which is the
       mirror of the Light choice rather than a repeat of it. */
    --wk-danger-text: #EF4444;
    --wk-danger-focus-ring: rgba(239,68,68,.45);

    --wk-success-tint: rgba(74,222,128,.14);
    --wk-warning-tint: rgba(251,191,36,.14);
    --wk-danger-tint: rgba(239,68,68,.14);
    --wk-info-tint: rgba(8,145,178,.18);
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: "Work Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: 15px;
    background: var(--wk-bg);
    color: var(--wk-text);
}

/* Heading hierarchy. Every page (ProjectSettings above all) relies on h1/h2 for
   structure; without these rules every heading fell back to the browser default,
   which is why the panel read as unstyled even though every class below IS defined.
   h2 doubles as the section separator the Models/Budgets/Chat/... blocks need. */
h1 {
    margin: 0 0 0.5rem;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--wk-text-strong);
}

h2 {
    margin: 2rem 0 1rem;
    padding-bottom: 0.5rem;
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--wk-text-strong);
    border-bottom: 1px solid var(--wk-border);
}

.wk-card > h2:first-child { margin-top: 0; }

h3 {
    margin: 1rem 0 0.5rem;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--wk-text);
}

/* ============================================================================
   APP SHELL (Components/Layout/MainLayout.razor + NavMenu.razor + AuthLayout.razor)
   "Mission Control": a 240px left sidebar, a sticky topbar and a content column.
   The grid's first column is sized in px because the sidebar is a fixed piece of
   chrome, not a fluid region; the content column takes the rest and never shrinks
   below its content (minmax(0, 1fr) lets wide tables scroll INSIDE the column
   instead of pushing the sidebar off the page).
   ============================================================================ */
.wk-shell {
    min-height: 100vh;
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
}

.wk-column {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* THE COLLAPSE STATE LIVES IN A CHECKBOX, NOT IN JAVASCRIPT. The input is the
   first child of .wk-shell so the :checked ~ .wk-sidebar sibling selector (in the
   1024px query below) can reach the drawer; it is visually hidden but NEVER
   display:none, which would also remove it from the tab order. */
.wk-nav-toggle {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* The label that stands in for the checkbox. Hidden on wide viewports, where the
   sidebar is always on; the 1024px query turns it into the menu button. */
.wk-nav-toggle-button {
    display: none;
    align-items: center;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius);
    background: var(--wk-surface);
    color: var(--wk-muted);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    transition: color 120ms ease, border-color 120ms ease;
}

.wk-nav-toggle-button:hover { color: var(--wk-text); border-color: var(--wk-muted); }

.wk-brand { color: var(--wk-text); font-weight: 700; text-decoration: none; flex-shrink: 0; }

/* The wordmark's tail, painted with the brand gradient - the desktop's .kdz-brand-text.
   The identity is the WORDMARK; the coloured monogram is deliberately NOT reproduced here.
   One of the THREE sanctioned gradient surfaces (the others: the sidebar's 2px active
   indicator and the auth shell's hairline) - a unit test counts them. */
.wk-brand-text {
    background: var(--wk-brand-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

/* ===== Sidebar (NavMenu.razor, rendered only for an authenticated visitor) =====
   Sticky full-height column; the groups scroll while the footer (theme, user,
   sign-out) stays reachable at the end of the column. */
.wk-sidebar {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    padding: 1rem 0.75rem 0.75rem;
    background: var(--wk-sidebar-bg);
    border-right: 1px solid var(--wk-border);
    overflow-y: auto;
    transition: transform 160ms ease;
}

.wk-sidebar .wk-brand { display: inline-block; padding: 0.25rem 0.5rem 1rem; font-size: 1.05rem; }

/* Project switcher - a CSS-only <details> disclosure; no script, no circuit. */
.wk-switcher { margin-bottom: 1.25rem; }

.wk-switcher-current {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem;
    background: var(--wk-surface);
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius);
    cursor: pointer;
    list-style: none;
    transition: border-color 120ms ease, background-color 120ms ease;
}

.wk-switcher-current::-webkit-details-marker { display: none; }
.wk-switcher-current::marker { content: none; }

.wk-switcher-current:hover { border-color: var(--wk-muted); }

.wk-switcher-current:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: 2px;
}

.wk-switcher-name {
    flex: 1;
    min-width: 0;
    font-family: var(--wk-font-mono);
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wk-switcher-chev { color: var(--wk-muted); font-size: 0.75rem; transition: transform 120ms ease; }
.wk-switcher[open] .wk-switcher-chev { transform: rotate(180deg); }

.wk-switcher-menu {
    margin-top: 0.25rem;
    padding: 0.25rem;
    background: var(--wk-surface-raised);
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius);
}

.wk-switcher-item {
    display: block;
    padding: 0.35rem 0.5rem;
    border-radius: var(--wk-radius-sm);
    color: var(--wk-muted);
    text-decoration: none;
    font-size: 0.85rem;
    transition: color 120ms ease, background-color 120ms ease;
}

.wk-switcher-item:hover { color: var(--wk-text); background: var(--wk-surface-muted); }

.wk-switcher-item:focus-visible { outline: 2px solid var(--wk-purple); outline-offset: -2px; }

/* Nav groups. The labels are the kodzonia.com signature: mono uppercase eyebrows.
   text-transform does the shouting, so the markup stays sentence case. */
.wk-navgroups { flex: 1; }

.wk-navgroup { margin-bottom: 1.25rem; }

.wk-navgroup-label {
    margin: 0;
    padding: 0 0.6rem 0.5rem;
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--wk-muted);
}

.wk-navitem {
    position: relative;
    display: block;
    padding: 0.45rem 0.6rem;
    margin-bottom: 2px;
    border-radius: var(--wk-radius-sm);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--wk-muted);
    text-decoration: none;
    transition: color 120ms ease, background-color 120ms ease;
}

.wk-navitem:hover { color: var(--wk-text); background: var(--wk-surface-muted); }

.wk-navitem:focus-visible { outline: 2px solid var(--wk-purple); outline-offset: -2px; }

.wk-navitem.is-active { color: var(--wk-text); background: var(--wk-surface-muted); }

/* THE ONLY GRADIENT SURFACE IN THE NAV: the 2px active indicator bar, one of the
   three sanctioned gradient places in this stylesheet. The bar hangs outside the
   item's padding box (negative left equal to the sidebar's own padding), so it
   reads as a spine mark rather than as an underline. */
.wk-navitem.is-active::before {
    content: "";
    position: absolute;
    left: -0.75rem;
    top: 20%;
    bottom: 20%;
    width: 2px;
    border-radius: 2px;
    background: var(--wk-brand-gradient);
}

/* Settings sub-items (the category spokes): a rail to the left, smaller type. */
.wk-navsub {
    margin: 2px 0 6px 1.4rem;
    padding-left: 0.6rem;
    border-left: 1px solid var(--wk-border);
}

.wk-navsub-item {
    display: block;
    padding: 0.25rem 0.5rem;
    border-radius: var(--wk-radius-sm);
    font-size: 0.8rem;
    color: var(--wk-muted);
    text-decoration: none;
    transition: color 120ms ease, background-color 120ms ease;
}

.wk-navsub-item:hover { color: var(--wk-text); background: var(--wk-surface-muted); }

.wk-navsub-item.is-active { color: var(--wk-text); background: var(--wk-surface-muted); }

.wk-navsub-item:focus-visible { outline: 2px solid var(--wk-purple); outline-offset: -2px; }

/* Sidebar footer: theme form, user card, sign-out. */
.wk-sidebar-footer {
    border-top: 1px solid var(--wk-border);
    padding-top: 0.75rem;
}

.wk-user-card {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
    padding: 0.4rem 0.6rem;
    font-size: 0.85rem;
}

.wk-user-name {
    color: var(--wk-text);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.wk-user-role {
    font-family: var(--wk-font-mono);
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.15em;
    color: var(--wk-muted);
    flex-shrink: 0;
}

/* Both footer forms (theme and sign-out) are real <form method="post"> elements,
   so their controls are restyled to read as chrome rather than inheriting .wk-button. */
.wk-nav-form { margin: 0; }

.wk-nav-signout {
    width: 100%;
    padding: 0.4rem 0.6rem;
    background: none;
    border: none;
    border-radius: var(--wk-radius-sm);
    color: var(--wk-muted);
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    text-align: left;
    transition: color 120ms ease, background-color 120ms ease;
}

.wk-nav-signout:hover { color: var(--wk-danger); background: var(--wk-danger-tint); }

/* ===== Topbar (MainLayout.razor) =====
   THE BAR IS FULL-BLEED, ITS CONTENTS ARE NOT. The bar used to be both: a full-width
   strip with padding: 0 2rem, while .wk-content centres at max-width 1180px below it. On a
   wide window that put the breadcrumb hard against the left edge and the pill against the
   right, with roughly a thousand pixels of nothing between them and NEITHER of them lined
   up with the card they introduce. The surface and the hairline still span the window - a
   chrome band that stopped at 1180px would read as a floating box - and .wk-topbar-inner
   below carries the content column's own measure.

   AND IT HAS A SURFACE NOW. The fill was var(--wk-bg), the PAGE colour, so a bar whose
   middle is mostly empty had nothing to distinguish it from the page that scrolls under it.
   var(--wk-surface) is the same colour the cards use, which is what makes the bar read as
   the chrome it is; the border-bottom stays, because the surface separates the bar from the
   page rather than replacing the hairline that already did.

   THE STICKY CONTRACT IS UNCHANGED, and the opaque fill is what finally makes it correct:
   content now passes BEHIND the bar instead of showing through it. z-index 20 is deliberate
   and bounded on both sides - above the sticky save bar (10), which must scroll under the
   bar, and below the sidebar drawer (30), which must slide over it. min-height 3.5rem is
   load bearing too: .wk-card[id]'s scroll-margin-top: 4.5rem is measured against it. */
.wk-topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    min-height: 3.5rem;
    background: var(--wk-surface);
    border-bottom: 1px solid var(--wk-border);
}

/* The measure the bar's CONTENTS read at - the same max-width and the same centring as
   .wk-content, so the breadcrumb starts exactly where the page content starts. The row
   layout (flex, centred items, the 1rem gap and the 2rem inset) moved here from the bar
   above: the pill's margin-left: auto has to resolve against THIS box to sit at the end of
   the measure instead of at the end of the window. A unit test compares the two max-widths
   against each other rather than against a literal, so they cannot drift apart. */
.wk-topbar-inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    width: 100%;
    max-width: 1180px;
    min-height: 3.5rem;
    margin: 0 auto;
    padding: 0 2rem;
}

.wk-breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    font-size: 0.85rem;
    color: var(--wk-muted);
}

.wk-breadcrumb a { color: var(--wk-muted); text-decoration: none; }
.wk-breadcrumb a:hover { color: var(--wk-text); }

.wk-breadcrumb-sep { font-family: var(--wk-font-mono); color: var(--wk-muted); }

.wk-breadcrumb-here {
    color: var(--wk-text);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The search pill is a PLAIN ANCHOR to the settings hub until the deferred
   command palette claims the slot; styled as a pill so the swap is invisible. */
.wk-search-pill {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-left: auto;
    padding: 0.45rem 0.9rem;
    min-width: 15rem;
    border: 1px solid var(--wk-border);
    border-radius: 999px;
    background: var(--wk-surface-muted);
    color: var(--wk-muted);
    font-size: 0.8rem;
    text-decoration: none;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.wk-search-pill:hover { border-color: var(--wk-muted); }

.wk-search-pill:focus-visible {
    border-color: var(--wk-purple);
    box-shadow: 0 0 0 3px var(--wk-focus-ring);
    outline: none;
}

.wk-search-pill-text { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.wk-search-pill-kbd {
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    color: var(--wk-muted);
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius-sm);
    padding: 0.1rem 0.4rem;
    background: var(--wk-surface);
}

/* The content column. Replaces the old 960px .wk-main cap: the shell owns the
   page frame now, and 1180px is the hub grid's natural width. */
.wk-content {
    flex: 1;
    width: 100%;
    max-width: 1180px;
    margin: 0 auto;
    padding: 2rem;
}

/* THE STANDING PROJECT-WIDE NOTICE (AutoUnavailableBanner.razor). FULL-BLEED, like the topbar
   above it and unlike .wk-content below it: this is chrome, not page content, and a warning
   indented to the content measure reads as something the page said rather than as a condition of
   the project. It takes the existing --wk-warning-tint wash and the palette's own ink instead of
   introducing a colour pair of its own - the tint already forks per palette, so the band stays
   legible on both without a new token whose contrast nothing recomputes. The bottom hairline is
   the same one the topbar carries, so the two bands stack as one chrome edge. */
.wk-global-banner {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    justify-content: center;
    padding: 0.625rem 1rem;
    background: var(--wk-warning-tint);
    color: var(--wk-text);
    border-bottom: 1px solid var(--wk-border);
    font-size: 0.8125rem;
}

/* ===== Auth shell (AuthLayout.razor) =====
   The auth pages get NO sidebar and NO topbar: a centered column under the
   wordmark, with the hairline as the only ornament. The hairline is the third and
   last sanctioned gradient surface in this stylesheet. */
.wk-auth {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 3rem 1rem;
}

.wk-auth-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.wk-auth-brand .wk-brand { font-size: 1.25rem; }

.wk-auth-hairline {
    width: 3rem;
    height: 2px;
    border-radius: 2px;
    background: var(--wk-brand-gradient);
}

.wk-auth-main { width: 100%; max-width: 26rem; }

.wk-auth-main a {
    color: var(--wk-purple-hover);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
}

.wk-auth-main a:hover { color: var(--wk-text-strong); }

/* The shared .wk-field rhythm pairs a 10-14rem label column with its control, which is
   what turns a settings page into a scannable panel. The auth card is 26rem wide, so that
   same column starved the control: a 224px label next to a 126px input, with the visitor
   typing into the narrower of the two. Auth fields stack instead - scoped here so the
   settings rhythm keeps the layout it was designed and reviewed for. */
.wk-auth-main .wk-field {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: 0.35rem;
}

.wk-auth-main a:focus-visible {
    outline: 2px solid var(--wk-purple-hover);
    outline-offset: 2px;
}

.wk-card {
    background: var(--wk-surface);
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius);
    padding: 1.5rem;
    margin-bottom: 1rem;
}

/* The roster's trash: a CSS-only <details> disclosure carrying the card look (same pattern as
   the nav switcher), so peeking at trashed sheets costs no circuit round-trip. The native
   disclosure triangle stays - it IS the affordance. */
.wk-trash > summary {
    cursor: pointer;
    font-weight: 600;
}

.wk-trash > summary + * { margin-top: 1rem; }

/* Form rhythm. Wide viewports pair a compact label with its control, turning the
   project-settings wall of fields into a scannable settings panel. Small screens
   keep the original stacked layout. */
.wk-field {
    display: grid;
    grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr);
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* A COLUMN OF LABELLED FIELDS INSIDE A CARD reads at the same measure as a settings category
   page. .wk-content is 1180px, which is right for an eight-column table and far too wide for a
   column of labelled fields - a 1100px input next to a 10rem label is a line the eye cannot
   track back from. The expression is deliberately IDENTICAL to .wk-category-page's, and a unit
   test asserts both, so the panel cannot drift into two different "narrow" widths.

   THERE IS NO margin: 0 auto HERE, AND THAT ABSENCE IS THE FIX. The predecessor of this rule
   (.wk-page-narrow) carried one, and every one of its call sites was a CARD - so a form card
   centred itself inside the 1180px column while the table card above it started at the column's
   edge, and the overview read as a zig-zag with no common left edge anywhere down the page. The
   measure belongs to the FIELDS, not to the surface holding them: the card is a sibling of the
   table cards and must share their edges, and the card's own padding is what insets these
   fields. Capping a card is capping the wrong box.

   IT IS DECLARED AFTER .wk-field ON PURPOSE. Two guards in this suite locate a rule by scanning
   for its selector text, and '.wk-field' is a PREFIX of this one - declared above, this rule
   would answer for '.wk-field' and a correct stylesheet would fail a correct test. Every other
   .wk-field-* rule in this file sits after the base rule for the same reason. */
.wk-field-column {
    width: min(100%, 54rem);
}

.wk-field span {
    display: block;
    margin: 0;
    color: var(--wk-muted);
    font-size: 0.85rem;
    font-weight: 500;
}

/* The field fill is var(--wk-bg), NOT var(--wk-surface), and that is the value
   this rule shipped with. Retokenising the palette is not a licence to restyle a
   component: on Dark, --wk-bg is the page colour that makes an input read as a
   well INSIDE a --wk-surface card, and swapping it for the surface would flatten
   every form against its card - an approved appearance changed by a refactor
   nobody reviewed for it. A unit test pins the declaration. */
.wk-input {
    width: 100%;
    min-height: 2.25rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--wk-radius);
    border: 1px solid var(--wk-border);
    background: var(--wk-bg);
    color: var(--wk-text);
    font: inherit;
    transition: border-color 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
}

.wk-input:hover:not(:disabled) { border-color: var(--wk-muted); }

.wk-input:focus-visible {
    border-color: var(--wk-purple);
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.25);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--wk-purple) 25%, transparent);
    outline: none;
}

/* ===== THE INVALID STATE OF A CONTROL =====
   Until this rule existed the panel had exactly ONE error class - .wk-error, which only
   recolours a paragraph - so a page that knew WHICH field was wrong had no way to say so
   and the reader had to re-read the whole form to find it.

   .wk-input and .wk-select share ONE rule rather than two copies: the select is styled by
   the page batches, and a second rule is how one control type ends up with an invalid state
   the other quietly lacks.

   THE COLOUR IS NEVER THE ONLY CARRIER. A border alone says nothing to a reader who cannot
   separate these hues, so the state always travels with the .wk-field-error SENTENCE below -
   the shared field-error component renders it with role="alert", and the border is the
   pointer that tells that sentence which control it is about. */
.wk-input.is-invalid,
.wk-select.is-invalid {
    border-color: var(--wk-danger);
}

/* AND THE MARK SURVIVES BEING FOCUSED, which is the whole point: .wk-input:focus-visible
   above repaints the border indigo, so without this higher-specificity rule the only marker
   vanishes at the exact moment the reader clicks in to fix the field. The halo turns red too,
   from the token that mirrors --wk-focus-ring on the danger hue. */
.wk-input.is-invalid:focus-visible,
.wk-select.is-invalid:focus-visible {
    border-color: var(--wk-danger);
    box-shadow: 0 0 0 3px var(--wk-danger-focus-ring);
    outline: none;
}

/* ===== NOTES THAT BELONG TO A FIELD =====
   Both render INSIDE the .wk-field, under the control, so they land in the grid's CONTROL
   column and read as being about that input - the same anatomy .wk-field-note already uses
   for help text that follows a whole row.

   display: block is load bearing on both: these are <span> elements (the error one must be a
   span so role="alert" sits on an inline element the control's label can own), and an inline
   note would sit on the label's baseline instead of under the box it describes.

   THE ERROR USES --wk-danger-text, NOT --wk-danger: this is small text, and the status hue
   measures 3.76:1 on a white card - a warning the reader it is aimed at cannot read. */
.wk-field-error {
    display: block;
    margin-top: 0.3rem;
    color: var(--wk-danger-text);
    font-size: 0.8rem;
}

.wk-field-hint {
    display: block;
    margin-top: 0.3rem;
    color: var(--wk-muted);
    font-size: 0.8rem;
}

.wk-button {
    min-height: 2.25rem;
    padding: 0.5rem 1.25rem;
    border: 1px solid transparent;
    border-radius: var(--wk-radius);
    background: var(--wk-button-primary);
    color: #fff;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    transition: background-color 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
}

.wk-button:hover:not(:disabled) { background: var(--wk-button-primary-hover); }

.wk-button:focus-visible {
    box-shadow: 0 0 0 3px rgba(84, 87, 232, 0.35);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--wk-button-primary) 35%, transparent);
    outline: none;
}

.wk-button-danger { background: var(--wk-button-danger); }
.wk-button-danger:hover:not(:disabled) { background: var(--wk-button-danger-hover); }

/* ===== THE LIGHT DESTRUCTIVE VARIANT, FOR A DELETE INSIDE A TABLE ROW =====
   A row's Remove is not the page's CTA. Painting one solid red per row turns a list of
   eight connections into a wall of alarm, and the eye loses the one action that IS the
   page's purpose. So this variant is transparent at rest, carries its intent in the ink
   and the border, and commits to the SOLID destructive fill on hover - the same fill whose
   white label is already contrast-pinned, which is why hover also switches the ink.

   .wk-button-danger IS DELIBERATELY NOT CHANGED: a form's destructive CTA (delete this
   project, revoke this key) stays solid. This is a second variant for a different weight
   of the same intent, not a de-emphasis of the first.

   The label is small destructive TEXT on a card, so it takes --wk-danger-text rather than
   the status hue - the same reason .wk-field-error does. */
.wk-button-danger-ghost {
    background: transparent;
    border-color: var(--wk-danger-text);
    color: var(--wk-danger-text);
    font-weight: 500;
}

.wk-button-danger-ghost:hover:not(:disabled) {
    background: var(--wk-button-danger);
    border-color: var(--wk-button-danger);
    color: #fff;
}

/* The keyboard reader gets the DESTRUCTIVE halo, not the primary one inherited from
   .wk-button:focus-visible: a red-bordered control ringed in indigo reads as a focus
   state borrowed from another button. */
.wk-button-danger-ghost:focus-visible {
    box-shadow: 0 0 0 3px var(--wk-danger-focus-ring);
    outline: none;
}

.wk-input:disabled,
.wk-button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.wk-field:has(:disabled) span { opacity: 0.6; }

input[type="checkbox"] {
    width: 1rem;
    height: 1rem;
    margin: 0;
    accent-color: var(--wk-purple);
    cursor: pointer;
}

input[type="checkbox"]:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.wk-error { color: var(--wk-danger); margin: 0.5rem 0; }
.wk-success { color: var(--wk-success); margin: 0.5rem 0; }
.wk-muted { color: var(--wk-muted); }

.wk-table { width: 100%; border-collapse: collapse; }
.wk-table th, .wk-table td { text-align: left; padding: 0.5rem; border-bottom: 1px solid var(--wk-border); }

/* ----------------------------------------------------------------------------
   HEADLINE KPIs AND CHARTS (ProjectUsage)

   THE KPI ROW IS auto-fit, NOT A FIXED FOUR-COLUMN GRID: the cards must reflow
   on a narrow viewport without a breakpoint of their own, and a fixed grid would
   either overflow the card or need a media query duplicating what auto-fit does
   for free. The minimum is wide enough for a grouped seven-digit token count,
   which is the longest value any of these four cards can hold.

   NO THEMED SELECTOR HERE, by the rule in this file's header: every value is a
   token, so both palettes are served by this one block. The chart SERIES colours
   cannot be - ApexCharts paints a canvas from JSON and cannot read a CSS custom
   property - so those two hexes live in ProjectUsage.razor, which says so.
   ---------------------------------------------------------------------------- */
.wk-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.wk-kpi {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.875rem 1rem;
    background: var(--wk-surface-muted);
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius-sm);
}

.wk-kpi-label {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--wk-muted);
}

/* TABULAR FIGURES so the four values line up on their digits and do not jitter
   when a reload changes a count's width. The mono stack is the one the tokens
   already publish, so the numbers read like the rest of the product's data. */
.wk-kpi-value {
    font-family: var(--wk-font-mono);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--wk-text-strong);
    font-variant-numeric: tabular-nums;
}

.wk-kpi-foot {
    font-size: 0.75rem;
    color: var(--wk-muted);
}

/* The chart is a canvas the JS module sizes itself; this wrapper only reserves
   the horizontal room and the gap to the table underneath it. min-width: 0 is
   load-bearing inside the card's layout - without it a wide chart refuses to
   shrink and pushes the card past the content column. */
.wk-chart {
    min-width: 0;
    margin-bottom: 1.25rem;
}

/* ===== THE SCROLL CONTAINER WIDE TABLES NEED =====
   The shell's grid comment has promised this since it was written - "minmax(0, 1fr) lets
   wide tables scroll INSIDE the column instead of pushing the sidebar off the page" - but
   the element that does the scrolling never existed, so the promise was only half kept:
   the column refuses to grow, and the table inside it compresses instead. The connections
   table carries eight columns and is the one that shows it on a narrow window.

   BOTH HALVES ARE LOAD BEARING. overflow-x creates the scroll area; the min-width on the
   table inside is what gives it something to scroll - a width:100% table with no floor
   simply squeezes its columns to illegibility and never overflows at all. */
.wk-table-scroll {
    width: 100%;
    overflow-x: auto;
}

.wk-table-scroll > .wk-table {
    min-width: 44rem;
}

/* ===== COLUMN WIDTH CONTRACT =====
   .wk-table is only width:100% with no column sizing, so the browser hands every column an
   equal share and an actions cell holding one button claims as much room as a project name.
   These two utilities go on the th AND the td of a column (the th alone does not survive a
   wider cell below it).

   width:1% is the standard shrink-to-content idiom for a collapsed column: a percentage
   below the content's intrinsic width makes the table layout give the column exactly what
   it needs and no more. nowrap is the other half - without it the cell "shrinks" by
   wrapping its own buttons onto two lines. */
.wk-col-actions {
    width: 1%;
    white-space: nowrap;
}

/* Metadata that is known-short and fixed in shape - a role, a date, a joined-at stamp. A
   fixed slot rather than a shrink, so these columns line up across rows and across tables
   instead of each one sizing itself to its longest value. */
.wk-col-meta {
    width: 9rem;
}

/* ============================================================================
   PAGE CONTENT (Dashboard / ProjectDetail / ProjectUsage)
   The shell owns the frame (.wk-content); pages compose a head, cards and
   tables out of the same tokens. No rule here consumes the brand gradient -
   its three sanctioned surfaces are all in the shell, and a test counts them.
   ============================================================================ */

/* Page head: the mono eyebrow over the h1, the kodzonia.com signature the
   sidebar's group labels already carry (.wk-navgroup-label is the same anatomy). */
.wk-page-head { margin-bottom: 1.5rem; }

.wk-page-head h1 { font-size: 1.6rem; letter-spacing: -0.01em; }

.wk-page-head p { max-width: 38rem; margin: 0.25rem 0 0; }

.wk-eyebrow {
    display: block;
    margin-bottom: 0.5rem;
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--wk-muted);
}

/* An eyebrow sits directly above its card's heading, so the heading's section
   separator margin must not push the pair apart. */
.wk-card > .wk-eyebrow + h2 { margin-top: 0; }

/* Fragment destinations (the sidebar's Members / AI Connections items) must not
   land scrolled under the sticky topbar. */
.wk-card[id] { scroll-margin-top: 4.5rem; }

/* Table headers read as mono micro-labels; the data rows keep body type. */
.wk-table th {
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--wk-muted);
}

/* ============================================================================
   SETTINGS HUB AND CATEGORY PAGES
   The hub is static SSR; category forms and the save bar are interactive islands.
   All colour comes from the shared tokens, so these rules are identical in both themes.
   ============================================================================ */
.wk-settings-hub { width: 100%; }

.wk-hub-meta {
    min-height: 1.25rem;
    margin: -0.75rem 0 1rem;
    color: var(--wk-muted);
    font-family: var(--wk-font-mono);
    font-size: 0.72rem;
}

.wk-hub-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
}

.wk-hub-card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 13rem;
    padding: 1.25rem;
    overflow: hidden;
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius-lg);
    background: var(--wk-surface);
    color: var(--wk-text);
    text-decoration: none;
    transition: border-color 120ms ease, background-color 120ms ease, transform 120ms ease;
}

.wk-hub-card::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: transparent;
}

.wk-hub-card.is-non-default::before {
    background: linear-gradient(180deg, var(--wk-purple), var(--wk-accent));
}

.wk-hub-card:hover {
    border-color: var(--wk-muted);
    background: var(--wk-surface-raised);
    transform: translateY(-1px);
}

.wk-hub-card:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: 2px;
}

.wk-hub-card-eyebrow {
    margin-bottom: 0.75rem;
    color: var(--wk-muted);
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.18em;
}

.wk-hub-card-title {
    margin-bottom: 0.4rem;
    color: var(--wk-text-strong);
    font-size: 1.05rem;
}

.wk-hub-card-description {
    color: var(--wk-muted);
    font-size: 0.85rem;
    line-height: 1.45;
}

.wk-hub-card-status {
    margin-top: auto;
    padding-top: 1.25rem;
    color: var(--wk-text);
    font-family: var(--wk-font-mono);
    font-size: 0.75rem;
    overflow-wrap: anywhere;
}

.wk-hub-card-meta {
    margin-top: 0.45rem;
    color: var(--wk-muted);
    font-size: 0.72rem;
}

/* The hub's sub-page links: pages that live UNDER a category route and own no configuration
   field, so they carry no status line and no non-default border. Deliberately NOT styled as
   hub cards - a card promises a category, and reading one of these as a 13th category is the
   confusion this row avoids. */
.wk-hub-subpages {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1rem;
}

.wk-hub-subpage {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.75rem 1rem;
    border: 1px dashed var(--wk-border);
    border-radius: var(--wk-radius);
    background: var(--wk-surface);
    color: var(--wk-text);
    font-size: 0.85rem;
    text-decoration: none;
    transition: border-color 120ms ease, background-color 120ms ease;
}

.wk-hub-subpage:hover {
    border-color: var(--wk-muted);
    background: var(--wk-surface-raised);
}

.wk-hub-subpage:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: 2px;
}

.wk-category-page {
    width: min(100%, 54rem);
    margin: 0 auto;
    padding-bottom: 7rem;
}

.wk-back-link {
    display: inline-block;
    margin-bottom: 1.25rem;
    color: var(--wk-muted);
    font-size: 0.85rem;
    text-decoration: none;
}

.wk-back-link:hover { color: var(--wk-text); }

.wk-back-link:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: 3px;
}

.wk-drill-head { margin-bottom: 1.5rem; }
.wk-drill-head p { max-width: 40rem; margin: 0.25rem 0 0; }
.wk-category-fields { margin-bottom: 1.25rem; }
.wk-category-fields .wk-field:last-child { margin-bottom: 0; }

/* A note that belongs to the field above it, not to the page: it sits in the CONTROL
   column of the .wk-field grid so it lines up under the input it explains rather than
   under the label, which would read as a new section. */
.wk-field-note {
    margin: -0.5rem 0 1rem;
    font-size: 0.85rem;
}

/* Pill multi-select (the subagent model allowlist). The checkbox stays in the DOM and
   keeps its native semantics - it is the accessible control and the manager guard is on
   it - while the label around it carries the visible state. Hiding the box with
   display:none would take it out of the accessibility tree along with its own disabled
   state, so it is only visually collapsed. */
.wk-pill-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.wk-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--wk-border);
    border-radius: 999px;
    background: var(--wk-bg);
    color: var(--wk-muted);
    cursor: pointer;
    font-size: 0.85rem;
    transition: border-color 120ms ease, background-color 120ms ease, color 120ms ease;
}

.wk-pill:hover:not(:has(:disabled)) { border-color: var(--wk-muted); }

.wk-pill.is-on {
    border-color: var(--wk-purple);
    background: var(--wk-surface-raised);
    color: var(--wk-text);
}

.wk-pill:has(:focus-visible) {
    outline: 2px solid var(--wk-purple);
    outline-offset: 2px;
}

.wk-pill:has(:disabled) { opacity: 0.6; cursor: not-allowed; }

/* A pill whose model no enabled, verified connection of the project serves right now. The pill
   stays tickable - the allowlist is POLICY, not reachability, and a connection may come back -
   but it is dimmed and dashed so a manager reads why an Auto dispatch would skip it, and why the
   page warns when every allowed model looks like this. The title attribute carries the sentence. */
.wk-pill.is-unreachable {
    border-style: dashed;
    opacity: 0.55;
}

.wk-inline-actions,
.wk-save-bar-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.wk-button-secondary {
    border-color: var(--wk-border);
    background: var(--wk-surface-muted);
    color: var(--wk-text);
}

.wk-button-secondary:hover:not(:disabled) { background: var(--wk-surface-raised); }

.wk-save-bar {
    position: sticky;
    z-index: 10;
    bottom: 1rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: 1rem;
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius-lg);
    background: var(--wk-surface-raised);
}

.wk-save-bar.is-dirty { border-color: var(--wk-purple); }

/* THE BAR AFTER A SUCCESSFUL SAVE. It holds one sentence and the last-changed stamp and NO
   buttons, so the dirty bar's space-between (which pushes a diff and an action group apart)
   would strand the confirmation at the far left of a 1180px column, nowhere near where the
   Save had been. flex-start keeps it under the reader's eye, and the success border is what
   distinguishes it at a glance from the purple "you have unsaved changes" state it replaced. */
.wk-save-bar.is-saved {
    justify-content: flex-start;
    border-color: var(--wk-success);
    background: var(--wk-success-tint);
}

.wk-save-bar.is-saved .wk-success { margin: 0; }

.wk-save-bar.is-read-only { align-items: flex-start; flex-direction: column; }
.wk-save-bar-notice { margin: 0; color: var(--wk-muted); }
.wk-save-bar-copy { min-width: 0; }

.wk-save-bar-diff {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    margin-top: 0.35rem;
    color: var(--wk-muted);
    font-family: var(--wk-font-mono);
    font-size: 0.72rem;
    overflow-wrap: anywhere;
}

.wk-save-bar-diff b { color: var(--wk-text); font-weight: 600; }

.wk-save-bar-meta {
    display: block;
    margin-top: 0.4rem;
    color: var(--wk-muted);
    font-size: 0.72rem;
}

/* ===== Theme segmented control =====
   Two real submit buttons sitting in one rounded track, so the pair reads as a
   single two-state control rather than as two unrelated links. The form lives in
   the 240px sidebar footer, so it WRAPS AT EVERY WIDTH, not just on phones: the
   segmented control and the status line each take their own row when they must. */
.wk-nav-theme {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    min-width: 0;
    max-width: 100%;
    margin-bottom: 0.25rem;
}

.wk-theme-segment {
    display: inline-flex;
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius);
    background: var(--wk-surface-muted);
    overflow: hidden;
}

.wk-theme-control {
    background: none;
    border: 1px solid transparent;
    color: var(--wk-muted);
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 500;
    padding: 0.2rem 0.7rem;
    transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
}

.wk-theme-control:hover:not(:disabled) { color: var(--wk-text); }

/* SELECTION IS NOT CARRIED BY COLOUR ALONE. The active segment changes WEIGHT,
   gains a BORDER and grows an INSET MARKER along its lower edge, so the state
   survives a reader who cannot separate these two hues, a monochrome display and
   a forced-colours mode that discards the background entirely. */
.wk-theme-control[aria-pressed="true"] {
    background: var(--wk-surface);
    color: var(--wk-text-strong);
    font-weight: 700;
    border-color: var(--wk-purple);
    box-shadow: inset 0 -2px 0 0 var(--wk-purple);
}

.wk-theme-control:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: -2px;
}

/* The script disables BOTH controls while the write is in flight; that pause has
   to be visible, or a reader keeps clicking a control that is ignoring them. */
.wk-theme-control:disabled {
    opacity: 0.5;
    cursor: progress;
}

/* THE STATUS LINE BREAKS AT EVERY WIDTH NOW. The failure message ("Theme could
   not be saved. The previous theme was restored.") measures ~387px on one
   unbreakable line - wider than the sidebar it lives in, let alone a phone - so
   the base rule lets it wrap on its own row under the segmented control. NOT
   clipped, ellipsed or hidden: this span is the live region, and it is the only
   report a reader gets of a write that failed. */
.wk-nav-theme-status {
    flex-basis: 100%;
    min-width: 0;
    color: var(--wk-muted);
    font-size: 0.8rem;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* ===== Sidebar collapse, CSS-only =====
   Under 1024px the sidebar leaves the grid and becomes a drawer: fixed, off-canvas
   by default, slid in by the checkbox the topbar's Menu label toggles. The grid
   collapses to a single column so the content claims the full width. */
@media (max-width: 1024px) {
    .wk-shell { grid-template-columns: minmax(0, 1fr); }

    .wk-hub-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .wk-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 30;
        width: 240px;
        height: auto;
        transform: translateX(-100%);
    }

    .wk-nav-toggle:checked ~ .wk-sidebar { transform: translateX(0); }

    .wk-nav-toggle-button { display: inline-flex; }

    .wk-nav-toggle:focus-visible ~ .wk-column .wk-nav-toggle-button {
        outline: 2px solid var(--wk-purple);
        outline-offset: 2px;
    }

    .wk-topbar-inner { padding: 0 1rem; }

    .wk-search-pill { min-width: 0; }
}

/* Keep controls readable and usable on compact screens. */
@media (max-width: 640px) {
    .wk-content { padding: 1.25rem 1rem; }

    .wk-card { padding: 1rem; }

    .wk-field {
        display: block;
        margin-bottom: 1rem;
    }

    .wk-field span { margin-bottom: 0.35rem; }

    .wk-hub-grid { grid-template-columns: minmax(0, 1fr); }

    .wk-category-page { padding-bottom: 0; }

    .wk-save-bar {
        position: static;
        flex-direction: column;
        align-items: stretch;
        margin-right: -1rem;
        margin-left: -1rem;
        width: calc(100% + 2rem);
        border-right: 0;
        border-left: 0;
        border-radius: 0;
    }

    .wk-save-bar-actions { width: 100%; }
    .wk-save-bar-actions .wk-button { flex: 1; }

    /* THE OPEN DRAWER NEVER EXCEEDS THE VIEWPORT. 240px fits a 360px phone, but
       a narrower one (or a larger minimum font) is capped at 85vw so the reader
       can always tap the content beside the drawer to get back. The footer form
       inside keeps the containment the base rule already gives it - these are
       the same overflow guarantees the old stacked-header fix pinned, re-homed
       to the drawer. */
    .wk-sidebar { width: min(240px, 85vw); }

    .wk-nav-theme {
        min-width: 0;
        max-width: 100%;
    }

    .wk-nav-theme-status {
        flex-basis: 100%;
        min-width: 0;
        white-space: normal;
        overflow-wrap: anywhere;
    }
}

/* ============================================================================
   GOVERNANCE ALERTS AND VISUAL HIERARCHY
   The governance page is the one surface that must answer "is anything wrong"
   BEFORE it answers "what are the numbers". Everything in this section exists
   to give a fired alert more visual weight than any KPI, and to let the detail
   tables step back until a reader asks for them.

   THE SEVERITY HUES ARE TOKENS, NOT LITERALS. --wk-danger and --wk-success were
   already published in both palettes; --wk-warning and --wk-info were NOT - only
   their -tint washes existed, because nothing had ever painted those two hues as
   a foreground. They are defined with the rest of the palette above rather than
   here, so the themed selector still appears exactly once in this file.
   ---------------------------------------------------------------------------- */

/* SEVERITY MODIFIERS. Additive decorations, never layout: each one only paints
   a left edge and a wash. The governance page's alert band maps each alert's
   severity to one of these classes on its .wk-alert-item rows; the CSS itself
   stays ignorant of what severity means. */
.wk-severity-critical {
    border-left: 4px solid var(--wk-danger);
    background: var(--wk-danger-tint);
}

.wk-severity-warning {
    border-left: 4px solid var(--wk-warning);
    background: var(--wk-warning-tint);
}

.wk-severity-info {
    border-left: 4px solid var(--wk-info);
    background: var(--wk-info-tint);
}

/* THE ALERT BAND IS THE PAGE'S HEADLINE, not a card among cards. It sits above
   everything, spans the full content column, and carries the heaviest border of
   any element on the page - the one piece of deliberate visual shouting in a
   panel that is otherwise uniformly quiet. When it renders empty it renders
   NOTHING: an empty band reading "no alerts" would train the reader to skip the
   region that matters most. */
.wk-alert-band {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.wk-alert-item {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--wk-border);
    border-radius: var(--wk-radius-sm);
}

/* The title carries the finding, the detail carries the evidence. The title is
   strong enough to scan a stack of alerts by; the detail stays at footnote
   weight so a reader can choose to read only the titles. */
.wk-alert-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--wk-text-strong);
}

.wk-alert-detail {
    font-size: 0.8125rem;
    color: var(--wk-muted);
}

/* THEMATIC CARD GROUPING. The panel's cards were a flat vertical stack, which
   makes eight equally-weighted cards read as eight unrelated facts. Pairing
   them two-up from the tablet breakpoint groups the ones that answer the same
   question (allocation, health, capacity, risk) into one visual unit, and the
   single column below that keeps them readable on a phone. The cards keep their
   own .wk-card styling - this container only decides how they sit together. */
.wk-card-group {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    margin-bottom: 1.25rem;
}

@media (min-width: 48rem) {
    .wk-card-group {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* An odd card at the end of a pair spans both columns rather than leaving a
       hole: a half-width card with nothing beside it reads as a missing card. */
    .wk-card-group > .wk-card:last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }
}

/* PROGRESSIVE DISCLOSURE FOR DETAIL TABLES. Every chart on the page had a full
   table repeating the same rows underneath it, which doubled the page's length
   and buried the next chart. Native <details> collapses them with no JS and no
   component: the chart stays the answer, the table becomes the receipt a reader
   can ask for. */
.wk-detail-toggle {
    margin-bottom: 1.25rem;
    border-top: 1px solid var(--wk-border);
    padding-top: 0.5rem;
}

.wk-detail-toggle > summary {
    cursor: pointer;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--wk-muted);
    padding: 0.25rem 0;
    list-style: revert;
}

.wk-detail-toggle > summary:hover {
    color: var(--wk-text-strong);
}

/* The summary keeps a visible focus ring: it is an interactive control, and the
   muted colour that makes it recede visually also makes it easy to lose when
   tabbing. */
.wk-detail-toggle > summary:focus-visible {
    outline: 2px solid var(--wk-purple);
    outline-offset: 2px;
    border-radius: var(--wk-radius-sm);
}

.wk-detail-toggle[open] > summary {
    margin-bottom: 0.5rem;
    color: var(--wk-text-strong);
}

/* ----------------------------------------------------------------------------
   CONNECTION USAGE BARS (ProjectDetail, AI Connections)

   One strip under each connection row: a bar per PROVIDER-NATIVE window, never
   normalized across providers. The strip row belongs visually to the connection
   above it, so that row drops its divider and the strip carries the divider
   instead. The bar tints by severity (ok / warn / crit at 60 and 85 percent),
   but the percentage is always printed beside it, so colour is never the only
   carrier of the number. The skeleton shimmer is motion, and the global
   reduced-motion query below removes it.
   ---------------------------------------------------------------------------- */
.wk-table tr:has(+ .wk-usage-row) > td { border-bottom: none; }

.wk-usage-row td {
    border-top: none;
    padding: 0 0.5rem 0.6rem;
}

.wk-usage-strip {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.wk-usage-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.wk-usage-meters {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.5rem 1rem;
}

.wk-usage-meter {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.wk-usage-meter-label {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--wk-muted);
}

.wk-usage-meter-label > span:first-child {
    min-width: 0;
    overflow-wrap: anywhere;
}

.wk-usage-meter-pct {
    font-family: var(--wk-font-mono);
    color: var(--wk-text);
    white-space: nowrap;
}

.wk-usage-bar {
    height: 6px;
    background: var(--wk-surface-muted);
    border-radius: var(--wk-radius-sm);
    overflow: hidden;
}

.wk-usage-bar > i {
    display: block;
    height: 100%;
    border-radius: var(--wk-radius-sm);
    background: var(--wk-success);
}

.wk-usage-bar.wk-usage-ok > i { background: var(--wk-success); }
.wk-usage-bar.wk-usage-warn > i { background: var(--wk-warning); }
.wk-usage-bar.wk-usage-crit > i { background: var(--wk-danger); }

.wk-usage-plan,
.wk-usage-limit {
    display: inline-block;
    padding: 0.05rem 0.5rem;
    border-radius: var(--wk-radius-sm);
    font-family: var(--wk-font-mono);
    font-size: 0.65rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.wk-usage-plan {
    background: var(--wk-surface-muted);
    border: 1px solid var(--wk-border);
    color: var(--wk-text);
}

.wk-usage-limit {
    background: var(--wk-danger-tint);
    color: var(--wk-danger-text);
}

.wk-usage-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.75rem;
}

.wk-usage-updated,
.wk-usage-none {
    font-size: 0.75rem;
    color: var(--wk-muted);
}

.wk-usage-skeleton {
    height: 1.4rem;
    border-radius: var(--wk-radius-sm);
    background: linear-gradient(
        90deg,
        var(--wk-surface-muted) 0%,
        var(--wk-border) 50%,
        var(--wk-surface-muted) 100%);
    background-size: 200% 100%;
    animation: wk-usage-shimmer 1.4s ease-in-out infinite;
}

@keyframes wk-usage-shimmer {
    from { background-position: 100% 0; }
    to { background-position: -100% 0; }
}

/* A reader who asks for less motion gets NO motion from this panel. The theme
   swap repaints the whole page at once, and the sidebar drawer slides - both
   exactly the kind of large-area change that provokes discomfort, so the
   transitions are removed rather than shortened. The wildcard covers every
   component, including the drawer's transform and the switcher's chevron. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition: none !important;
        animation: none !important;
        scroll-behavior: auto !important;
    }
}
