/* =========================================================================
   Wrangler Admin UI Kit — self-contained production stylesheet.
   OKLCH dark theme, hue ~320 (hot-pink "girly"). No Tailwind, no DaisyUI.

   wr-* is the CANONICAL vocabulary (ruling 2026-08-04). Layers:

     1. Tokens + base reset             (from handoff/assets/kit.css)
     2. wr-* handoff recipes            (from handoff/assets/kit.css, verbatim)
     3. wr-* extension recipes          (NEW — canonical recipes for every
        component the admin templates need that the handoff kit lacks, coined
        in the kit's own token/recipe style: wr-alert, wr-drawer, wr-modal,
        wr-menu, wr-collapse, wr-toggle, wr-loading, … see class-mapping.md)
     4. Utility layer                   (permanent — single-purpose layout/
        typography/color utilities actually used by the templates; unprefixed
        by design, see the banner)
     5. Permanent unprefixed classes    (cow herd, htmx indicator, scrollbar —
        named by external contracts: cows.js and the HTMX runtime)

   The TRANSITIONAL SHIM (legacy DaisyUI/admin.css selectors aliased onto the
   wr-* vocabulary so unported pages could render mid-branch) was deleted
   wholesale in Task 3 (issue #383) once every template was ported to wr-*
   per class-mapping.md. Every template now speaks wr-* + the permanent
   unprefixed utility/external-contract layers only — enforced repo-wide by
   tests/unit/test_kit_css_migration.py::TestCanonicalVocabularyRepoWide.

   Anything added here follows the handoff kit's law: OKLCH hue 320 tokens,
   flat recipes, CSS-only interaction (no JS).
   ========================================================================= */

/* =========================================================================
   1. TOKENS + BASE RESET  (from handoff/assets/kit.css)
   ========================================================================= */

:root, [data-theme="wrangler"] {
  color-scheme: dark;
  --b100: oklch(20% 0.02 320);
  --b200: oklch(16% 0.02 320);
  --b300: oklch(13% 0.03 320);
  --bc:   oklch(90% 0.02 330);
  --primary: oklch(65% 0.28 340);
  --secondary: oklch(55% 0.2 300);
  --accent: oklch(75% 0.15 350);
  --success: oklch(70% 0.2 160);
  --warning: oklch(75% 0.15 80);
  --error:   oklch(60% 0.25 20);
  --info:    oklch(65% 0.15 250);
  --grad-title: linear-gradient(135deg, oklch(65% 0.28 340), oklch(75% 0.15 350), oklch(55% 0.2 300));
  --grad-btn:   linear-gradient(135deg, oklch(65% 0.28 340), oklch(55% 0.22 310));
  --grad-btn-h: linear-gradient(135deg, oklch(70% 0.28 340), oklch(60% 0.22 310));
  --grad-main:  linear-gradient(160deg, oklch(18% 0.015 320), oklch(20% 0.02 330), oklch(17% 0.025 310));
  --grad-side:  linear-gradient(180deg, oklch(18% 0.03 320), oklch(14% 0.04 310));
}

* { box-sizing: border-box; }
html, body, #root { margin: 0; padding: 0; height: 100%; }
body {
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  color: var(--bc);
  background: var(--grad-main);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }

/* Keyboard-only bypass for the persistent sidebar.  It stays outside the
   viewport until focused, then appears above every shell layer (#508). */
.wr-skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 1000;
  padding: 8px 12px; border: 2px solid var(--primary); border-radius: 8px;
  background: var(--b100); color: var(--bc); font-weight: 700;
  transform: translateY(calc(-100% - 16px));
  transition: transform .15s ease;
}
.wr-skip-link:focus,
.wr-skip-link:focus-visible { transform: translateY(0); }

.wr-title-gradient {
  background: var(--grad-title);
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}

/* =========================================================================
   2. wr-* HANDOFF RECIPES  (from handoff/assets/kit.css, verbatim)
   ========================================================================= */

/* ---------- Shell layout ------------------------------------------------ */
.wr-shell { display: grid; grid-template-columns: 256px 1fr; min-height: 100vh; }
.wr-main { display: flex; flex-direction: column; min-width: 0; padding: 20px 28px 40px; }
.wr-page { display: flex; flex-direction: column; gap: 18px; }

@keyframes fade-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.wr-page-enter { animation: fade-up .3s ease-out; }

/* ---------- Sidebar ----------------------------------------------------- */
.wr-sidebar {
  background: var(--grad-side);
  border-right: 1px solid oklch(65% 0.28 340 / 0.1);
  padding: 20px 16px;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
}
.wr-sidebar-logo {
  display: flex; align-items: center; gap: 10px; padding-bottom: 14px; margin-bottom: 12px;
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, transparent, oklch(65% 0.28 340 / 0.3), transparent) 1;
}
.wr-nav-hearts { list-style: none; padding: 0 0 0 16px; margin: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.wr-nav-hearts li::marker { content: "\2665  "; color: var(--primary); }
.wr-nav-hearts a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 8px;
  color: oklch(90% 0.02 330 / 0.75); font-size: 13px; font-weight: 500;
  transition: all .2s ease;
}
.wr-nav-hearts a:hover { background: oklch(65% 0.28 340 / 0.08); box-shadow: 0 0 15px oklch(65% 0.28 340 / 0.1); color: var(--bc); }
.wr-nav-hearts a.wr-sidebar-active {
  background: oklch(65% 0.28 340 / 0.15);
  border-right: 2px solid var(--primary);
  color: var(--primary);
  box-shadow: inset 0 0 20px oklch(65% 0.28 340 / 0.08), 0 0 15px oklch(65% 0.28 340 / 0.1);
  font-weight: 600;
}
.wr-sidebar-user {
  display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 10px;
  background: oklch(65% 0.28 340 / 0.06); margin-top: 12px;
  border: 1px solid oklch(65% 0.28 340 / 0.12);
}
.wr-sidebar-footer { font-size: 10px; letter-spacing: 0.05em; color: oklch(65% 0.28 340 / 0.35); text-align: center; margin-top: 10px; }

/* ---------- Top bar ---------------------------------------------------- */
.wr-topbar { display: flex; align-items: center; gap: 14px; padding: 4px 0 18px; }
.wr-search {
  display: flex; align-items: center; gap: 8px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.15);
  border-radius: 8px; padding: 6px 10px; width: 280px;
  transition: all .2s ease;
}
.wr-search:focus-within { border-color: oklch(65% 0.28 340 / 0.5); box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-search input { background: transparent; border: 0; outline: 0; color: inherit; font-size: 13px; flex: 1; font-family: inherit; }
.wr-search kbd { font-family: ui-monospace, monospace; font-size: 10px; padding: 2px 5px; border-radius: 4px; background: var(--b300); color: oklch(90% 0.02 330 / 0.6); }
.wr-icon-btn {
  background: transparent; border: 1px solid transparent; border-radius: 6px; padding: 6px; cursor: pointer;
  color: var(--bc); transition: all .15s ease; display: inline-flex; align-items: center; justify-content: center;
}
.wr-icon-btn:hover { background: oklch(65% 0.28 340 / 0.08); border-color: oklch(65% 0.28 340 / 0.2); }

/* ---------- Card ------------------------------------------------------- */
@keyframes border-shimmer {
  0%   { border-color: oklch(65% 0.28 340 / 0.15); }
  50%  { border-color: oklch(55% 0.2 300 / 0.25); }
  100% { border-color: oklch(65% 0.28 340 / 0.15); }
}
.wr-card {
  background: var(--b100);
  border: 1px solid oklch(65% 0.28 340 / 0.12);
  border-radius: 12px;
  padding: 18px;
}
.wr-card-glow {
  box-shadow: 0 0 20px oklch(65% 0.28 340 / 0.08);
  animation: border-shimmer 4s ease-in-out infinite;
  transition: box-shadow .3s ease, transform .2s ease;
}
.wr-card-glow:hover { box-shadow: 0 0 30px oklch(65% 0.28 340 / 0.2), 0 0 60px oklch(55% 0.2 300 / 0.05); transform: translateY(-1px); }

.wr-two-col { display: grid; grid-template-columns: 1.2fr 1fr; gap: 18px; }
@media (max-width: 1000px) { .wr-two-col { grid-template-columns: 1fr; } }

/* ---------- Stat grid -------------------------------------------------- */
.wr-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 1000px) { .wr-stat-grid { grid-template-columns: repeat(2, 1fr); } }
.wr-stat {
  background: var(--b100); border: 1px solid oklch(65% 0.28 340 / 0.12); border-radius: 12px;
  padding: 14px 16px; box-shadow: 0 0 20px oklch(65% 0.28 340 / 0.08);
  animation: border-shimmer 4s ease-in-out infinite;
  transition: transform .2s ease, box-shadow .3s ease;
}
.wr-stat:hover { transform: translateY(-1px); }
.wr-stat-pink   { border-left: 3px solid transparent; border-image: linear-gradient(180deg, oklch(65% 0.28 340), oklch(75% 0.15 350)) 1; }
.wr-stat-purple { border-left: 3px solid transparent; border-image: linear-gradient(180deg, oklch(55% 0.2 300), oklch(65% 0.28 340)) 1; }
.wr-stat-rose   { border-left: 3px solid transparent; border-image: linear-gradient(180deg, oklch(75% 0.15 350), oklch(55% 0.2 300)) 1; }
.wr-stat-label  { font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: oklch(90% 0.02 330 / 0.55); font-weight: 600; }
.wr-stat-value  { font-size: 28px; font-weight: 700; margin-top: 4px;
  background: linear-gradient(135deg, oklch(85% 0.05 340), oklch(90% 0.02 330));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.wr-stat-delta { font-size: 12px; margin-top: 4px; color: oklch(90% 0.02 330 / 0.55); }
.wr-stat-delta.pos { color: oklch(80% 0.15 160); }
.wr-stat-delta.neg { color: oklch(75% 0.2 20); }

/* ---------- Divider ---------------------------------------------------- */
/* Header text opaque and lower-chroma (#403 R2): the old 65%-chroma-.28 pink
   needs alpha 0.96+ to clear AA, so muting-by-alpha can't work here. The
   saturated pink identity lives in the decorative gradient lines below,
   which are exempt (R4) and keep their original values. */
.wr-divider-sparkle {
  display: flex; align-items: center; gap: 8px;
  color: oklch(70% 0.22 340); font-size: 12px; letter-spacing: 0.3em;
  text-transform: uppercase;
}
.wr-divider-sparkle::before, .wr-divider-sparkle::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, transparent, oklch(65% 0.28 340 / 0.3), oklch(55% 0.2 300 / 0.2), transparent);
}

/* A <summary> reusing .wr-divider-sparkle needs its own affordance: the divider
   is display:flex, which suppresses the native disclosure marker in Chrome
   (markers render only for display:list-item), so list-style alone draws nothing
   and the control is indistinguishable from a static section heading (#401). */
.wr-disclosure { cursor: pointer; list-style: none; }
.wr-disclosure::-webkit-details-marker { display: none; }
.wr-disclosure-marker {
  flex: none; display: inline-block; font-size: 9px;
  transition: transform .15s ease;
}
details[open] > .wr-disclosure .wr-disclosure-marker { transform: rotate(90deg); }

/* ---------- Button ----------------------------------------------------- */
.wr-btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid transparent; border-radius: 8px;
  font-family: inherit; font-weight: 600; cursor: pointer;
  transition: all .2s ease; color: var(--bc); background: transparent;
}
.wr-btn-md { height: 36px; padding: 0 16px; font-size: 13px; }
.wr-btn-sm { height: 30px; padding: 0 12px; font-size: 12px; }
.wr-btn-primary { background: var(--grad-btn); color: oklch(98% 0.01 340); border: none; box-shadow: 0 0 15px oklch(65% 0.28 340 / 0.3); }
.wr-btn-primary:hover { background: var(--grad-btn-h); box-shadow: 0 0 25px oklch(65% 0.28 340 / 0.5); transform: translateY(-1px); }
.wr-btn-secondary { background: var(--secondary); color: oklch(95% 0.01 300); }
.wr-btn-ghost { background: transparent; color: oklch(90% 0.02 330 / 0.8); }
.wr-btn-ghost:hover { background: oklch(65% 0.28 340 / 0.08); color: var(--bc); }
.wr-btn-outline { border-color: oklch(65% 0.28 340 / 0.4); }
.wr-btn-outline:hover { background: oklch(65% 0.28 340 / 0.08); }
@keyframes sparkle { 0%,100% { opacity:0; transform: scale(0) rotate(0); } 50% { opacity:1; transform: scale(1) rotate(180deg); } }
.wr-btn-sparkle::after { content: "\2728"; position: absolute; top: -2px; right: -2px; font-size: 0.7rem; animation: sparkle 2s ease-in-out infinite; pointer-events: none; }

/* ---------- Badge ------------------------------------------------------ */
.wr-badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 22px; padding: 0 10px; border-radius: 9999px;
  font-size: 11px; font-weight: 600;
}
.wr-badge-primary { background: var(--primary); color: oklch(98% 0.01 340); box-shadow: 0 0 8px oklch(65% 0.28 340 / 0.3); }
.wr-badge-success { background: var(--success); color: oklch(15% 0.02 160); box-shadow: 0 0 8px oklch(70% 0.2 160 / 0.3); }
.wr-badge-error   { background: var(--error);   color: oklch(98% 0.01 20);  box-shadow: 0 0 8px oklch(60% 0.25 20 / 0.3); }
.wr-badge-warning { background: var(--warning); color: oklch(15% 0.02 80);  box-shadow: 0 0 8px oklch(75% 0.15 80 / 0.3); }
.wr-badge-info    { background: var(--info);    color: oklch(98% 0.01 250); }
/* Required-secret env-var chip (#388 MCP Catalog card grid, mock 2e) — an
   amber-tinted `code` pill distinct from the neutral `wr-badge`/image-tag
   chips, flagging "this backend needs a secret" at a glance in the grid. */
.wr-secret-chip {
  font-family: ui-monospace, monospace; font-size: 9px; white-space: nowrap;
  background: oklch(75% 0.15 80 / 0.12); color: oklch(85% 0.12 80);
  padding: 2px 6px; border-radius: 5px;
}
@keyframes pulse-dot { 50% { opacity: .4; } }
.wr-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: pulse-dot 2s cubic-bezier(.4,0,.6,1) infinite; }

/* ---------- Input ------------------------------------------------------ */
.wr-field { display: flex; flex-direction: column; gap: 4px; }
.wr-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: oklch(90% 0.02 330 / 0.55); font-weight: 600; }
.wr-input {
  height: 36px; padding: 0 12px; border-radius: 8px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.2);
  color: var(--bc); font-family: inherit; font-size: 13px; outline: 0;
  transition: all .2s ease;
}
.wr-input:focus { border-color: oklch(65% 0.28 340 / 0.5); box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
/* Informational-only field (#388 install-modal secrets_schema preview: shows
   the required env var + placeholder shape with no functional input — the
   value is entered later, per key, in key settings). Stays legible (not the
   browser default heavy dim) since the placeholder text is the point. */
.wr-input:disabled { opacity: 0.65; cursor: not-allowed; }

/* ---------- Avatar ----------------------------------------------------- */
.wr-avatar { border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; background: oklch(25% 0.03 320); color: oklch(85% 0.02 320); flex: none; }
.wr-avatar-admin { background: var(--primary); color: oklch(98% 0.01 340); box-shadow: 0 0 12px oklch(65% 0.28 340 / 0.4), 0 0 24px oklch(55% 0.2 300 / 0.15); }

/* ---------- Table ------------------------------------------------------ */
.wr-table-wrap { overflow-x: auto; }
.wr-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.wr-table thead th {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: oklch(90% 0.02 330 / 0.55); font-weight: 600;
  text-align: left; padding: 8px 10px;
  border-bottom: 1px solid oklch(65% 0.28 340 / 0.15);
}
.wr-table tbody td { padding: 11px 10px; border-bottom: 1px solid oklch(13% 0.03 320); transition: background .15s ease; }
.wr-table tbody tr:hover td { background: oklch(65% 0.28 340 / 0.04); }
.wr-provider-cell { display: flex; align-items: center; gap: 10px; }
.wr-provider-emoji {
  width: 28px; height: 28px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center;
  background: oklch(16% 0.02 320); border: 1px solid oklch(65% 0.28 340 / 0.12); font-size: 14px;
}

/* ---------- Activity --------------------------------------------------- */
.wr-activity { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.wr-activity li { display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px; border-radius: 8px; transition: background .15s ease; }
.wr-activity li:hover { background: oklch(65% 0.28 340 / 0.04); }

.wr-mini-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px; border-radius: 8px;
  background: oklch(16% 0.02 320); border: 1px solid oklch(65% 0.28 340 / 0.1);
  font-size: 13px;
}

/* A selectable label row for a checkbox-driven list (#382, 3a provider-
   credentials pin picker): plain row by default, tinted when its checkbox
   is checked. Distinct from wr-mini-row (a static display row, no
   checked/hover states) — this one is a form-control label. */
.wr-pin-row {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 7px; border-radius: 7px; cursor: pointer;
}
.wr-pin-row:hover { background: oklch(65% 0.28 340 / 0.05); }
.wr-pin-row-checked, .wr-pin-row-checked:hover { background: oklch(65% 0.28 340 / 0.08); }

/* ---------- Flash ------------------------------------------------------ */
@keyframes flash-in { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
.wr-flash {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-radius: 8px; font-size: 13px;
  margin-bottom: 14px;
  border-left: 3px solid transparent;
  animation: flash-in .3s ease-out;
}
.wr-flash-success { background: oklch(70% 0.2 160 / 0.1); color: oklch(85% 0.12 160); border-image: linear-gradient(180deg, oklch(70% 0.2 160), oklch(65% 0.15 180)) 1; }
.wr-flash-error   { background: oklch(60% 0.25 20 / 0.1); color: oklch(80% 0.15 20); border-image: linear-gradient(180deg, oklch(60% 0.25 20), oklch(55% 0.2 0)) 1; }
.wr-flash-warning { background: oklch(75% 0.15 80 / 0.1); color: oklch(85% 0.12 80); border-image: linear-gradient(180deg, oklch(75% 0.15 80), oklch(70% 0.12 60)) 1; }
.wr-flash-info    { background: oklch(65% 0.15 250 / 0.1); color: oklch(80% 0.1 250); border-image: linear-gradient(180deg, oklch(65% 0.15 250), oklch(60% 0.12 270)) 1; }

/* ---------- Modal ------------------------------------------------------ */
.wr-modal-root { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 100; padding: 20px; }
.wr-modal-backdrop { position: absolute; inset: 0; background: oklch(10% 0.05 320 / 0.7); backdrop-filter: blur(4px); }
/* The backdrop is <form method="dialog"><button>close</button></form>: the button
   is what actually submits (and so dismisses) the dialog. Stretch it over the whole
   backdrop and strip the UA chrome — otherwise it paints as a default grey "close"
   button in the corner and only that 47x21 patch dismisses on click (#399). */
.wr-modal-backdrop button {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: transparent; border: 0; color: transparent;
  font-size: 0; cursor: pointer;
}
.wr-modal-box {
  position: relative; background: var(--b100);
  border: 1px solid oklch(65% 0.28 340 / 0.2); border-radius: 16px;
  padding: 20px; max-width: 440px; width: 100%;
  box-shadow: 0 0 40px oklch(65% 0.28 340 / 0.15), 0 0 80px oklch(55% 0.2 300 / 0.05);
  animation: fade-up .25s ease-out;
}
.wr-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }

/* ---------- Login ------------------------------------------------------ */
.wr-login-root { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.wr-login-card {
  background: var(--b100); border: 1px solid oklch(65% 0.28 340 / 0.15); border-radius: 16px;
  padding: 28px; width: 100%; max-width: 400px;
  box-shadow: 0 0 40px oklch(65% 0.28 340 / 0.15), 0 0 80px oklch(55% 0.2 300 / 0.05);
  animation: border-shimmer 4s ease-in-out infinite;
}

/* ---------- Empty state ------------------------------------------------ */
.wr-empty { display: flex; flex-direction: column; align-items: center; padding: 30px 10px; text-align: center; }
.wr-empty img { filter: drop-shadow(0 0 10px oklch(65% 0.28 340 / 0.4)); opacity: 0.8; }

/* =========================================================================
   3. wr-* EXTENSION RECIPES  (NEW — canonical, in the kit's style)
   Components the admin templates need that the handoff kit lacks. Coined
   names follow the handoff convention (wr-<component>[-<variant|size>]).
   CSS-only interaction (drawer/modal/collapse) uses the same hidden-input
   `:checked` technique the templates already carry — no JavaScript.
   ========================================================================= */

/* ---------- Button extensions ------------------------------------------ */
.wr-btn:disabled, .wr-btn[disabled] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
.wr-btn-xs { height: 24px; padding: 0 8px; font-size: 11px; border-radius: 6px; }
.wr-btn-accent { background: var(--accent); color: oklch(15% 0.02 350); }
.wr-btn-accent:hover { background: oklch(80% 0.15 350); }
/* Darker than --error, and hover darkens further (#403 R3): the token's 60%
   red holds the label at 4.11:1 and the old lightening hover fell to 3.56.
   The token itself is unchanged — alerts/borders still use it. */
.wr-btn-error { background: oklch(55% 0.25 20); color: oklch(98% 0.01 20); }
.wr-btn-error:hover { background: oklch(50% 0.25 20); }
.wr-btn-wide { padding-left: 40px; padding-right: 40px; }
.wr-btn-square { padding: 0; aspect-ratio: 1; }
.wr-btn-circle { padding: 0; aspect-ratio: 1; border-radius: 9999px; }

/* ---------- Badge extensions ------------------------------------------- */
.wr-badge-secondary { background: var(--secondary); color: oklch(95% 0.01 300); }
.wr-badge-accent    { background: var(--accent); color: oklch(15% 0.02 350); }
.wr-badge-ghost     { background: oklch(65% 0.28 340 / 0.08); color: oklch(90% 0.02 330 / 0.8); }
.wr-badge-neutral   { background: oklch(25% 0.03 320); color: oklch(85% 0.02 320); }
.wr-badge-outline   { background: transparent; border: 1px solid oklch(65% 0.28 340 / 0.4); color: inherit; }
.wr-badge-sm { height: 18px; padding: 0 8px; font-size: 10px; }
.wr-badge-xs { height: 16px; padding: 0 6px; font-size: 9px; gap: 4px; }

/* ---------- Card extensions -------------------------------------------- */
/* wr-card carries the padding; wr-card-body is the flex-column content flow
   inside it (keeps the DaisyUI card > card-body DOM without double padding). */
.wr-card-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.wr-card-title { font-size: 16px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.wr-card-glow-success { box-shadow: 0 0 20px oklch(70% 0.2 160 / 0.08); border-color: oklch(70% 0.2 160 / 0.12); }
.wr-card-glow-error   { box-shadow: 0 0 20px oklch(60% 0.25 20 / 0.08); border-color: oklch(60% 0.25 20 / 0.12); }

/* ---------- Alert ------------------------------------------------------- */
.wr-alert {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border-radius: 8px; font-size: 13px;
  border: 1px solid oklch(65% 0.28 340 / 0.15);
  background: oklch(20% 0.02 320); color: var(--bc);
}
.wr-alert-success { background: oklch(70% 0.2 160 / 0.12); color: oklch(85% 0.12 160); border-color: oklch(70% 0.2 160 / 0.4); }
.wr-alert-error   { background: oklch(60% 0.25 20 / 0.12); color: oklch(80% 0.15 20); border-color: oklch(60% 0.25 20 / 0.4); }
.wr-alert-warning { background: oklch(75% 0.15 80 / 0.12); color: oklch(85% 0.12 80); border-color: oklch(75% 0.15 80 / 0.4); }
.wr-alert-info    { background: oklch(65% 0.15 250 / 0.12); color: oklch(80% 0.1 250); border-color: oklch(65% 0.15 250 / 0.4); }
.wr-alert-soft    { background: oklch(20% 0.02 320 / 0.6); }

/* ---------- Form field extensions -------------------------------------- */
/* wr-field (column wrapper) + wr-label (label text) come from the handoff.
   wr-label-row is the DaisyUI-style label row container; wr-label-alt the
   small helper text under a field. */
.wr-label-row { display: flex; align-items: center; justify-content: space-between; padding: 2px 0; gap: 8px; }
.wr-label-alt { font-size: 12px; color: oklch(90% 0.02 330 / 0.55); }
.wr-input::placeholder { color: oklch(90% 0.02 330 / 0.55); }
.wr-input-sm { height: 30px; font-size: 12px; padding: 0 10px; }
.wr-input-xs { height: 24px; font-size: 11px; padding: 0 8px; border-radius: 6px; }

.wr-select {
  height: 36px; padding: 0 32px 0 12px; border-radius: 8px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.2);
  color: var(--bc); font-family: inherit; font-size: 13px; outline: 0;
  transition: all .2s ease; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23c9a9c9' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 14px;
}
.wr-select:focus { border-color: oklch(65% 0.28 340 / 0.5); box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-select-sm { height: 30px; font-size: 12px; padding: 0 30px 0 10px; }
.wr-select-xs { height: 24px; font-size: 11px; padding: 0 26px 0 8px; border-radius: 6px; background-size: 12px; }

.wr-textarea {
  padding: 8px 12px; border-radius: 8px; min-height: 72px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.2);
  color: var(--bc); font-family: inherit; font-size: 13px; outline: 0;
  transition: all .2s ease; resize: vertical;
}
.wr-textarea::placeholder { color: oklch(90% 0.02 330 / 0.55); }
.wr-textarea:focus { border-color: oklch(65% 0.28 340 / 0.5); box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-textarea-sm { font-size: 12px; min-height: 56px; padding: 6px 10px; }

.wr-file-input {
  height: 36px; border-radius: 8px; font-size: 13px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.2);
  color: var(--bc); font-family: inherit; outline: 0;
  transition: all .2s ease; overflow: hidden;
}
.wr-file-input::file-selector-button {
  height: 100%; margin-right: 12px; padding: 0 14px; border: 0; cursor: pointer;
  background: oklch(25% 0.03 320); color: var(--bc);
  font-family: inherit; font-size: 13px; font-weight: 600;
}
.wr-file-input:focus { border-color: oklch(65% 0.28 340 / 0.5); box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-file-input-sm { height: 30px; font-size: 12px; }
.wr-file-input-sm::file-selector-button { padding: 0 12px; font-size: 12px; }

/* ---------- Checkbox / radio / toggle ----------------------------------- */
.wr-checkbox {
  appearance: none; -webkit-appearance: none;
  width: 20px; height: 20px; border-radius: 5px; cursor: pointer;
  border: 1px solid oklch(65% 0.28 340 / 0.4); background: var(--b200);
  display: inline-grid; place-content: center; transition: all .15s ease; flex: none;
}
.wr-checkbox::before {
  content: ""; width: 10px; height: 10px; transform: scale(0);
  transition: transform .1s ease-in-out; transform-origin: center;
  box-shadow: inset 1em 1em oklch(98% 0.01 340);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.wr-checkbox:checked { background: var(--primary); border-color: transparent; }
.wr-checkbox:checked::before { transform: scale(1); }
.wr-checkbox:focus-visible { box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-checkbox-primary:checked { background: var(--primary); }
.wr-checkbox-sm { width: 16px; height: 16px; }
.wr-checkbox-sm::before { width: 8px; height: 8px; }
.wr-checkbox-xs { width: 14px; height: 14px; border-radius: 4px; }
.wr-checkbox-xs::before { width: 7px; height: 7px; }

.wr-radio {
  appearance: none; -webkit-appearance: none;
  width: 20px; height: 20px; border-radius: 50%; cursor: pointer;
  border: 1px solid oklch(65% 0.28 340 / 0.4); background: var(--b200);
  display: inline-grid; place-content: center; transition: all .15s ease; flex: none;
}
.wr-radio::before {
  content: ""; width: 10px; height: 10px; border-radius: 50%; transform: scale(0);
  transition: transform .1s ease-in-out; background: oklch(98% 0.01 340);
}
.wr-radio:checked { background: var(--primary); border-color: transparent; }
.wr-radio:checked::before { transform: scale(1); }
.wr-radio:focus-visible { box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.3), 0 0 15px oklch(65% 0.28 340 / 0.15); }
.wr-radio-primary:checked { background: var(--primary); }
.wr-radio-sm { width: 16px; height: 16px; }
.wr-radio-sm::before { width: 8px; height: 8px; }
.wr-radio-xs { width: 14px; height: 14px; }
.wr-radio-xs::before { width: 7px; height: 7px; }

.wr-toggle {
  appearance: none; -webkit-appearance: none; cursor: pointer; flex: none;
  width: 40px; height: 22px; border-radius: 9999px; position: relative;
  background: oklch(30% 0.03 320); border: 1px solid oklch(65% 0.28 340 / 0.25);
  transition: background .2s ease;
}
.wr-toggle::before {
  content: ""; position: absolute; top: 50%; left: 3px; transform: translateY(-50%);
  width: 16px; height: 16px; border-radius: 50%; background: oklch(85% 0.02 330);
  transition: left .2s ease, background .2s ease;
}
.wr-toggle:checked { background: var(--primary); border-color: transparent; box-shadow: 0 0 10px oklch(65% 0.28 340 / 0.4); }
.wr-toggle:checked::before { left: calc(100% - 19px); background: oklch(98% 0.01 340); }
.wr-toggle-primary:checked { background: var(--primary); }
.wr-toggle-sm { width: 34px; height: 18px; }
.wr-toggle-sm::before { width: 13px; height: 13px; }
.wr-toggle-sm:checked::before { left: calc(100% - 16px); }
.wr-toggle-xs { width: 28px; height: 15px; }
.wr-toggle-xs::before { width: 11px; height: 11px; }
.wr-toggle-xs:checked::before { left: calc(100% - 13px); }

/* ---------- Table density ----------------------------------------------- */
.wr-table-sm thead th { padding: 6px 8px; }
.wr-table-sm tbody td { padding: 7px 8px; }
.wr-table-xs { font-size: 12px; }
.wr-table-xs thead th { padding: 4px 6px; }
.wr-table-xs tbody td { padding: 5px 6px; }

/* ---------- Stats block (container + items) ----------------------------- */
/* wr-stat (handoff) is the standalone stat CARD for the redesigned pages.
   wr-stats/wr-stat-item reproduce the DaisyUI stats strip: a bordered
   container of plain stat cells (labels/values reuse the handoff recipes). */
.wr-stats { display: flex; flex-direction: column; background: var(--b100); border: 1px solid oklch(65% 0.28 340 / 0.12); border-radius: 12px; overflow: hidden; }
.wr-stats-horizontal { flex-direction: row; }
.wr-stat-item { padding: 14px 16px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* ---------- Loading ------------------------------------------------------ */
@keyframes wr-spin { to { transform: rotate(360deg); } }
/* Standalone spinner box — scoped off buttons so wr-loading can double as the
   button busy STATE below without the box sizing shrinking the button. */
.wr-loading:not(.wr-btn) { display: inline-block; width: 24px; height: 24px; vertical-align: middle; }
.wr-loading-spinner {
  border-radius: 50%;
  border: 3px solid oklch(65% 0.28 340 / 0.2);
  border-top-color: var(--primary);
  animation: wr-spin .7s linear infinite;
}
.wr-loading-md { width: 24px; height: 24px; }
.wr-loading-sm { width: 18px; height: 18px; border-width: 2px; }

/* Button busy state: JS toggles wr-loading onto a wr-btn (classList.add) —
   the label goes transparent and a centered spinner appears, reproducing the
   old DaisyUI .btn.loading state (which came from the removed CDN). wr-btn is
   already position:relative, so the ::after centers over the label. */
.wr-btn.wr-loading { color: transparent; pointer-events: none; }
.wr-btn.wr-loading::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid oklch(98% 0.01 340 / 0.3);
  border-top-color: oklch(98% 0.01 340);
  animation: wr-spin .7s linear infinite;
}

@keyframes pulse-pink {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}
.wr-loading-pulse { animation: pulse-pink 1.5s ease-in-out infinite; color: oklch(65% 0.28 340); }

/* ---------- Progress ----------------------------------------------------- */
.wr-progress { appearance: none; -webkit-appearance: none; width: 100%; height: 8px; border-radius: 9999px; overflow: hidden; background: oklch(25% 0.03 320); border: 0; }
.wr-progress::-webkit-progress-bar { background: oklch(25% 0.03 320); }
.wr-progress::-webkit-progress-value { background: var(--primary); border-radius: 9999px; }
.wr-progress::-moz-progress-bar { background: var(--primary); }
.wr-progress-primary::-webkit-progress-value { background: var(--primary); }
.wr-progress-primary::-moz-progress-bar { background: var(--primary); }
.wr-progress-info::-webkit-progress-value { background: var(--info); }
.wr-progress-info::-moz-progress-bar { background: var(--info); }
.wr-progress-warning::-webkit-progress-value { background: var(--warning); }
.wr-progress-warning::-moz-progress-bar { background: var(--warning); }
.wr-progress-error::-webkit-progress-value { background: var(--error); }
.wr-progress-error::-moz-progress-bar { background: var(--error); }

/* ---------- Plain divider ------------------------------------------------ */
.wr-divider { display: flex; align-items: center; margin: 12px 0; color: oklch(90% 0.02 330 / 0.55); font-size: 12px; }
.wr-divider::before, .wr-divider::after { content: ""; flex: 1; height: 1px; background: oklch(65% 0.28 340 / 0.15); }
.wr-divider:not(:empty)::before { margin-right: 8px; }
.wr-divider:not(:empty)::after { margin-left: 8px; }

/* ---------- Link --------------------------------------------------------- */
.wr-link { text-decoration: underline; cursor: pointer; }
.wr-link-hover { text-decoration: none; }
.wr-link-hover:hover { text-decoration: underline; }
.wr-link-primary { color: var(--primary); }

/* ---------- Menu / navbar ------------------------------------------------ */
.wr-menu { display: flex; flex-direction: column; list-style: none; margin: 0; padding: 0; }
.wr-menu li > a { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; }
.wr-menu-sm li > a { padding: 6px 8px; font-size: 13px; }
.wr-navbar { display: flex; align-items: center; gap: 8px; min-height: 56px; padding: 8px 12px; }

/* ---------- Join --------------------------------------------------------- */
.wr-join { display: inline-flex; }
.wr-join .wr-join-item { border-radius: 0; }
.wr-join > :first-child.wr-join-item { border-top-left-radius: 8px; border-bottom-left-radius: 8px; }
.wr-join > :last-child.wr-join-item { border-top-right-radius: 8px; border-bottom-right-radius: 8px; }
.wr-join > .wr-join-item:not(:first-child) { margin-left: -1px; }

/* ---------- Collapse (CSS-only, checkbox-driven) ------------------------- */
.wr-collapse {
  position: relative; display: grid;
  grid-template-rows: auto 0fr; overflow: hidden;
  border-radius: 8px; border: 1px solid oklch(65% 0.28 340 / 0.12);
  transition: grid-template-rows .2s ease;
}
.wr-collapse > input[type="checkbox"], .wr-collapse > input[type="radio"] {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; z-index: 1; margin: 0;
}
.wr-collapse-title { padding: 12px 14px; font-weight: 600; display: flex; align-items: center; }
.wr-collapse-content { grid-row: 2; min-height: 0; padding: 0 14px; overflow: hidden; }
.wr-collapse:has(> input:checked) { grid-template-rows: auto 1fr; }
.wr-collapse:has(> input:checked) > .wr-collapse-content { padding-bottom: 12px; }
.wr-collapse-arrow > .wr-collapse-title::after {
  content: ""; margin-left: auto; width: 8px; height: 8px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .2s ease;
}
.wr-collapse-arrow:has(> input:checked) > .wr-collapse-title::after { transform: rotate(-135deg); }

/* ---------- Toggleable modal (CSS-only) ---------------------------------- */
/* wr-modal-root/box/actions/backdrop (handoff) style a statically-visible
   modal; wr-modal adds the DaisyUI-style show/hide layer: hidden until its
   sibling input.wr-modal-toggle is :checked, it is :target, wr-modal-open
   is set, or (native <dialog>) it carries the browser's own [open] attribute
   via .showModal() — the real templates (keys.html, users.html) use dialog +
   .showModal(), not the checkbox/:target techniques, so [open] must be in
   this reveal group too. Same hidden-checkbox + label technique the
   templates already use for the other two paths. */
.wr-modal {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center; padding: 20px;
  background: oklch(10% 0.05 320 / 0.7);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .2s ease, visibility .2s ease;
}
.wr-modal.wr-modal-open,
.wr-modal:target,
.wr-modal[open],
input.wr-modal-toggle:checked + .wr-modal {
  opacity: 1; visibility: visible; pointer-events: auto;
}
.wr-modal .wr-modal-box { max-height: calc(100vh - 40px); overflow-y: auto; }

/* ---------- Responsive drawer -------------------------------------------- */
/* The non-tabbable checkbox remains the CSS state hook, while drawer.js gives
   the visible mobile button dialog semantics and managed focus.  Below 1024px
   the sidebar overlays content; wr-drawer-docked pins it beside content. */
.wr-drawer { position: relative; display: grid; grid-template-columns: 1fr; width: 100%; }
.wr-drawer-toggle { position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.wr-drawer-body-locked { overflow: hidden; }
.wr-drawer-content { grid-column: 1; grid-row: 1; min-width: 0; }
.wr-drawer-side {
  grid-column: 1; grid-row: 1;
  position: fixed; inset: 0; width: 100%; height: 100dvh;
  display: grid; grid-template-columns: auto;
  visibility: hidden; pointer-events: none;
  transition: visibility 0s linear .2s;
  overflow-x: hidden; overflow-y: auto;
}
.wr-drawer-side > .wr-drawer-overlay {
  position: fixed; inset: 0; background: oklch(10% 0.05 320 / 0.5);
  padding: 0; border: 0; cursor: pointer; z-index: 0;
  opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility .2s ease;
}
.wr-drawer-side > aside {
  position: relative; z-index: 1;
  transform: translateX(-100%); transition: transform .2s ease;
}
.wr-drawer-toggle:checked ~ .wr-drawer-side {
  visibility: visible; pointer-events: auto;
  transition-delay: 0s;
}
.wr-drawer-toggle:checked ~ .wr-drawer-side > .wr-drawer-overlay { opacity: 1; visibility: visible; }
.wr-drawer-toggle:checked ~ .wr-drawer-side > aside { transform: translateX(0); }

@media (min-width: 1024px) {
  .wr-drawer-docked { grid-template-columns: max-content 1fr; }
  .wr-drawer-docked > .wr-drawer-side {
    position: sticky; inset: auto; top: 0; width: auto; height: 100dvh;
    grid-column: 1; visibility: visible; pointer-events: auto;
    transition: none;
  }
  .wr-drawer-docked > .wr-drawer-side > .wr-drawer-overlay { display: none; }
  .wr-drawer-docked > .wr-drawer-side > aside { transform: none; transition: none; }
  .wr-drawer-docked > .wr-drawer-content { grid-column: 2; }
}
/* Sidebar gradient background inside the drawer */
.wr-drawer-side aside { background: var(--grad-side); }

/* ---------- Avatar wrapper (DaisyUI avatar > div DOM) -------------------- */
.wr-avatar-wrap { display: inline-flex; }
.wr-avatar-wrap > div { aspect-ratio: 1; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; }
.wr-avatar-glow > div { box-shadow: 0 0 12px oklch(65% 0.28 340 / 0.4), 0 0 24px oklch(55% 0.2 300 / 0.15); }

/* ---------- Chrome extras ------------------------------------------------ */
.wr-splash-card {
  position: relative; z-index: 10;
  backdrop-filter: blur(12px);
  background: oklch(16% 0.02 320 / 0.85);
  border: 1px solid oklch(65% 0.28 340 / 0.2);
}
.wr-splash-card:hover { border-color: oklch(65% 0.28 340 / 0.4); }
.wr-splash-glow {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 600px; height: 600px; border-radius: 50%;
  background: radial-gradient(circle, oklch(65% 0.28 340 / 0.08) 0%, transparent 70%);
  pointer-events: none; z-index: 1;
}
@keyframes sparkle-float {
  0%, 100% { opacity: 0.4; transform: translateY(0) scale(1); }
  50% { opacity: 1; transform: translateY(-3px) scale(1.1); }
}
.wr-sparkle-icon { display: inline-block; animation: sparkle-float 2s ease-in-out infinite; }
.wr-sidebar-logo-border {
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, transparent, oklch(65% 0.28 340 / 0.3), transparent) 1;
}
.wr-empty svg { filter: drop-shadow(0 0 10px oklch(65% 0.28 340 / 0.2)); }
.wr-empty p { color: oklch(65% 0.28 340 / 0.5); }

/* ---------- Docked drawer panel (#385, epic #376 M4) -------------------- */
/* A right-docked settings drawer built on the same native-<dialog> reveal
   machinery as .wr-modal (opacity/visibility/pointer-events gated by [open],
   :target, or the hidden-checkbox path). Where .wr-modal centers its box,
   .wr-drawer-panel pins the box to the right edge, full-height, and slides it
   in from the right. Used by keys.html: row "Edit" fetches the key-settings
   sections into <dialog class="wr-modal wr-drawer-panel"> and .showModal()s it.
   The same sections partial renders full-width on the detail page (one
   template, two frames — A1b). */
/* Override the UA <dialog> box model: it defaults to width/height:fit-content with
   margin:auto and max-width/max-height:calc(100% - 34px), which centred the panel
   (making place-items:stretch and the box's margin-left:auto no-ops) and clipped a
   100dvh box 34px short, pushing its bottom below the fold (#398). */
.wr-drawer-panel {
  place-items: stretch; padding: 0; margin: 0;
  width: 100vw; max-width: 100vw;
  height: 100dvh; max-height: 100dvh;
}
.wr-drawer-panel .wr-drawer-panel-box {
  /* Positioned so the panel wins paint order against the absolutely-positioned
     .wr-modal-backdrop sibling that follows it. Without this the box is static,
     the backdrop paints on top, and its blur + 70% scrim cover the panel while
     swallowing every click (#396). .wr-modal-box gets this via its own
     position:relative; the drawer box was missing it. */
  position: relative; z-index: 1;
  margin-left: auto; height: 100%;
  width: min(560px, calc(100vw - 1rem));
  border-radius: 16px 0 0 16px;
  border: 1px solid oklch(65% 0.28 340 / 0.25); border-right: 0;
  background: var(--b100);
  box-shadow: -20px 0 60px oklch(65% 0.28 340 / 0.2), -40px 0 120px oklch(55% 0.2 300 / 0.1);
  overflow-y: auto; padding: 22px 24px;
  transform: translateX(24px); opacity: 0;
  transition: transform .22s ease, opacity .22s ease;
}
.wr-drawer-panel[open] .wr-drawer-panel-box,
.wr-drawer-panel.wr-modal-open .wr-drawer-panel-box,
.wr-drawer-panel:target .wr-drawer-panel-box {
  transform: translateX(0); opacity: 1;
}

/* The key-settings sections wrapper (both frames render inside it). Passive
   today — it exists as a stable styling/JS hook (data-frame lives on it) so
   #363/#364/#365/#366/#382 can scope their section styles without re-marking
   the DOM. */
.wr-key-settings { display: block; }

/* ---------- Segmented control (#385) ------------------------------------ */
/* A radio-group rendered as one connected pill bar — the 3a Priority control
   (HIGH / NORMAL / LOW). The real radios are visually hidden; each <label>
   is a segment, and the checked radio lights its label via :checked + label
   or :has(). Fully CSS-only, keyboard-accessible (the radios still receive
   focus). */
.wr-segment {
  display: flex; gap: 3px; padding: 3px;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.2);
  border-radius: 9px;
}
.wr-segment input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.wr-segment-item {
  flex: 1; text-align: center; font-size: 12px; font-weight: 600;
  padding: 6px 0; border-radius: 7px; cursor: pointer;
  color: oklch(90% 0.02 330 / 0.55); transition: background .15s ease, color .15s ease;
}
.wr-segment-item:hover { background: oklch(65% 0.28 340 / 0.08); }
.wr-segment input[type="radio"]:checked + .wr-segment-item {
  background: var(--grad-btn); color: oklch(98% 0.01 340);
  box-shadow: 0 0 12px oklch(65% 0.28 340 / 0.35);
}
.wr-segment input[type="radio"]:focus-visible + .wr-segment-item {
  box-shadow: 0 0 0 2px oklch(65% 0.28 340 / 0.4);
}

/* ---------- Reach chip (#385, 2b list + 3a reach) ----------------------- */
/* The "Reaches" pills on the 2b key rows: a capability -> destination pill
   derived server-side from the routing graph (key allowed_services x live
   rule targets). The lit variant is a granted reach; the muted variant is an
   ambient/uncounted marker (e.g. "mcp x2"). */
.wr-reach-chip {
  display: inline-flex; align-items: center; white-space: nowrap;
  font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 9999px;
  background: oklch(65% 0.28 340 / 0.14); color: oklch(88% 0.06 340);
}
.wr-reach-chip-muted { background: var(--b300); color: oklch(90% 0.02 330 / 0.55); }

/* A read-only presentational toggle pill for the 3a reach rows — mirrors the
   wr-toggle look without being a form control (reach is a reflection of the
   allowed_services grant, not an editable switch here). Off by default; the
   -on modifier lights it. */
.wr-reach-toggle {
  position: relative; flex: none; width: 26px; height: 16px; border-radius: 9999px;
  background: var(--b300);
}
.wr-reach-toggle::before {
  content: ""; position: absolute; left: 2px; top: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: oklch(90% 0.02 330 / 0.5); transition: left .15s ease;
}
.wr-reach-toggle-on { background: var(--grad-btn); box-shadow: 0 0 8px oklch(65% 0.28 340 / 0.4); }
.wr-reach-toggle-on::before { left: calc(100% - 14px); background: oklch(98% 0.01 340); }

/* ---------- Meter (#385, budget bars on 2b/3a) -------------------------- */
/* A slim div-based budget bar (distinct from the native <progress> .wr-progress
   recipe): a track with a gradient fill sized by inline width. The 2b rows use
   the -xs height; the 3a general section uses the default. Empty track (no
   fill child) is the "no budget" state. */
.wr-meter { height: 6px; border-radius: 3px; background: var(--b300); overflow: hidden; }
.wr-meter-xs { height: 5px; }
.wr-meter-fill { height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--secondary), var(--primary)); }
.wr-meter-fill-warning { background: linear-gradient(90deg, var(--warning), var(--error)); }

/* ---------- Keys list row grid (#385, 2b) ------------------------------- */
/* The 7-column layout of the promoted API-keys rows: key / priority / reaches
   / token-budget / last-used / status / actions. Collapses to a stacked block
   below the lg breakpoint so the glowing rows stay readable on mobile. */
.wr-key-grid {
  display: grid; gap: 12px; align-items: center;
  grid-template-columns: 1fr;
}
.wr-key-grid-header { display: none; }
@media (min-width: 1024px) {
  .wr-key-grid {
    grid-template-columns: 220px 90px minmax(0, 1fr) 170px 96px 88px 116px;
  }
  .wr-key-grid-header { display: grid; }
}

/* ---------- Attention strip (#384, 2a dashboard) ------------------------ */
/* Load-bearing dashboard cards deep-linking to the routing canvas — failing
   providers / unrouted models from the routing graph's validation block
   (#379). A full-row <a>, not a wr-alert: it needs a hover glow and a
   right-aligned CTA the plain alert recipe doesn't carry. -warning is a
   dormant/credential-gap tint, -error is an active-failure tint (mirrors the
   badge palette so the two read as escalating severity). Collapses entirely
   when validation is null/absent (ambient contract) — there is no "empty"
   variant to style. */
.wr-attn-strip {
  display: flex; align-items: center; gap: 10px;
  border-radius: 10px; padding: 10px 14px;
  text-decoration: none; transition: box-shadow .2s ease;
}
.wr-attn-strip:hover { box-shadow: 0 0 20px currentColor; }
.wr-attn-strip svg { flex: none; }
.wr-attn-strip-message { font-size: 12px; color: oklch(90% 0.02 330 / 0.8); }
.wr-attn-strip-cta {
  margin-left: auto; font-size: 11px; font-weight: 600; white-space: nowrap;
}
.wr-attn-strip-warning {
  background: oklch(75% 0.15 80 / 0.08); border: 1px solid oklch(75% 0.15 80 / 0.35);
  color: oklch(75% 0.15 80 / 0.25);
}
.wr-attn-strip-warning .wr-attn-strip-message strong,
.wr-attn-strip-warning .wr-attn-strip-cta { color: oklch(85% 0.12 80); }
.wr-attn-strip-error {
  background: oklch(60% 0.25 20 / 0.07); border: 1px solid oklch(60% 0.25 20 / 0.35);
  color: oklch(60% 0.25 20 / 0.25);
}
.wr-attn-strip-error .wr-attn-strip-message strong,
.wr-attn-strip-error .wr-attn-strip-cta { color: oklch(80% 0.15 20); }

/* ---------- Traffic-by-route row (#384, 2a dashboard) -------------------- */
/* One 24h traffic row: monospace "model -> destination" label, a wr-meter
   bar sized relative to the busiest row, and a right-aligned count. Ambient
   (#378) — the whole section hides when the ledger has no rows; a row with
   any failures gets the -warning fill + a trailing X mark on the count. */
.wr-traffic-row { display: flex; align-items: center; gap: 10px; }
.wr-traffic-row-label {
  font-family: ui-monospace, monospace; font-size: 10.5px; color: oklch(85% 0.08 340);
  width: 200px; flex: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wr-traffic-row-failing .wr-traffic-row-label { color: oklch(85% 0.12 80); }
.wr-traffic-row .wr-meter { flex: 1; height: 6px; }
.wr-traffic-row-count {
  font-size: 10.5px; color: oklch(90% 0.02 330 / 0.6); width: 64px; flex: none;
  text-align: right; white-space: nowrap;
}
.wr-traffic-row-failing .wr-traffic-row-count { color: oklch(85% 0.12 80); }

/* ---------- Route chip (#391, 1d Providers card) ------------------------- */
/* The "routed by" pills on a provider card: a monospace model/pattern label
   with a small metric suffix (24h count, or a static "prefix" note). Distinct
   from .wr-reach-chip (a pill-shaped capability->destination grant on the 2b
   key rows) — this is a rectangular, monospace routing fact, matching mock
   1d's chip treatment. -failing tints it like the traffic row's warning
   state; -muted is the ambient/no-traffic-yet marker (e.g. the static accepted
   prefix shown alongside real 24h rows). */
.wr-route-chip {
  display: inline-flex; align-items: center; white-space: nowrap;
  font-family: ui-monospace, monospace; font-size: 10px; padding: 3px 8px; border-radius: 6px;
  background: oklch(55% 0.2 300 / 0.15); color: oklch(85% 0.08 320);
  border: 1px solid oklch(55% 0.2 300 / 0.25);
}
.wr-route-chip-muted {
  background: var(--b200); color: oklch(90% 0.02 330 / 0.55); border-color: transparent;
}
.wr-route-chip-failing {
  background: oklch(75% 0.15 80 / 0.12); color: oklch(85% 0.12 80);
  border-color: oklch(75% 0.15 80 / 0.3);
}

/* ---------- Models layout (#387, 2d llama.cpp Models) -------------------- */
/* Main column (wizard + catalog) beside a fixed-width sidebar (disk / VRAM
   placement / jobs cards) — distinct ratio from wr-two-col's 1.2fr/1fr split,
   which uses two proportional columns rather than a content column plus a
   fixed info rail. */
.wr-models-layout { display: grid; grid-template-columns: 1fr 340px; gap: 16px; align-items: start; }
@media (max-width: 1000px) { .wr-models-layout { grid-template-columns: 1fr; } }

/* Pull-wizard quant row: a radio-selectable candidate in the per-quant
   VRAM-fit table. wr-quant-row-picked highlights the selected/recommended
   row (tinted background + primary border); disabled (does-not-fit-at this
   placement) rows dim via the shared .opacity-40 utility rather than a new
   modifier. */
.wr-quant-row {
  display: grid; grid-template-columns: 20px 1fr 80px 90px 110px; gap: 8px;
  align-items: center; padding: 6px 8px; border-radius: 7px; border: 1px solid transparent;
}
.wr-quant-row-picked {
  background: oklch(65% 0.28 340 / 0.08); border-color: oklch(65% 0.28 340 / 0.3);
}
.wr-quant-row-radio {
  width: 12px; height: 12px; border-radius: 50%; border: 2px solid oklch(90% 0.02 330 / 0.3);
  appearance: none; -webkit-appearance: none; margin: 0; cursor: pointer;
}
.wr-quant-row-radio:checked {
  border-color: var(--primary); background: var(--primary);
  box-shadow: 0 0 6px oklch(65% 0.28 340 / 0.6);
}
.wr-quant-row-radio:disabled { cursor: not-allowed; opacity: .5; }

/* Placement toggle (GPU/CPU segmented pill) reuses wr-segment; the models
   page's version is compact enough to want an xs variant of the pill. */
.wr-segment-xs .wr-segment-item { padding: 3px 10px; font-size: 10px; }

/* ---------- Users list row grid (#386, 2c Users) -------------------------- */
/* The 6-column layout of the glowing user rows: user / role / status /
   API keys / joined / actions. Mirrors wr-key-grid's collapse-to-stacked
   behavior below lg so the rows stay readable on mobile. */
.wr-user-grid {
  display: grid; gap: 12px; align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
  .wr-user-grid {
    grid-template-columns: 1fr 130px 100px 130px 90px 110px;
  }
}

/* In-place key rollup under a user row (HTMX-loaded, toggled by the API-keys
   chevron button). A left-inset shelf so the mini-rows read as "belonging"
   to the avatar column above them, distinct from wr-mini-row's own general
   activity-list usage elsewhere. */
.wr-user-keys-rollup {
  background: var(--b200); border-top: 1px solid oklch(65% 0.28 340 / 0.1);
  padding: 10px 16px 12px 58px;
  display: flex; flex-direction: column; gap: 6px;
}
.wr-user-keys-rollup-chevron { transition: transform .15s ease; }
.wr-user-keys-rollup-chevron.wr-user-keys-rollup-chevron-open { transform: rotate(180deg); }

/* ---------- Routing canvas island (#380, mock 1a) ------------------------ */
/* The React island's own chrome. It consumes these classes via plain
   className strings from JSX — no CSS-in-JS, no second stylesheet (the
   island-boundary rule in ux-style-guide.md). Loading/error states first
   (T1's deferred wr-island-* minor, closed here), then the canvas shell,
   node chrome per class, edge styling, and the validation panel. */

.wr-island-loading, .wr-island-error {
  display: flex; align-items: center; justify-content: center;
  height: 100%; min-height: 200px;
  font-size: 13px; color: oklch(90% 0.02 330 / 0.6);
}
.wr-island-error {
  color: oklch(85% 0.12 80);
  background: oklch(75% 0.15 80 / 0.06);
}

/* Canvas pane + docked inspector, side by side (mock 1a) — the outer flex
   wrapper Canvas.jsx renders into the single #routing-canvas-root mount. */
.wr-island-shell { display: flex; gap: 14px; align-items: stretch; }

.wr-island-canvas {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 100%;
  background:
    radial-gradient(oklch(65% 0.28 340 / 0.10) 1px, transparent 1.5px) 0 0 / 22px 22px,
    var(--b200);
}

/* ---- Node chrome: shared card + per-class accent stripe ---- */
.wr-node-card {
  background: var(--b100);
  border: 1px solid oklch(65% 0.28 340 / 0.22);
  border-radius: 10px;
  box-shadow: 0 0 14px oklch(65% 0.28 340 / 0.1);
  padding: 7px 10px;
  font-size: 12px;
  min-width: 170px;
  transition: box-shadow .2s ease, transform .2s ease;
}
.wr-node-card:hover { box-shadow: 0 0 24px oklch(65% 0.28 340 / 0.3); transform: translateY(-1px); }
.wr-node-handle { background: var(--b300); border: 2px solid var(--primary); width: 9px; height: 9px; box-shadow: 0 0 6px oklch(65% 0.28 340 / 0.5); }

/* Accent stripes per node class (brief: pink=key, purple=rule, rose=queue,
   plain=destination). */
.wr-node-key { border-left: 3px solid var(--primary); }
.wr-node-rule { border-left: 3px solid var(--secondary); }
.wr-node-queue { border-left: 3px solid var(--accent); }
.wr-node-destination { border-left: 3px solid transparent; }

.wr-node-kicker {
  font-size: 9px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: oklch(65% 0.28 340 / 0.85);
}
.wr-node-title { font-size: 12.5px; font-weight: 600; margin-top: 1px; }
.wr-node-revoked { opacity: .55; }

/* ---- Key node ---- */
.wr-node-services { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; margin-top: 7px; }
.wr-node-service-row { font-size: 10.5px; color: oklch(90% 0.02 330 / 0.6); }
.wr-node-service-empty { font-size: 10.5px; color: oklch(90% 0.02 330 / 0.55); font-style: italic; }

/* Pinned-credential "ports" (#382, mock 1a's stacked handle dots) — a
   second row group below the service list, one row per
   key.pinned_credentials entry. Read-only illustration (A3: no click/drag
   handler is ever attached to these rows) styled distinctly from the plain
   service rows so a pin reads as "this key has its own credential for X"
   at a glance, echoing the mock's small glowing port dot next to each
   stacked service row. */
.wr-node-pins {
  display: flex; flex-direction: column; align-items: flex-end; gap: 5px;
  margin-top: 7px; padding-top: 7px; border-top: 1px solid oklch(65% 0.28 340 / 0.12);
}
.wr-node-pin-row { display: flex; align-items: center; gap: 5px; font-size: 10px; color: oklch(88% 0.06 340); }
.wr-node-pin-label { text-transform: capitalize; }
.wr-node-pin-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--b300); border: 2px solid var(--primary);
  box-shadow: 0 0 6px oklch(65% 0.28 340 / 0.5);
}

/* ---- Rule node ---- */
.wr-node-model { font-family: ui-monospace, monospace; font-size: 11px; font-weight: 600; color: oklch(85% 0.08 340); }
.wr-node-rule-detail { font-size: 10px; color: oklch(90% 0.02 330 / 0.55); margin-top: 3px; display: flex; gap: 4px; }
.wr-node-rule-arrow { opacity: .6; }

/* Built-ins (prefix/capability/default/env routes): dashed border, read-only
   affordance, no glow — visually distinct from operator-managed pins. */
.wr-node-builtin {
  border-style: dashed; border-color: oklch(65% 0.28 340 / 0.35);
  box-shadow: none; cursor: default;
}
.wr-node-builtin:hover { box-shadow: none; transform: none; }
/* DB pins (kind:"pin") get the normal solid/glow treatment above, plus a
   thin left accent line distinguishing an operator-managed exact route from
   a plain (still-editable-if-it-were-a-pin) rule card at a glance — #382
   fills this hook (previously a no-op) now that the credential-pin ports
   land on KeyNode; unrelated to those (see .wr-node-pins below — this is
   the ROUTING-rule "pin" concept, kind:"pin" in nodes.rules, not a
   provider-credential pin). */
.wr-node-pin { border-left-color: oklch(85% 0.08 340 / 0.7); }

/* ---- Queue node ---- */
.wr-node-priority-rows { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.wr-node-priority-row {
  display: flex; align-items: center; gap: 5px;
  font-size: 9px; font-weight: 600; color: oklch(90% 0.02 330 / 0.6);
}
.wr-node-priority-label { width: 26px; flex: none; }
.wr-node-priority-bar { flex: 1; height: 4px; border-radius: 2px; background: var(--b300); overflow: hidden; }
.wr-node-priority-fill { display: block; height: 4px; border-radius: 2px; }
.wr-node-priority-fill-high { background: var(--primary); }
.wr-node-priority-fill-norm { background: var(--primary); }
.wr-node-priority-fill-low { background: var(--secondary); }
.wr-node-priority-value { flex: none; min-width: 1em; text-align: right; }
.wr-node-queue-footer { font-size: 9.5px; color: oklch(90% 0.02 330 / 0.55); margin-top: 6px; }
.wr-node-per-replica-mark { margin-left: 2px; color: var(--warning); cursor: help; }

/* ---- Destination node ---- */
.wr-node-destination-head { display: flex; align-items: center; gap: 8px; }
.wr-node-destination-glyph {
  width: 24px; height: 24px; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--b200); border: 1px solid oklch(65% 0.28 340 / 0.15); font-size: 12px; flex: none;
}
.wr-node-destination-name { font-size: 12.5px; font-weight: 600; flex: 1; }
.wr-node-destination-caps { font-size: 10px; color: oklch(90% 0.02 330 / 0.55); margin-top: 6px; }
.wr-node-destination-models { font-size: 10px; color: oklch(90% 0.02 330 / 0.55); margin-top: 5px; }
.wr-node-destination-warn { font-size: 10px; color: oklch(85% 0.12 80); margin-top: 5px; }
.wr-node-disabled { opacity: .55; }
.wr-node-no-credential { border-color: oklch(75% 0.15 80 / 0.5); box-shadow: 0 0 16px oklch(75% 0.15 80 / 0.15); }
.wr-node-no-credential:hover { box-shadow: 0 0 24px oklch(75% 0.15 80 / 0.35); }

.wr-node-vram-row {
  display: flex; align-items: center; gap: 6px; margin-top: 7px;
  font-size: 9px; font-weight: 600; color: oklch(90% 0.02 330 / 0.55);
}
.wr-node-vram-bar { flex: 1; height: 5px; border-radius: 3px; background: var(--b300); overflow: hidden; }
.wr-node-vram-fill { display: block; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--secondary), var(--primary)); }

/* Selection ring (mock 1a): reactflow flags the clicked node `.selected` on
   its own wrapper (Canvas.jsx sets each node's `selected` from the App-owned
   selection state) — style through to the card inside rather than the
   library's default (no) chrome, so a click always has a visible result. */
.react-flow__node.selected .wr-node-card {
  border-color: var(--primary);
  box-shadow: 0 0 18px oklch(65% 0.28 340 / 0.45), inset 0 0 12px oklch(65% 0.28 340 / 0.12);
}

/* ---- Edges ---- */
/* reactflow renders edge paths as SVG <path> with the className we assign in
   graph.js's layoutEdges(); style via stroke/marker on that path element. */
.wr-edge .react-flow__edge-path { stroke: oklch(65% 0.28 340 / 0.35); stroke-width: 1.5px; }
.wr-edge-derived .react-flow__edge-path { stroke: oklch(65% 0.28 340 / 0.2); stroke-width: 1.25px; }
/* stroke-dasharray intentionally omitted here — reactflow's own `.animated`
   class (set via layoutEdges()'s `animated: style === "active"`) already
   applies a dashed stroke + the dashdraw marching-ants animation; this rule
   only needs to override color/width to match mock 1a's hot-pink active edge. */
.wr-edge-active .react-flow__edge-path { stroke: oklch(72% 0.26 340 / 0.9); stroke-width: 2px; }
.wr-edge-failing .react-flow__edge-path { stroke: oklch(75% 0.15 80 / 0.8); stroke-width: 1.5px; stroke-dasharray: 4 4; }
.wr-edge-idle .react-flow__edge-path { stroke: oklch(90% 0.02 330 / 0.14); stroke-width: 1.5px; }
.wr-edge-plain .react-flow__edge-path { stroke: oklch(65% 0.28 340 / 0.35); stroke-width: 1.5px; }

/* ---- Zoom/lock control strip ---- */
/* Restyles reactflow's <Controls> panel with kit.css's existing wr-icon-btn
   vocabulary rather than the library's own CSS (constraint: no second visual
   language). */
.wr-node-controls {
  background: var(--b100); border: 1px solid oklch(65% 0.28 340 / 0.2);
  border-radius: 8px; overflow: hidden; box-shadow: none;
}
.wr-node-controls button.react-flow__controls-button {
  background: transparent; border: none; border-bottom: 1px solid oklch(65% 0.28 340 / 0.1);
  border-radius: 0; padding: 7px; width: auto; height: auto;
}
.wr-node-controls button.react-flow__controls-button:last-child { border-bottom: none; }
.wr-node-controls button.react-flow__controls-button:hover { background: oklch(65% 0.28 340 / 0.08); }
.wr-node-controls button.react-flow__controls-button svg { fill: oklch(90% 0.02 330 / 0.8); max-width: 13px; max-height: 13px; }
.wr-node-controls button[aria-pressed="true"] { background: oklch(65% 0.28 340 / 0.15); }

/* ---- Validation panel ---- */
.wr-validation-panel {
  display: flex; flex-direction: column; gap: 6px;
  position: absolute; right: 14px; bottom: 14px; width: 264px;
  background: oklch(16% 0.02 320 / 0.92); border: 1px solid oklch(75% 0.15 80 / 0.35);
  border-radius: 10px; padding: 10px 12px; z-index: 25; backdrop-filter: blur(4px);
}
.wr-validation-panel-title {
  display: flex; align-items: center; gap: 6px;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase; font-weight: 700;
  color: oklch(85% 0.12 80);
}
.wr-validation-row { font-size: 11px; line-height: 1.45; color: oklch(90% 0.02 330 / 0.75); }
.wr-validation-row-subject { color: oklch(85% 0.12 80); font-weight: 600; }
/* T3: rows that focus a node in the inspector on click (unrouted-model rows
   always; failing-provider rows always — both kinds have a corresponding
   node per the brief). */
.wr-validation-row-clickable { cursor: pointer; border-radius: 6px; margin: -4px -6px; padding: 4px 6px; transition: background .15s ease; }
.wr-validation-row-clickable:hover, .wr-validation-row-clickable:focus-visible { background: oklch(75% 0.15 80 / 0.1); }
.wr-validation-row-link { color: oklch(85% 0.12 80); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

/* ---- Inspector (docked right, T3, mock 1a) ---- */
.wr-inspector {
  width: 300px;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  box-sizing: border-box;
}
.wr-inspector-header { display: flex; align-items: center; justify-content: space-between; }
.wr-inspector-kind {
  font-size: 9px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: oklch(70% 0.15 300);
}
.wr-inspector-close { padding: 4px; width: 22px; height: 22px; font-size: 11px; line-height: 1; }
.wr-inspector-title-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wr-inspector-title { font-size: 16px; font-weight: 700; }
.wr-inspector-rows { display: flex; flex-direction: column; }
.wr-inspector-row {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 12px; padding: 6px 0; border-bottom: 1px solid oklch(65% 0.28 340 / 0.08);
}
.wr-inspector-row-label { color: oklch(90% 0.02 330 / 0.55); flex: none; white-space: nowrap; }
.wr-inspector-row-value { font-weight: 500; text-align: right; min-width: 0; }
.wr-inspector-warn {
  display: flex; gap: 8px; font-size: 11.5px; line-height: 1.45;
  background: oklch(75% 0.15 80 / 0.1); color: oklch(85% 0.12 80);
  border-left: 3px solid var(--warning); border-radius: 6px; padding: 9px 10px;
}
.wr-inspector-warn svg { flex: none; margin-top: 1px; }
.wr-inspector-actions { display: flex; gap: 8px; margin-top: auto; }
.wr-inspector-note { font-size: 11px; color: oklch(90% 0.02 330 / 0.55); align-self: center; }
/* Inert "Edit" affordance for a rule (T4 ships the real editor) — a <span>,
   not a disabled <button>/<a>, since there is no action to suppress, only
   one to advertise as not-yet-available; wr-btn's own :disabled selector
   doesn't apply to a span, so this mirrors that same opacity/cursor pair. */
.wr-btn-disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

/* ---- Canvas editing (#381 T4) ---- */
/* Pending treatment: a staged-but-unsaved pin (create/edit) — the optimistic
   render tags its rule node pending:true (staging.js) and RuleNode adds
   wr-node-pending + a "pending" chip. Chosen treatment: a dashed pink accent
   stripe (distinct from wr-node-builtin's dim grey dash — this dash is
   attention-pink, "you have unsaved work here" not "read-only") plus a small
   corner chip. Documented in the style guide's routing-canvas section. */
.wr-node-pending {
  border-style: dashed;
  border-color: oklch(72% 0.26 340 / 0.7);
  box-shadow: 0 0 18px oklch(65% 0.28 340 / 0.28);
}
.wr-node-pending:hover { box-shadow: 0 0 24px oklch(65% 0.28 340 / 0.4); }
.wr-node-pending-chip {
  position: absolute; top: -8px; right: -6px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 99px;
  background: var(--primary); color: oklch(18% 0.02 320);
  box-shadow: 0 0 8px oklch(65% 0.28 340 / 0.5);
}

/* A pin's rule->destination edge is drag-reconnectable (retarget the pin);
   a subtle solid-pink accent invites the drag without competing with the
   active-traffic animation. reactflow renders the handle end-caps itself. */
.wr-edge-reconnectable .react-flow__edge-path { stroke: oklch(72% 0.26 340 / 0.6); }

/* Dirty-state action bar — the mock-1a header cluster, docked at the top of
   the canvas pane (the island owns only the canvas, not the server-rendered
   page <h2>). Present only when the staged diff is non-empty. */
.wr-routing-dirtybar {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: 30; display: flex; align-items: center; gap: 12px;
  padding: 7px 10px 7px 14px; border-radius: 10px;
  background: oklch(16% 0.02 320 / 0.94); border: 1px solid oklch(65% 0.28 340 / 0.35);
  box-shadow: 0 0 20px oklch(65% 0.28 340 / 0.25); backdrop-filter: blur(4px);
}
.wr-routing-dirtybar-status { font-size: 11.5px; }
.wr-routing-dirtybar-pending { color: oklch(90% 0.02 330 / 0.75); font-weight: 600; }
.wr-routing-dirtybar-error { color: oklch(72% 0.19 25); font-weight: 600; }
.wr-routing-dirtybar-conflict { color: oklch(85% 0.12 80); font-weight: 600; max-width: 340px; }
.wr-routing-dirtybar-actions { display: flex; gap: 8px; }

/* "+ Add route" create affordance — top-right of the canvas pane, out of the
   way of the dirty bar (centered) and the validation panel (bottom-right). */
.wr-routing-add-route { position: absolute; top: 12px; right: 14px; z-index: 28; }

/* Inline pin-editor fields (Inspector's RuleEditor) — a stacked label+input
   column reusing wr-input; the editor is otherwise a wr-inspector clone. */
.wr-inspector-editing .wr-inspector-actions { flex-wrap: wrap; }
.wr-inspector-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 4px; }
.wr-inspector-field-label {
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase; font-weight: 600;
  color: oklch(90% 0.02 330 / 0.55);
}

/* =========================================================================
   4. UTILITY LAYER  (permanent)
   Naming rule (stated per the 2026-08-04 ruling): component/structural
   vocabulary is wr-*; single-purpose layout/typography/color utilities stay
   UNPREFIXED. The handoff kit ships no utility layer at all (its recipes own
   their layout), so there is no wr-* utility convention to follow, none of
   these names collide with any component family the Task-3 gate bans, and
   they are generic CSS vocabulary rather than DaisyUI API. This is a
   used-only subset (every rule below appears in router/ui/templates/), not a
   Tailwind re-implementation. Breakpoints: md=768px, lg=1024px, xl=1280px.
   This layer must stay BELOW the recipes: utilities win ties.
   ========================================================================= */

/* ---- Display / layout ---- */
.block { display: block; }
.inline { display: inline; }
.inline-block { display: inline-block; }
.inline-flex { display: inline-flex; }
.flex { display: flex; }
.grid { display: grid; }
.hidden { display: none; }

.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1 1 0%; }
.flex-none { flex: none; }
.shrink-0 { flex-shrink: 0; }
.grow { flex-grow: 1; }

.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-start { justify-content: flex-start; }
.justify-end { justify-content: flex-end; }
.self-start { align-self: flex-start; }

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

/* ---- Position ---- */
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.inset-0 { inset: 0; }
.top-2 { top: 0.5rem; }
.right-2 { right: 0.5rem; }
.z-0 { z-index: 0; }
.z-10 { z-index: 10; }
.z-20 { z-index: 20; }

/* ---- Sizing ---- */
.w-full { width: 100%; }
.w-2 { width: 0.5rem; }
.w-3 { width: 0.75rem; }
.w-3\.5 { width: 0.875rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
.w-8 { width: 2rem; }
.w-10 { width: 2.5rem; }
.w-12 { width: 3rem; }
.w-1\.5 { width: 0.375rem; }
.w-32 { width: 8rem; }
.w-40 { width: 10rem; }
.w-44 { width: 11rem; }
.w-64 { width: 16rem; }
.min-w-0 { min-width: 0; }
.h-2 { height: 0.5rem; }
.h-3 { height: 0.75rem; }
.h-3\.5 { height: 0.875rem; }
.h-4 { height: 1rem; }
.h-5 { height: 1.25rem; }
.h-10 { height: 2.5rem; }
.h-12 { height: 3rem; }
.h-1\.5 { height: 0.375rem; }
.min-h-0 { min-height: 0; }
.min-h-full { min-height: 100%; }
.min-h-screen { min-height: 100vh; }
.max-w-xs { max-width: 20rem; }
.max-w-sm { max-width: 24rem; }
.max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; }
.max-w-2xl { max-width: 42rem; }
.max-w-full { max-width: 100%; }
.max-w-24 { max-width: 6rem; }
.max-w-40 { max-width: 10rem; }
.max-w-48 { max-width: 12rem; }
.max-w-\[200px\] { max-width: 200px; }
.max-h-20 { max-height: 5rem; }
.max-h-48 { max-height: 12rem; }
.max-h-96 { max-height: 24rem; }

/* ---- Spacing: margin ---- */
.mx-auto { margin-left: auto; margin-right: auto; }
.mx-4 { margin-left: 1rem; margin-right: 1rem; }
.my-0 { margin-top: 0; margin-bottom: 0; }
.my-1 { margin-top: 0.25rem; margin-bottom: 0.25rem; }
.my-2 { margin-top: 0.5rem; margin-bottom: 0.5rem; }
.mb-0\.5 { margin-bottom: 0.125rem; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mt-0\.5 { margin-top: 0.125rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-1\.5 { margin-top: 0.375rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.ml-1 { margin-left: 0.25rem; }
.ml-2 { margin-left: 0.5rem; }
.ml-5 { margin-left: 1.25rem; }
.mr-3 { margin-right: 0.75rem; }

/* ---- Spacing: padding ---- */
.p-0 { padding: 0; }
.p-2 { padding: 0.5rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.px-0 { padding-left: 0; padding-right: 0; }
.px-1 { padding-left: 0.25rem; padding-right: 0.25rem; }
.px-1\.5 { padding-left: 0.375rem; padding-right: 0.375rem; }
.px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.py-0 { padding-top: 0; padding-bottom: 0; }
.py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; }
.py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
.py-1\.5 { padding-top: 0.375rem; padding-bottom: 0.375rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
.py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.pt-1 { padding-top: 0.25rem; }
.pt-3 { padding-top: 0.75rem; }
.pb-1 { padding-bottom: 0.25rem; }
.pb-3 { padding-bottom: 0.75rem; }
.pl-4 { padding-left: 1rem; }

/* ---- Gap ---- */
.gap-1 { gap: 0.25rem; }
.gap-1\.5 { gap: 0.375rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-x-4 { column-gap: 1rem; }
.gap-y-0\.5 { row-gap: 0.125rem; }
.gap-y-1 { row-gap: 0.25rem; }

/* ---- Space-between (Tailwind's owl selector) ---- */
.space-x-3 > * + * { margin-left: 0.75rem; }
.space-y-0\.5 > * + * { margin-top: 0.125rem; }
.space-y-1 > * + * { margin-top: 0.25rem; }
.space-y-2 > * + * { margin-top: 0.5rem; }
.space-y-3 > * + * { margin-top: 0.75rem; }
.space-y-4 > * + * { margin-top: 1rem; }
.space-y-6 > * + * { margin-top: 1.5rem; }

/* ---- Border ---- */
.border { border: 1px solid oklch(65% 0.28 340 / 0.12); }
.border-b { border-bottom: 1px solid oklch(65% 0.28 340 / 0.12); }
.border-r { border-right: 1px solid oklch(65% 0.28 340 / 0.12); }
.border-t { border-top: 1px solid oklch(65% 0.28 340 / 0.12); }
.border-l-2 { border-left-width: 2px; border-left-style: solid; }
.border-base-300 { border-color: var(--b300); }
.border-base-300\/30 { border-color: oklch(13% 0.03 320 / 0.3); }
.border-base-300\/50 { border-color: oklch(13% 0.03 320 / 0.5); }
.border-primary\/20 { border-color: oklch(65% 0.28 340 / 0.2); }
.border-warning\/20 { border-color: oklch(75% 0.15 80 / 0.2); }

.rounded { border-radius: 6px; }
.rounded-lg { border-radius: 8px; }
.rounded-full { border-radius: 9999px; }

/* ---- Background colors ---- */
.bg-base-100 { background-color: var(--b100); }
.bg-base-200 { background-color: var(--b200); }
.bg-base-200\/30 { background-color: oklch(16% 0.02 320 / 0.3); }
.bg-base-200\/50 { background-color: oklch(16% 0.02 320 / 0.5); }
.bg-base-300 { background-color: var(--b300); }
.bg-base-300\/50 { background-color: oklch(13% 0.03 320 / 0.5); }
.bg-base-content\/20 { background-color: oklch(90% 0.02 330 / 0.2); }
.bg-primary { background-color: var(--primary); }
.bg-neutral { background-color: oklch(25% 0.03 320); }
.bg-success { background-color: var(--success); }
.bg-warning { background-color: var(--warning); }
.bg-error { background-color: var(--error); }

/* ---- Text colors ---- */
.text-primary { color: var(--primary); }
.text-primary-content { color: oklch(98% 0.01 340); }
.text-neutral-content { color: oklch(85% 0.02 320); }
.text-accent { color: var(--accent); }
.text-success { color: var(--success); }
.text-warning { color: var(--warning); }
.text-warning\/80 { color: oklch(75% 0.15 80 / 0.8); }
/* Text-role red is lighter than --error: the token itself (60%) tops out at
   4.16:1 on --b100, below AA for text — but it stays put for borders/alerts
   (#403 R3). The /70 tier needs alpha >= 0.83 over this red to clear 4.5. */
.text-error { color: oklch(70% 0.2 20); }
.text-error\/70 { color: oklch(70% 0.2 20 / 0.85); }
/* Muted ladder (#403 R1): bc-alpha below ~0.53 composites under 4.5:1 on the
   page backgrounds, so 0.55 is the floor for anything that is text. /20 is
   decorative-only (error-page art) — never attach it to readable text. */
.text-base-content\/20 { color: oklch(90% 0.02 330 / 0.2); }
.text-base-content\/30 { color: oklch(90% 0.02 330 / 0.55); }
.text-base-content\/40 { color: oklch(90% 0.02 330 / 0.62); }
.text-base-content\/50 { color: oklch(90% 0.02 330 / 0.7); }
.text-base-content\/60 { color: oklch(90% 0.02 330 / 0.78); }
.text-base-content\/70 { color: oklch(90% 0.02 330 / 0.87); }

/* ---- Typography ---- */
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
.text-4xl { font-size: 2.25rem; line-height: 2.5rem; }
.text-5xl { font-size: 3rem; line-height: 1; }
.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
.italic { font-style: italic; }
.uppercase { text-transform: uppercase; }
.capitalize { text-transform: capitalize; }
.tracking-wider { letter-spacing: 0.05em; }
.leading-tight { line-height: 1.25; }
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.break-all { word-break: break-all; }
.break-words { overflow-wrap: break-word; }
.whitespace-nowrap { white-space: nowrap; }
.whitespace-pre-wrap { white-space: pre-wrap; }
.align-bottom { vertical-align: bottom; }
.list-disc { list-style-type: disc; }
.list-inside { list-style-position: inside; }

/* ---- Effects / misc ---- */
.cursor-pointer { cursor: pointer; }
.opacity-30 { opacity: 0.3; }
.opacity-40 { opacity: 0.4; }
.opacity-50 { opacity: 0.5; }
.overflow-hidden { overflow: hidden; }
.overflow-auto { overflow: auto; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }
.shadow-sm { box-shadow: 0 1px 2px 0 oklch(0% 0 0 / 0.15); }
.shadow-lg { box-shadow: 0 10px 15px -3px oklch(0% 0 0 / 0.25), 0 4px 6px -4px oklch(0% 0 0 / 0.25); }
.shadow-2xl { box-shadow: 0 25px 50px -12px oklch(0% 0 0 / 0.5); }
.stroke-current { stroke: currentColor; }
.select-all { user-select: all; }

/* ---- Hover-state utilities the templates use ---- */
.hover\:bg-base-200\/50:hover { background-color: oklch(16% 0.02 320 / 0.5); }
.hover\:bg-base-300:hover { background-color: var(--b300); }
.hover\:text-base-content:hover { color: var(--bc); }
.hover\:text-primary\/80:hover { color: oklch(65% 0.28 340 / 0.8); }

/* ---- animate-pulse (Tailwind) ---- */
@keyframes wr-pulse { 50% { opacity: 0.5; } }
.animate-pulse { animation: wr-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }

/* ---- Responsive: md (>=768px) ---- */
@media (min-width: 768px) {
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ---- Responsive: lg (>=1024px) ---- */
@media (min-width: 1024px) {
  .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .lg\:hidden { display: none; }
  .lg\:p-6 { padding: 1.5rem; }
}

/* ---- Responsive: xl (>=1280px) ---- */
@media (min-width: 1280px) {
  .xl\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- U4 compute-node operations --------------------------------- */
.wr-u4-eyebrow {
  color: oklch(75% 0.18 340);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.wr-u4-section-head,
.wr-u4-detail-head {
  align-items: flex-start;
  display: flex;
  gap: 16px;
  justify-content: space-between;
}
.wr-fleet-pulse { position: relative; overflow: hidden; }
.wr-fleet-pulse::before {
  animation: wr-fleet-beat 2.4s ease-in-out infinite;
  background: var(--success);
  border-radius: 999px;
  box-shadow: 0 0 14px oklch(70% 0.2 160 / 0.5);
  content: "";
  height: 7px;
  position: absolute;
  right: 18px;
  top: 18px;
  width: 7px;
}
@keyframes wr-fleet-beat {
  0%, 100% { opacity: 0.35; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1.25); }
}
.wr-u4-summary { display: flex; flex-wrap: wrap; gap: 8px 14px; padding-right: 20px; }
.wr-u4-summary span { color: oklch(90% 0.02 330 / 0.62); font-size: 12px; }
.wr-u4-summary strong { color: var(--bc); font-size: 14px; }
.wr-u4-chip-row { align-items: center; display: flex; flex-wrap: wrap; gap: 6px; }
.wr-u4-mobile-list { display: none; }
.wr-u4-mobile-card,
.wr-u4-key-card,
.wr-u4-assignment-row {
  background: oklch(15% 0.025 320 / 0.72);
  border: 1px solid oklch(65% 0.28 340 / 0.12);
  border-radius: 10px;
  padding: 14px;
}
.wr-u4-compact-dl { display: grid; gap: 7px; margin-top: 12px; }
.wr-u4-compact-dl > div { display: flex; gap: 12px; justify-content: space-between; }
.wr-u4-compact-dl dt { color: oklch(90% 0.02 330 / 0.55); font-size: 11px; }
.wr-u4-compact-dl dd { font-size: 12px; font-weight: 600; text-align: right; }
.wr-u4-inline-form {
  align-items: end;
  display: grid;
  gap: 12px;
  grid-template-columns: minmax(220px, 1fr) minmax(220px, 1fr) auto;
}
.wr-form-control { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.wr-label-text { color: oklch(90% 0.02 330 / 0.72); font-size: 12px; font-weight: 600; }
.wr-input-bordered,
.wr-select-bordered,
.wr-textarea-bordered { border-color: oklch(65% 0.28 340 / 0.22); }
.wr-u4-key-grid { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-revoke-form { align-items: end; display: grid; gap: 8px; grid-template-columns: 1fr auto; }
.wr-u4-touch { min-height: 44px; }
.wr-u4-detail-grid {
  display: grid;
  gap: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.wr-u4-detail-grid > div { border-top: 1px solid oklch(65% 0.28 340 / 0.1); padding: 10px 0; }
.wr-u4-detail-grid > div:nth-child(odd) { padding-right: 16px; }
.wr-u4-detail-grid dt { color: oklch(90% 0.02 330 / 0.55); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; }
.wr-u4-detail-grid dd { font-size: 13px; font-weight: 600; margin-top: 3px; }
.wr-u4-danger { border: 1px solid oklch(75% 0.15 80 / 0.22); border-radius: 9px; padding: 12px; }
.wr-u4-danger summary { cursor: pointer; font-size: 13px; font-weight: 700; }
.wr-btn-warning { background: var(--warning); color: oklch(15% 0.02 80); }
.wr-btn-warning:hover { filter: brightness(1.08); }
.wr-u4-assignment-form { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-span-2 { grid-column: span 2; }
.wr-u4-check { align-items: flex-start; display: flex; gap: 10px; }
.wr-u4-check small { color: oklch(90% 0.02 330 / 0.55); display: block; font-size: 11px; margin-top: 2px; }
.wr-u4-assignment-list { display: grid; gap: 10px; }
.wr-u4-assignment-row { align-items: center; display: flex; gap: 16px; justify-content: space-between; }
.wr-u4-inline-error { color: oklch(78% 0.17 20); font-size: 12px; overflow-wrap: anywhere; }
.wr-u4-lineage { display: grid; gap: 0; }
.wr-u4-lineage li { display: grid; gap: 12px; grid-template-columns: 12px 1fr; min-width: 0; padding-bottom: 16px; position: relative; }
.wr-u4-lineage li:not(:last-child)::before { background: oklch(65% 0.28 340 / 0.24); bottom: 0; content: ""; left: 5px; position: absolute; top: 12px; width: 1px; }
.wr-u4-lineage-dot { background: var(--primary); border-radius: 999px; box-shadow: 0 0 9px oklch(65% 0.28 340 / 0.5); height: 9px; margin-top: 4px; width: 9px; }
.wr-u4-secret-dialog textarea { overflow-wrap: anywhere; resize: none; width: 100%; }
.wr-u4-create-panel { border: 1px solid oklch(65% 0.28 340 / 0.2); border-radius: 9px; padding: 12px; }
.wr-u4-create-panel summary { cursor: pointer; font-size: 13px; font-weight: 700; }
.wr-u4-card-form { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-detect-panel {
  background: oklch(15% 0.025 320 / 0.72);
  border: 1px solid oklch(65% 0.28 340 / 0.18);
  border-radius: 9px;
  display: grid;
  gap: 10px;
  padding: 12px;
}
.wr-u4-detect-panel[hidden] { display: none; }
.wr-u4-detect-options { display: grid; gap: 4px; }
.wr-u4-detect-option {
  align-items: center;
  border: 1px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  display: grid;
  gap: 8px;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  padding: 7px 8px;
}
.wr-u4-detect-option:hover { background: oklch(65% 0.28 340 / 0.05); }
.wr-u4-detect-option-selected {
  background: oklch(65% 0.28 340 / 0.08);
  border-color: oklch(65% 0.28 340 / 0.3);
}
.wr-u4-detect-actions {
  align-items: center;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}
.wr-u4-filter-bar {
  align-items: end;
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.wr-u4-form-note {
  background: oklch(65% 0.28 340 / 0.07);
  border: 1px solid oklch(65% 0.28 340 / 0.13);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  font-size: 12px;
  gap: 3px;
  padding: 10px;
}
.wr-u4-form-note span { color: oklch(90% 0.02 330 / 0.7); }
.wr-u4-library-grid { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-library-card {
  background: oklch(15% 0.025 320 / 0.72);
  border: 1px solid oklch(65% 0.28 340 / 0.12);
  border-radius: 10px;
  min-width: 0;
  padding: 14px;
}
.wr-u4-member-list { display: grid; gap: 7px; }
.wr-u4-member-list li {
  align-items: center;
  border-top: 1px solid oklch(65% 0.28 340 / 0.08);
  display: flex;
  gap: 10px;
  justify-content: space-between;
  min-width: 0;
  padding-top: 8px;
}
.wr-u4-relationship-grid,
.wr-u4-metadata-grid { display: grid; gap: 16px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-link-list { display: grid; gap: 7px; }
.wr-u4-metadata-grid pre {
  background: oklch(13% 0.025 320);
  border: 1px solid oklch(65% 0.28 340 / 0.12);
  border-radius: 8px;
  font-size: 11px;
  margin-top: 8px;
  max-height: 280px;
  overflow: auto;
  padding: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

@media (max-width: 767px) {
  .wr-u4-desktop-table { display: none; }
  .wr-u4-mobile-list { display: grid; gap: 10px; }
  .wr-u4-section-head,
  .wr-u4-detail-head { align-items: stretch; flex-direction: column; }
  .wr-u4-summary { padding-right: 0; }
  .wr-u4-inline-form,
  .wr-u4-revoke-form,
  .wr-u4-assignment-form,
  .wr-u4-card-form,
  .wr-u4-detail-grid,
  .wr-u4-key-grid,
  .wr-u4-library-grid,
  .wr-u4-relationship-grid,
  .wr-u4-metadata-grid { grid-template-columns: 1fr; }
  .wr-u4-span-2 { grid-column: auto; }
  .wr-u4-assignment-row { align-items: stretch; flex-direction: column; }
  .wr-u4-detect-actions { align-items: stretch; flex-direction: column; }
  .wr-u4-detect-actions .wr-btn { width: 100%; }
  .wr-u4-assignment-row form,
  .wr-u4-assignment-row .wr-btn { width: 100%; }
  .wr-u4-member-list li { align-items: stretch; flex-direction: column; }
  .wr-u4-member-list form,
  .wr-u4-member-list .wr-btn { width: 100%; }
}

/* =========================================================================
   5. PERMANENT UNPREFIXED CLASSES
   Named by external contracts, deliberately NOT wr-*:
   - .cow / .cow-* — spawned by router/ui/static/cows.js (untouched by the
     kit migration; renaming would require touching cows.js).
   - .htmx-indicator / .htmx-request — the HTMX runtime's own configured
     class names (htmx.config.indicatorClass / requestClass).
   ========================================================================= */

/* ---------- Cow herd (splash/auth pastures) ----------------------------- */
@keyframes drift {
  0%   { transform: translateX(110vw); }
  100% { transform: translateX(-40vw); }
}
@keyframes drift-reverse {
  0%   { transform: translateX(-40vw); }
  100% { transform: translateX(110vw); }
}

.cow {
  position: fixed;
  white-space: pre;
  font-family: monospace;
  color: oklch(90% 0.02 330 / var(--cow-opacity, 0.25));
  animation: drift var(--cow-speed, 25s) linear infinite;
  animation-delay: var(--cow-delay, 0s);
  top: var(--cow-top, 50%);
  font-size: var(--cow-size, 0.6rem);
  z-index: 0;
  pointer-events: none;
  line-height: 1.1;
}
.cow-reverse { animation-name: drift-reverse; }

@keyframes disco-pulse {
  0%   { color: oklch(75% 0.3 340 / var(--cow-opacity)); text-shadow: 0 0 8px oklch(65% 0.28 340 / 0.4); }
  25%  { color: oklch(75% 0.3 290 / var(--cow-opacity)); text-shadow: 0 0 8px oklch(55% 0.2 290 / 0.4); }
  50%  { color: oklch(75% 0.3 200 / var(--cow-opacity)); text-shadow: 0 0 8px oklch(65% 0.15 200 / 0.4); }
  75%  { color: oklch(75% 0.3 100 / var(--cow-opacity)); text-shadow: 0 0 8px oklch(75% 0.15 100 / 0.4); }
  100% { color: oklch(75% 0.3 340 / var(--cow-opacity)); text-shadow: 0 0 8px oklch(65% 0.28 340 / 0.4); }
}
.cow-disco { animation: drift var(--cow-speed, 25s) linear infinite, disco-pulse 3s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

@keyframes party-wiggle {
  0%, 100% { transform: translateX(var(--drift-x, 0)) rotate(0deg); }
  25%      { transform: translateX(var(--drift-x, 0)) rotate(3deg) translateY(-4px); }
  75%      { transform: translateX(var(--drift-x, 0)) rotate(-3deg) translateY(4px); }
}
.cow-party { animation: drift var(--cow-speed, 25s) linear infinite, party-wiggle 0.8s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

.cow-flip { transform-origin: center; filter: drop-shadow(0 0 6px oklch(55% 0.2 300 / 0.3)); }

@keyframes wobble {
  0%, 100% { transform: translateX(var(--drift-x, 0)) skewX(0deg); }
  25%      { transform: translateX(var(--drift-x, 0)) skewX(5deg); }
  75%      { transform: translateX(var(--drift-x, 0)) skewX(-5deg); }
}
.cow-wobble { animation: drift var(--cow-speed, 25s) linear infinite, wobble 2s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

@keyframes ghostfade {
  0%, 100% { opacity: var(--cow-opacity, 0.2); filter: blur(0px); }
  50%      { opacity: calc(var(--cow-opacity, 0.2) * 0.3); filter: blur(2px); }
}
.cow-ghost { animation: drift var(--cow-speed, 25s) linear infinite, ghostfade 4s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

@keyframes bounce-hop {
  0%, 100% { transform: translateX(var(--drift-x, 0)) translateY(0); }
  50%      { transform: translateX(var(--drift-x, 0)) translateY(-12px); }
}
.cow-bounce { animation: drift var(--cow-speed, 25s) linear infinite, bounce-hop 0.6s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

@keyframes rainbow-shift {
  0%   { filter: hue-rotate(0deg) brightness(1.2); }
  100% { filter: hue-rotate(360deg) brightness(1.2); }
}
.cow-rainbow { animation: drift var(--cow-speed, 25s) linear infinite, rainbow-shift 6s linear infinite; animation-delay: var(--cow-delay, 0s); color: oklch(70% 0.25 340 / var(--cow-opacity)); }

@keyframes fancy-bob {
  0%, 100% { transform: translateX(var(--drift-x, 0)) translateY(0) rotate(-1deg); }
  50%      { transform: translateX(var(--drift-x, 0)) translateY(-3px) rotate(1deg); }
}
.cow-fancy { animation: drift var(--cow-speed, 25s) linear infinite, fancy-bob 3s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); text-shadow: 0 0 12px oklch(75% 0.15 60 / 0.3); }

@keyframes float-up {
  0%, 100% { transform: translateX(var(--drift-x, 0)) translateY(0); }
  50%      { transform: translateX(var(--drift-x, 0)) translateY(-8px); }
}
.cow-float { animation: drift var(--cow-speed, 25s) linear infinite, float-up 4s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); text-shadow: 0 0 10px oklch(65% 0.28 340 / 0.25); }

.cow-fast { transform: skewX(-8deg); filter: drop-shadow(4px 0 2px oklch(55% 0.2 300 / 0.2)); }

/* Reverse-direction overrides for vibe cows (swap drift -> drift-reverse) */
.cow-reverse.cow-disco   { animation: drift-reverse var(--cow-speed, 25s) linear infinite, disco-pulse 3s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-party   { animation: drift-reverse var(--cow-speed, 25s) linear infinite, party-wiggle 0.8s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-wobble  { animation: drift-reverse var(--cow-speed, 25s) linear infinite, wobble 2s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-ghost   { animation: drift-reverse var(--cow-speed, 25s) linear infinite, ghostfade 4s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-bounce  { animation: drift-reverse var(--cow-speed, 25s) linear infinite, bounce-hop 0.6s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-rainbow { animation: drift-reverse var(--cow-speed, 25s) linear infinite, rainbow-shift 6s linear infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-fancy   { animation: drift-reverse var(--cow-speed, 25s) linear infinite, fancy-bob 3s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }
.cow-reverse.cow-float   { animation: drift-reverse var(--cow-speed, 25s) linear infinite, float-up 4s ease-in-out infinite; animation-delay: var(--cow-delay, 0s); }

/* Keyboard focus must remain visible on every interactive recipe.  Use a
   light inner outline plus the kit's pink accent so the indicator separates
   from both the dark chrome and filled primary buttons (#499). */
a:focus-visible,
button:focus-visible,
.wr-btn:focus-visible,
.wr-icon-btn:focus-visible,
summary:focus-visible {
  outline: 2px solid oklch(98% 0.01 340);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px oklch(65% 0.28 340 / 0.7), 0 0 16px oklch(65% 0.28 340 / 0.35);
}

/* Honour the platform motion preference across both kit recipes and
   third-party island styles.  State changes remain immediate, while entrance
   effects, sparkles, pulses, shimmers, and moving canvas edges run at most one
   imperceptibly short iteration.  Page entrance and cow drift are removed
   explicitly so their transforms can never flash during first paint (#504). */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }

  .wr-page-enter,
  .cow {
    animation: none !important;
  }
}

/* ---------- HTMX loading indicator ------------------------------------- */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-block; }
.htmx-request.htmx-indicator { display: inline-block; }

/* ---------- Scrollbar styling ------------------------------------------ */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--b300); }
::-webkit-scrollbar-thumb { background: linear-gradient(180deg, oklch(35% 0.08 340), oklch(25% 0.05 320)); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: linear-gradient(180deg, oklch(45% 0.12 340), oklch(35% 0.08 320)); }


/* U4 capability-default cards */
.wr-u4-default-grid { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wr-u4-default-card {
  background: oklch(19% 0.025 285 / 0.82);
  border: 1px solid oklch(65% 0.28 340 / 0.18);
  border-radius: 9px;
  min-width: 0;
  padding: 14px;
}
.wr-u4-default-head { align-items: flex-start; display: flex; gap: 12px; justify-content: space-between; }
.wr-u4-default-current { align-items: baseline; display: flex; gap: 10px; margin-top: 12px; min-width: 0; }
.wr-u4-default-current span { color: oklch(90% 0.02 330 / 0.55); font-size: 11px; text-transform: uppercase; }
.wr-u4-default-current code { font-size: 11px; overflow-wrap: anywhere; }
.wr-u4-default-form { align-items: end; display: grid; gap: 8px; grid-template-columns: minmax(0, 1fr) auto; margin-top: 10px; }
.wr-u4-default-form label { color: oklch(90% 0.02 330 / 0.62); font-size: 11px; grid-column: 1 / -1; }
.wr-u4-default-guidance { color: oklch(90% 0.02 330 / 0.58); font-size: 11px; line-height: 1.45; margin-top: 10px; }

@media (max-width: 767px) {
  .wr-u4-default-grid,
  .wr-u4-default-form { grid-template-columns: 1fr; }
  .wr-u4-default-form .wr-btn { width: 100%; }
}
