/* Varahi9 — Super Admin shell, components, and views. */

*,*::before,*::after{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--bg-root);color:var(--text-primary);
  font-family:var(--font-ui);font-size:var(--fs-md);line-height:var(--lh-normal);
  -webkit-font-smoothing:antialiased;overflow:hidden;
}
:lang(te),.te{font-family:var(--font-te);line-height:1.75}
button,input,select,textarea{font:inherit;color:inherit}
a{color:var(--text-link);text-decoration:none}
a:hover{text-decoration:underline}
h1,h2,h3,h4{margin:0;font-weight:var(--fw-semibold);line-height:var(--lh-tight)}

/* Focus is never removed — only restyled. An admin console must be fully
   operable from the keyboard (WCAG 2.2 AA, Part 2 Prompt 5 §ACCESSIBILITY). */
:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;border-radius:var(--r-sm)}
:focus:not(:focus-visible){outline:none}

.skip-link{position:absolute;left:-9999px;top:0;z-index:999;background:var(--accent);
  color:var(--text-inverse);padding:var(--s-2) var(--s-4);border-radius:0 0 var(--r-md) 0}
.skip-link:focus{left:0}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ===== Shell ============================================================== */
/* height, not 100vh: the DEVELOPMENT banner is prepended ABOVE this element,
   so a flat 100vh made the shell taller than the window — which pushed the
   status bar off the bottom of the screen and left the fixed AI dock hovering
   over a 28px strip of nothing. app.js measures the banner into
   --devbanner-h (0 when there is no banner, i.e. in production). */
.shell{display:grid;height:calc(100vh - var(--devbanner-h, 0px));
  grid-template-columns:var(--nav-w) 1fr;
  grid-template-rows:var(--header-h) 1fr var(--statusbar-h);
  grid-template-areas:"brand header" "nav main" "nav status";
  transition:grid-template-columns var(--dur-normal) var(--ease)}
.shell[data-nav="collapsed"]{grid-template-columns:var(--nav-w-collapsed) 1fr}

/* ===== Brand ============================================================== */
/* Holds the nav toggle and nothing else. The product name, the saffron mark and
   the environment all live in the workspace switcher below, which states them
   per workspace instead of asserting one set for all four — a static "prod"
   badge is wrong the moment anyone selects Staging. */
.brand{grid-area:brand;display:flex;align-items:center;gap:var(--s-3);
  padding:0 var(--s-4);background:var(--bg-sunken);
  border-right:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle)}
.shell[data-nav="collapsed"] .brand{justify-content:center;padding-inline:var(--s-2)}

/* ===== Header ============================================================= */
.header{grid-area:header;display:flex;align-items:center;gap:var(--s-3);
  padding:0 var(--s-4);background:var(--bg-surface);min-width:0;overflow:hidden;
  border-bottom:1px solid var(--border-subtle);
  container-type:inline-size;container-name:hdr}
.header__crumbs{display:flex;align-items:center;gap:var(--s-2);
  font-size:var(--fs-sm);color:var(--text-secondary);min-width:0;flex-shrink:1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.header__crumbs > *{flex:none}
.header__crumbs b{color:var(--text-primary);font-weight:var(--fw-medium)}
.header__spacer{flex:0 0 var(--s-4)}

.searchbtn{display:flex;align-items:center;gap:var(--s-2);height:30px;padding:0 var(--s-3);
  flex:0 1 260px;min-width:150px;background:var(--bg-sunken);border:1px solid var(--border-default);
  border-radius:var(--r-md);color:var(--text-tertiary);cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease)}
.searchbtn:hover{border-color:var(--border-strong)}
.searchbtn > svg{flex:none}
.searchbtn > span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.searchbtn kbd{margin-left:auto;flex:none;font-family:var(--font-mono);font-size:var(--fs-2xs);
  padding:1px 5px;border:1px solid var(--border-default);border-radius:var(--r-sm);
  background:var(--bg-raised)}

/* Platform health lives in the header because it must be visible on EVERY screen.
   An operator should never have to navigate to discover the platform is degraded. */
.health{display:flex;align-items:center;gap:var(--s-3);padding:0 var(--s-3);height:30px;
  flex:none;background:var(--bg-sunken);border:1px solid var(--border-subtle);
  border-radius:var(--r-md)}
.health__item{display:flex;align-items:center;gap:6px;font-size:var(--fs-xs);
  color:var(--text-secondary);white-space:nowrap}
.health__dot{width:7px;height:7px;border-radius:50%;background:var(--sev-ok);flex:none}
.health__dot[data-s="degraded"]{background:var(--sev-medium)}
.health__dot[data-s="down"]{background:var(--sev-critical);
  animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.health__val{font-family:var(--font-mono);color:var(--text-primary);
  font-variant-numeric:tabular-nums}

.iconbtn{width:30px;height:30px;flex:none;display:grid;place-items:center;
  background:transparent;border:1px solid transparent;border-radius:var(--r-md);
  cursor:pointer;color:var(--text-secondary);position:relative;line-height:0}
.iconbtn:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.iconbtn:hover{background:var(--bg-hover);color:var(--text-primary)}
.iconbtn__badge{position:absolute;top:-1px;right:-1px;min-width:16px;height:16px;padding:0 4px;
  border-radius:var(--r-full);background:var(--sev-critical);color:var(--text-inverse);
  font-size:var(--fs-2xs);font-weight:var(--fw-bold);display:grid;place-items:center;
  border:2px solid var(--bg-surface)}

.whoami{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-1) var(--s-2);
  height:36px;flex:none;border-radius:var(--r-md);cursor:pointer;
  background:transparent;border:1px solid transparent;white-space:nowrap}
.whoami:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.whoami:hover{background:var(--bg-hover)}
.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-quiet);
  color:var(--accent);display:grid;place-items:center;font-size:var(--fs-xs);
  font-weight:var(--fw-semibold);flex:none}
.whoami .avatar{width:26px;height:26px}
.whoami__chev{color:var(--text-tertiary);flex:none;display:grid;place-items:center}

/* Elevation pill — the visible consequence of JIT privilege (G.3).
   Nobody HOLDS super-admin; it is borrowed, and the console never lets you
   forget how much time is left on the loan. */
.elev__ico{display:grid;place-items:center;flex:none}
.elev{display:flex;align-items:center;gap:6px;height:28px;padding:0 var(--s-3);
  flex:none;white-space:nowrap;
  border-radius:var(--r-full);font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  background:var(--sev-high-bg);color:var(--sev-high);border:1px solid var(--sev-high)}
.elev[data-state="inactive"]{background:var(--bg-sunken);color:var(--text-tertiary);
  border-color:var(--border-default);font-weight:var(--fw-medium);cursor:pointer}
.elev__time{font-family:var(--font-mono);font-variant-numeric:tabular-nums}

/* ===== Navigation ========================================================= */
/* ===== The rail is dark in both themes ===================================
   Asked for directly, and it is also how most consoles read: the rail is
   chrome, not content. Keeping it dark stops the eye treating navigation as
   part of the page and gives the content area the only bright surface.

   This redefines the palette TOKENS on the rail rather than rewriting its
   rules. Custom properties inherit, so every descendant — nav items, the
   workspace switcher and the menu it opens, counts, the sign-out row — picks
   the dark values up without a single selector being touched, and any rule
   added later inherits them too. The values are copied from the dark theme
   block in tokens.css, so the two cannot drift apart in meaning.

   --bg-root is deliberately absent: it belongs to .main, which stays light. */
.brand, .nav {
  /* §13: deep navy, explicitly NOT pure black. Pure black against the
     #F8FAFC workspace is a harder edge than the design calls for, and on an
     OLED panel it reads as a hole rather than a surface. */
  --bg-sunken:  #0F172A;
  --bg-surface: #0F172A;
  --bg-raised:  #141414;
  --bg-overlay: #1C1C1F;
  --bg-hover:   rgba(255,255,255,.06);
  --bg-active:  rgba(255,255,255,.10);

  --border-subtle: #1C1C1F;
  --border-default:#2A2A2E;
  --border-strong: #46464D;
  --border-focus:  #FFFFFF;

  --text-primary:  #FAFAFA;
  --text-secondary:#A1A1AA;
  --text-tertiary: #909099;
  --text-inverse:  #000000;
  --text-link:     #8FB8FF;

  --accent:       #FAFAFA;
  --accent-hover: #FFFFFF;
  --accent-quiet: #1C1C1F;
  --v9-saffron:   #FF8A4C;

  /* The alert and approval counts live in the rail, so their chips need the
     values that were measured against black, not against white. */
  --sev-critical: #FF6369;  --sev-critical-bg: #1F0D0F;
  --sev-high:     #FF9152;  --sev-high-bg:     #1F1409;
  --sev-medium:   #F5C35C;  --sev-medium-bg:   #1F1A0A;
  --sev-low:      #6FB6FF;  --sev-low-bg:      #0A1626;
  --sev-ok:       #4ADE80;  --sev-ok-bg:       #08180F;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.6);
  --shadow-md: 0 4px 14px rgba(0,0,0,.7);
  --shadow-lg: 0 18px 50px rgba(0,0,0,.8);
}
/* The rail is dark on a light page, so it needs its own edge rather than
   borrowing the page's subtle one. */
:root:not([data-theme="dark"]) .nav,
:root:not([data-theme="dark"]) .brand { border-right-color: #000000 }

.nav{grid-area:nav;background:var(--bg-sunken);border-right:1px solid var(--border-subtle);
  overflow-y:auto;overflow-x:hidden;padding:var(--s-3) var(--s-3) var(--s-2);
  display:flex;flex-direction:column;
  scrollbar-width:none;-ms-overflow-style:none}
.nav::-webkit-scrollbar{display:none}

/* ---- Workspace switcher -------------------------------------------------- */
/* Sits above everything because switching workspace re-scopes every screen
   below it. A control with that much reach does not belong in a settings page. */
.ws{position:relative;flex:none;margin-bottom:var(--s-3)}
.ws__btn{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  width:100%;padding:var(--s-2);border:0;border-radius:var(--r-lg);background:none;
  cursor:pointer;text-align:left;transition:background var(--dur-fast) var(--ease)}
.ws__btn:hover{background:var(--bg-hover)}
.ws__mark{width:32px;height:32px;flex:none;border-radius:var(--r-md);
  display:grid;place-items:center;font-size:var(--fs-md);font-weight:var(--fw-semibold);
  background:linear-gradient(140deg,var(--v9-saffron),var(--v9-gold));color:#0A0A0A;
  box-shadow:var(--shadow-sm)}
/* line-height:1 gives a 15px line box to a glyph box nearer 17px, and both
   rules clip — so a workspace name with a descender (g, j, p, y) lost its
   tail. The gap absorbs the extra height the taller line boxes take. */
.ws__meta{display:flex;flex-direction:column;overflow:hidden;gap:0}
.ws__name{font-size:var(--fs-md);font-weight:var(--fw-medium);line-height:1.25;
  color:var(--text-primary);max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ws__plan{font-size:var(--fs-2xs);line-height:1.4;color:var(--text-tertiary)}
.ws__chev{color:var(--text-tertiary);flex:none;transition:color var(--dur-fast) var(--ease)}
.ws__btn:hover .ws__chev{color:var(--text-secondary)}
.ws__menu{position:absolute;top:52px;left:0;width:100%;z-index:calc(var(--z-nav) + 10);
  background:var(--bg-overlay);border:1px solid var(--border-default);
  border-radius:var(--r-lg);box-shadow:var(--shadow-lg);padding:var(--s-1);
  display:flex;flex-direction:column;gap:2px}
.ws__opt{display:flex;flex-direction:column;gap:2px;padding:var(--s-2) var(--s-3);
  border:0;border-radius:var(--r-md);background:none;cursor:pointer;text-align:left;
  font-size:var(--fs-md);color:var(--text-secondary)}
.ws__opt:hover{background:var(--bg-hover);color:var(--text-primary)}
.ws__opt[aria-selected="true"]{background:var(--accent-quiet);color:var(--text-primary);
  font-weight:var(--fw-medium)}
.ws__opt small{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.ws__sep{height:1px;background:var(--border-subtle);margin:var(--s-1) var(--s-2)}
.ws__scrim{position:fixed;inset:0;z-index:calc(var(--z-nav) + 5)}

.nav__scroll{flex:1;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column;
  gap:var(--s-4);scrollbar-width:none;-ms-overflow-style:none}
.nav__scroll::-webkit-scrollbar{display:none}

.nav__group{display:flex;flex-direction:column;gap:2px}
.nav__grouphead{display:flex;align-items:center;gap:var(--s-2);width:100%;
  padding:var(--s-1) var(--s-3) var(--s-1);background:none;border:0;cursor:pointer;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-tertiary);border-radius:var(--r-md)}
.nav__grouphead:hover{color:var(--text-secondary)}
.nav__chev{margin-left:auto;transition:transform var(--dur-fast) var(--ease);
  color:var(--text-tertiary);display:grid;place-items:center}
.nav__group[data-open="false"] .nav__chev{transform:rotate(-90deg)}
.nav__group[data-open="false"] .nav__items{display:none}
.nav__count{font-family:var(--font-mono);font-size:var(--fs-2xs);
  background:var(--sev-critical);color:var(--text-inverse);border-radius:var(--r-full);
  padding:0 5px;min-width:16px;text-align:center;margin-left:auto}

/* ---- Rows --------------------------------------------------------------- */
.nav__item{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);
  width:100%;padding:7px 10px;background:none;border:0;border-radius:var(--r-md);
  cursor:pointer;font-size:var(--fs-md);color:var(--text-secondary);text-align:left;
  transition:background var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.nav__item:hover{background:var(--bg-hover);color:var(--text-primary)}
.nav__item[aria-current="page"]{background:var(--bg-active);color:var(--text-primary);
  font-weight:var(--fw-medium)}
/* The active row keeps a saffron marker: the brand cue survives the restyle,
   and colour alone is never the only signal — weight and background move too. */
.nav__item[aria-current="page"] .nav__ico{color:var(--v9-saffron)}
.nav__item:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.nav__left{display:flex;align-items:center;gap:10px;min-width:0}
.nav__ico{flex:none;display:grid;place-items:center;color:var(--text-tertiary);
  transition:color var(--dur-fast) var(--ease)}
.nav__item:hover .nav__ico{color:var(--text-secondary)}
.nav__label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:.005em}
.nav__right{display:flex;align-items:center;gap:6px;flex:none}

.nav__pill{font-family:var(--font-mono);font-size:var(--fs-2xs);padding:0 6px;
  min-width:20px;height:18px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-full);background:var(--bg-raised);color:var(--text-tertiary);
  border:1px solid var(--border-subtle)}
.nav__pill[data-tone="critical"]{background:var(--sev-critical-bg);color:var(--sev-critical);
  border-color:transparent}
.nav__pill[data-tone="warn"]{background:var(--sev-medium-bg);color:var(--sev-medium);
  border-color:transparent}
/* Shortcut hints appear on hover only. Shown always they are visual noise on
   every row; shown never, nobody discovers them. */
.nav__kbd{display:none;height:18px;padding:0 5px;align-items:center;
  font-family:var(--font-mono);font-size:var(--fs-2xs);color:var(--text-tertiary);
  background:var(--bg-raised);border:1px solid var(--border-subtle);border-radius:var(--r-sm)}
.nav__item:hover .nav__kbd,.nav__item:focus-visible .nav__kbd{display:inline-flex}
.nav__expand{color:var(--text-tertiary);display:grid;place-items:center;
  transition:transform var(--dur-fast) var(--ease)}
.nav__item[aria-expanded="true"] .nav__expand{transform:rotate(90deg)}

/* ---- Nested children ----------------------------------------------------- */
.nav__sub{display:grid;grid-template-rows:0fr;opacity:0;
  transition:grid-template-rows var(--dur-normal) var(--ease),opacity var(--dur-fast) var(--ease)}
.nav__sub[data-open="true"]{grid-template-rows:1fr;opacity:1}
.nav__subinner{overflow:hidden;min-height:0;position:relative;
  display:flex;flex-direction:column;gap:2px;margin-top:2px;visibility:hidden}
.nav__sub[data-open="true"] .nav__subinner{visibility:visible}
/* The guide line is what makes a nested list read as nested at a glance,
   without indenting so far that labels start truncating in a 260px rail. */
.nav__subinner::before{content:"";position:absolute;left:19px;top:0;bottom:0;width:1px;
  background:var(--border-default)}
.nav__item--child{padding-left:32px}

/* ---- Pinned footer ------------------------------------------------------- */
/* Personal, not platform. Below the divider because these follow the operator
   rather than the workspace selected above. */
.nav__pinned{flex:none;margin-top:var(--s-3);padding-top:var(--s-3);
  border-top:1px solid var(--border-subtle);display:flex;flex-direction:column;gap:2px}
.nav__item--danger:hover{background:var(--sev-critical-bg);color:var(--sev-critical)}
.nav__item--danger:hover .nav__ico{color:var(--sev-critical)}

/* Collapsed rail: icons only, labels return as native tooltips. */
.shell[data-nav="collapsed"] .nav{padding-inline:var(--s-2)}
.shell[data-nav="collapsed"] .nav__label,
.shell[data-nav="collapsed"] .nav__right,
.shell[data-nav="collapsed"] .ws__meta,
.shell[data-nav="collapsed"] .ws__chev,
.shell[data-nav="collapsed"] .nav__grouphead span:not(.nav__count),
.shell[data-nav="collapsed"] .nav__subinner::before{display:none}
.shell[data-nav="collapsed"] .nav__item{justify-content:center;padding-inline:0}
.shell[data-nav="collapsed"] .nav__item--child{padding-left:0}
.shell[data-nav="collapsed"] .nav__grouphead{justify-content:center;padding-inline:0}
.shell[data-nav="collapsed"] .ws__btn{justify-content:center}

/* ===== Main =============================================================== */
/* ===== Route transition ==================================================
   A screen that repaints instantly reads as a redraw, not a navigation — there
   is no moment that says "the thing you asked for is being fetched". These
   three together supply it: the bar says work started, the fade separates the
   old screen from the new one, and a floor on the duration stops a cached
   screen flashing past faster than the eye can register it as a change.

   The floor is a FLOOR, not a delay added to every route: a screen that takes
   longer than it waits no longer. */
/* The gutter lives here, not on a wrapper.
   `.view` carried `padding: 24px 32px 48px; max-width:1680px; margin:0 auto`
   but NOTHING ever had that class — renderView() appends each screen's blocks
   straight into <main>. So the rule matched nothing and every screen's text sat
   flush against the rail on the left and the window edge on the right
   (measured: 0px and 15px). Moving both the padding and the max-width onto the
   element that actually holds the content is what the original rule meant. */
.main{grid-area:main;overflow-y:auto;overflow-x:hidden;background:var(--bg-root);
  scroll-behavior:smooth;
  padding:var(--s-6) var(--s-8) var(--s-12);
  transition:opacity var(--dur-instant) var(--ease), transform var(--dur-instant) var(--ease)}
.main[data-swapping="true"]{opacity:0;transform:translateY(6px);
  /* A second click during the swap would queue a navigation nobody sees. */
  pointer-events:none}

.routebar{position:fixed;top:0;left:0;height:2px;width:0;
  background:var(--v9-saffron);z-index:calc(var(--z-toast) + 10);
  opacity:0;pointer-events:none;
  transition:width var(--dur-normal) var(--ease), opacity var(--dur-fast) var(--ease)}
.routebar[data-on="true"]{opacity:1}

/* Reduced motion: the bar and the floor stay, since they carry information.
   The movement does not. */
@media (prefers-reduced-motion: reduce) {
  .main{transition:opacity var(--dur-instant) var(--ease)}
  .main[data-swapping="true"]{transform:none}
  /* The status bar's "live" pulse is the one infinite animation in the shell;
     for reduced motion it becomes a plain dot. */
  .live__dot{animation:none}
}
/* Was `.view`'s max-width. Each screen's blocks are siblings rather than
   children of one wrapper, so the cap goes on each of them; they are all full
   width, so centring them individually looks identical to centring a wrapper.
   Inert below ~1744px. */
.main > *{max-width:1680px;margin-inline:auto}
.view__head{display:flex;align-items:flex-start;gap:var(--s-4);margin-bottom:var(--s-6)}
.view__title{font-size:var(--fs-2xl);letter-spacing:-.02em}
.view__sub{margin-top:var(--s-1);font-size:var(--fs-sm);color:var(--text-secondary);
  max-width:72ch;line-height:var(--lh-relaxed)}
.view__actions{margin-left:auto;display:flex;gap:var(--s-2);flex:none}

.section{margin-bottom:var(--s-8)}
.section__head{display:flex;align-items:center;gap:var(--s-3);margin-bottom:var(--s-3)}
.section__title{font-size:var(--fs-lg)}
.section__hint{font-size:var(--fs-xs);color:var(--text-tertiary)}
.section__actions{margin-left:auto;display:flex;gap:var(--s-2)}

/* ===== Buttons ============================================================ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:40px;
  padding:0 var(--s-4);border-radius:var(--r-md);border:1px solid var(--border-default);
  background:var(--bg-raised);color:var(--text-primary);font-size:var(--fs-sm);
  font-weight:var(--fw-semibold);cursor:pointer;white-space:nowrap;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
.btn:hover{background:var(--bg-overlay);border-color:var(--border-strong)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn--primary{background:var(--accent);border-color:var(--accent);color:var(--text-inverse)}
.btn--primary:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
.btn--ghost{background:transparent;border-color:transparent;color:var(--text-secondary)}
.btn--ghost:hover{background:var(--bg-hover);color:var(--text-primary)}
/* Destructive actions are never the primary style. They must look like what
   they are, and in this console they additionally require typed confirmation. */
.btn--danger{background:var(--sev-critical-bg);border-color:var(--sev-critical);
  color:var(--sev-critical)}
.btn--danger:hover{background:var(--sev-critical);color:var(--text-inverse)}
.btn--sm{height:32px;padding:0 var(--s-3);font-size:var(--fs-xs)}
.btn--icon{width:30px;padding:0}

/* ===== Cards & metrics ==================================================== */
.grid{display:grid;gap:var(--s-4)}
.grid--4{grid-template-columns:repeat(4,1fr)}
.grid--3{grid-template-columns:repeat(3,1fr)}
.grid--2{grid-template-columns:repeat(2,1fr)}
.grid--sidebar{grid-template-columns:minmax(0,1fr) 380px}

.card{background:var(--bg-surface);border:1px solid var(--border-subtle);
  border-radius:var(--r-lg);overflow:hidden}
.card__head{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-3) var(--s-4);
  border-bottom:1px solid var(--border-subtle)}
.card__title{font-size:var(--fs-md);font-weight:var(--fw-semibold)}
.card__actions{margin-left:auto;display:flex;gap:var(--s-1)}
.card__body{padding:var(--s-4)}
.card__body--flush{padding:0}

.metric{background:var(--bg-surface);border:1px solid var(--border-subtle);
  border-radius:var(--r-lg);padding:var(--s-4);display:flex;flex-direction:column;gap:var(--s-2);
  cursor:pointer;transition:border-color var(--dur-fast) var(--ease)}
.metric:hover{border-color:var(--border-strong)}
.metric__label{font-size:var(--fs-xs);color:var(--text-secondary);
  letter-spacing:0;font-weight:var(--fw-semibold)}
.metric__row{display:flex;align-items:baseline;gap:var(--s-2)}
.metric__value{font-size:var(--fs-3xl);font-weight:var(--fw-semibold);letter-spacing:-.025em;
  font-family:var(--font-mono);font-variant-numeric:tabular-nums;line-height:1}
.metric__unit{font-size:var(--fs-sm);color:var(--text-tertiary)}
.metric__delta{font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  font-variant-numeric:tabular-nums;display:flex;align-items:center;gap:3px}
.metric__delta[data-dir="up"]{color:var(--sev-ok)}
.metric__delta[data-dir="down"]{color:var(--sev-critical)}
.metric__delta[data-dir="flat"]{color:var(--text-tertiary)}
/* Every metric states its window. A number without a window is not information. */
.metric__window{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.metric__spark{height:28px;margin-top:auto}

/* ===== Severity ========================================================== */
.sev{display:inline-flex;align-items:center;gap:6px;padding:4px var(--s-3);
  border-radius:var(--r-full);font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  /* Sentence case, not uppercase. Uppercase strips the word-shape a reader
     recognises before they finish reading, and pairing it with letterspacing at
     a small size is the least legible combination available. */
  letter-spacing:0;border:1px solid transparent;white-space:nowrap;line-height:1.4}
.sev[data-sev="critical"]{background:var(--sev-critical-bg);color:var(--sev-critical);border-color:var(--sev-critical)}
.sev[data-sev="high"]    {background:var(--sev-high-bg);    color:var(--sev-high);    border-color:var(--sev-high)}
.sev[data-sev="medium"]  {background:var(--sev-medium-bg);  color:var(--sev-medium);  border-color:var(--sev-medium)}
.sev[data-sev="low"]     {background:var(--sev-low-bg);     color:var(--sev-low);     border-color:var(--sev-low)}
.sev[data-sev="ok"]      {background:var(--sev-ok-bg);      color:var(--sev-ok);      border-color:var(--sev-ok)}
.sev[data-sev="info"]    {background:var(--bg-raised);      color:var(--text-secondary);border-color:var(--border-default)}

.tag{display:inline-flex;align-items:center;gap:6px;padding:4px var(--s-3);
  border-radius:var(--r-full);font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  background:var(--bg-raised);color:var(--text-primary);
  border:1px solid var(--border-default);white-space:nowrap;line-height:1.4}
/* Status pills carry their severity as a tinted ground, as the reference does,
   rather than as grey-on-grey. Colour is never the only signal — the word is
   always present — but it is what makes the column scannable. */
.tag[data-sev="critical"]{background:var(--sev-critical-bg);color:var(--sev-critical);border-color:var(--sev-critical)}
.tag[data-sev="high"]    {background:var(--sev-high-bg);    color:var(--sev-high);    border-color:var(--sev-high)}
.tag[data-sev="medium"]  {background:var(--sev-medium-bg);  color:var(--sev-medium);  border-color:var(--sev-medium)}
.tag[data-sev="low"]     {background:var(--sev-low-bg);     color:var(--sev-low);     border-color:var(--sev-low)}
.tag[data-sev="ok"]      {background:var(--sev-ok-bg);      color:var(--sev-ok);      border-color:var(--sev-ok)}
.tag[data-sev="info"]    {background:var(--bg-sunken);      color:var(--text-secondary);border-color:var(--border-default)}

/* ===== Alerts ============================================================ */
.alert{display:flex;gap:var(--s-3);padding:var(--s-3) var(--s-4);
  border-left:3px solid var(--border-default);background:var(--bg-surface);
  border-bottom:1px solid var(--border-subtle);cursor:pointer;align-items:flex-start}
.alert:hover{background:var(--bg-hover)}
.alert:last-child{border-bottom:0}
.alert[data-sev="critical"]{border-left-color:var(--sev-critical);background:var(--sev-critical-bg)}
.alert[data-sev="high"]{border-left-color:var(--sev-high)}
.alert[data-sev="medium"]{border-left-color:var(--sev-medium)}
.alert[data-sev="low"]{border-left-color:var(--sev-low)}
.alert__ico{font-size:14px;line-height:1.3;flex:none}
.alert__body{min-width:0;flex:1}
.alert__title{font-size:var(--fs-md);font-weight:var(--fw-medium);margin-bottom:2px}
.alert__meta{font-size:var(--fs-xs);color:var(--text-tertiary);
  display:flex;gap:var(--s-2);flex-wrap:wrap}
.alert__act{flex:none;display:flex;gap:var(--s-1);align-items:center}

/* ===== Tables ============================================================ */
.tablewrap{border:1px solid var(--border-subtle);border-radius:var(--r-lg);
  background:var(--bg-surface);overflow:hidden}
.toolbar{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-3);
  border-bottom:1px solid var(--border-subtle);flex-wrap:wrap}
.toolbar__spacer{flex:1}
.field{display:flex;align-items:center;gap:var(--s-2);height:38px;padding:0 var(--s-3);
  background:var(--bg-sunken);border:1px solid var(--border-default);
  border-radius:var(--r-md)}
.field:focus-within{border-color:var(--border-focus)}
.field input,.field select{background:none;border:0;outline:none;font-size:var(--fs-sm);
  min-width:0;width:100%}
.field select{cursor:pointer}
.field--search{min-width:220px}

.savedviews{display:flex;gap:var(--s-1);align-items:center}
.savedview{height:32px;padding:0 var(--s-3);border-radius:var(--r-full);
  font-size:var(--fs-xs);font-weight:var(--fw-medium);border:1px solid var(--border-default);
  background:var(--bg-sunken);color:var(--text-secondary);cursor:pointer}
.savedview[aria-pressed="true"]{background:var(--accent-quiet);color:var(--accent);
  border-color:var(--accent)}

table{width:100%;border-collapse:collapse;font-size:var(--fs-md)}
thead th{position:sticky;top:0;z-index:1;background:var(--bg-sunken);
  padding:var(--s-3);text-align:left;font-size:var(--fs-sm);
  font-weight:var(--fw-bold);color:var(--text-primary);
  letter-spacing:0;border-bottom:1px solid var(--border-default);white-space:nowrap}
thead th[data-sortable]{cursor:pointer;user-select:none}
thead th[data-sortable]:hover{color:var(--text-primary)}
thead th[aria-sort] .sortmark{opacity:1}
.sortmark{opacity:.35;margin-left:5px;font-size:var(--fs-2xs)}
tbody td{padding:var(--cell-py) var(--s-3);border-bottom:1px solid var(--border-subtle);
  height:var(--row-h);vertical-align:middle}
tbody tr{cursor:pointer}
tbody tr:hover{background:var(--bg-hover)}
tbody tr[aria-selected="true"]{background:var(--accent-quiet)}
tbody tr:last-child td{border-bottom:0}
.num{font-family:var(--font-mono);font-variant-numeric:tabular-nums;text-align:right}
.mono{font-family:var(--font-mono);font-size:var(--fs-sm)}
/* `max-width:0` was the table-layout:fixed trick, which belongs on a <td> and
   not on a span inside one. It looked harmless for a long time because
   max-width does not apply to non-replaced INLINE elements — so on the spans
   this class is usually put on, the declaration did nothing at all.

   It stops being inert the moment the span is a FLEX ITEM, because a flex item
   is blockified and the 0 then applies: the text collapses to zero width and
   disappears. That is what happened to the task title inside .cellstack and the
   assignee name inside .who.

   min-width:0 is the half that makes ellipsis work in a flex row at all (a flex
   item's default min-width:auto refuses to shrink below its content). Both
   declarations stay inert in the inline case, so no existing screen changes. */
.truncate{min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cellstack{display:flex;flex-direction:column;gap:1px;min-width:0}
.cellstack small{color:var(--text-tertiary);font-size:var(--fs-xs)}
.who{display:flex;align-items:center;gap:var(--s-2);min-width:0}

.bulkbar{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-3);
  background:var(--accent-quiet);border-bottom:1px solid var(--accent)}
.bulkbar__count{font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--accent)}

.tablefoot{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-3);
  border-top:1px solid var(--border-subtle);font-size:var(--fs-xs);color:var(--text-tertiary);
  flex-wrap:wrap}

/* ---- Pager -------------------------------------------------------------- */
.pager{display:flex;align-items:center;gap:2px}
.pager__btn{min-width:26px;height:26px;padding:0 6px;border-radius:var(--r-sm,4px);
  border:1px solid transparent;background:transparent;color:var(--text-secondary);
  font-size:var(--fs-xs);font-variant-numeric:tabular-nums;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center}
.pager__btn:hover:not(:disabled){background:var(--bg-raised);color:var(--text-primary)}
.pager__btn:disabled{opacity:.35;cursor:default}
.pager__btn.is-current{background:var(--accent);color:var(--text-inverse);
  border-color:var(--accent);font-weight:600}
.pager__btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.pager__gap{padding:0 2px;color:var(--text-tertiary)}
.pager__size{display:inline-flex;align-items:center}
.input--sm{height:26px;padding:0 var(--s-2);font-size:var(--fs-xs)}

.empty{padding:var(--s-12) var(--s-4);text-align:center;color:var(--text-tertiary)}
.empty__ico{font-size:28px;opacity:.4;margin-bottom:var(--s-3)}
.empty__title{font-size:var(--fs-lg);color:var(--text-secondary);margin-bottom:var(--s-2)}

/* ===== Drawer ============================================================ */
.scrim{position:fixed;inset:0;background:var(--bg-scrim);z-index:var(--z-drawer);
  opacity:0;pointer-events:none;transition:opacity var(--dur-normal) var(--ease)}
.scrim[data-open="true"]{opacity:1;pointer-events:auto}
.drawer{position:fixed;top:0;right:0;bottom:0;width:min(var(--drawer-w),92vw);
  background:var(--bg-surface);border-left:1px solid var(--border-default);
  z-index:calc(var(--z-drawer) + 1);display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform var(--dur-normal) var(--ease);
  box-shadow:var(--shadow-lg)}
.drawer[data-open="true"]{transform:none}
.drawer__head{display:flex;align-items:flex-start;gap:var(--s-3);padding:var(--s-4);
  border-bottom:1px solid var(--border-subtle)}
.drawer__title{font-size:var(--fs-xl);letter-spacing:-.015em}
.drawer__sub{font-size:var(--fs-xs);color:var(--text-tertiary);margin-top:2px}
.drawer__body{flex:1;overflow-y:auto;padding:var(--s-4)}
.drawer__foot{display:flex;gap:var(--s-2);padding:var(--s-3) var(--s-4);
  border-top:1px solid var(--border-subtle);background:var(--bg-sunken)}

.tabs{display:flex;gap:var(--s-1);border-bottom:1px solid var(--border-subtle);
  margin-bottom:var(--s-4)}
.tab{padding:var(--s-2) var(--s-3);background:none;border:0;border-bottom:2px solid transparent;
  color:var(--text-secondary);font-size:var(--fs-sm);cursor:pointer;margin-bottom:-1px}
.tab:hover{color:var(--text-primary)}
.tab[aria-selected="true"]{color:var(--text-primary);border-bottom-color:var(--v9-saffron);
  font-weight:var(--fw-medium)}

.kv{display:grid;grid-template-columns:150px 1fr;gap:var(--s-2) var(--s-3);
  font-size:var(--fs-sm)}
.kv dt{color:var(--text-tertiary)}
.kv dd{margin:0;color:var(--text-primary);min-width:0;word-break:break-word}

/* ===== Timeline (investigation / audit) ================================== */
.timeline{position:relative;padding-left:var(--s-6)}
.timeline::before{content:"";position:absolute;left:7px;top:4px;bottom:4px;width:1px;
  background:var(--border-default)}
.tl{position:relative;padding-bottom:var(--s-4)}
.tl::before{content:"";position:absolute;left:calc(var(--s-6) * -1 + 4px);top:5px;
  width:7px;height:7px;border-radius:50%;background:var(--text-tertiary);
  box-shadow:0 0 0 3px var(--bg-surface)}
.tl[data-sev="critical"]::before{background:var(--sev-critical)}
.tl[data-sev="high"]::before{background:var(--sev-high)}
.tl[data-sev="ok"]::before{background:var(--sev-ok)}
.tl__time{font-family:var(--font-mono);font-size:var(--fs-2xs);color:var(--text-tertiary)}
.tl__title{font-size:var(--fs-sm);font-weight:var(--fw-medium);margin:1px 0 2px}
.tl__body{font-size:var(--fs-xs);color:var(--text-secondary);line-height:var(--lh-relaxed)}

/* ===== Command palette =================================================== */
.palette{position:fixed;inset:0;z-index:var(--z-palette);display:none;
  padding-top:12vh;justify-content:center;background:var(--bg-scrim)}
.palette[data-open="true"]{display:flex}

.palette__box{width:min(640px,92vw);max-height:60vh;background:var(--bg-overlay);
  border:1px solid var(--border-strong);border-radius:var(--r-xl);box-shadow:var(--shadow-lg);
  display:flex;flex-direction:column;overflow:hidden}
.palette__input{width:100%;padding:var(--s-4);background:none;border:0;outline:none;
  font-size:var(--fs-lg);border-bottom:1px solid var(--border-subtle)}
.palette__list{overflow-y:auto;padding:var(--s-2)}
.palette__group{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.08em;
  color:var(--text-tertiary);padding:var(--s-2) var(--s-3) var(--s-1);font-weight:var(--fw-semibold)}
.palette__item{display:flex;align-items:center;gap:var(--s-3);width:100%;
  padding:var(--s-2) var(--s-3);background:none;border:0;border-radius:var(--r-md);
  cursor:pointer;text-align:left;font-size:var(--fs-md);color:var(--text-primary)}
.palette__item[data-active="true"],.palette__item:hover{background:var(--accent-quiet)}
.palette__hint{margin-left:auto;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-family:var(--font-mono)}
.palette__foot{display:flex;gap:var(--s-4);padding:var(--s-2) var(--s-4);
  border-top:1px solid var(--border-subtle);font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* ===== Modal (confirmation) ============================================== */
.modal{position:fixed;inset:0;z-index:var(--z-modal);display:none;
  place-items:center;background:var(--bg-scrim);padding:var(--s-4)}
.modal[data-open="true"]{display:grid}

/* ===== Frosted backdrops, shared by every dismissing overlay =============
   The screen behind stays readable — you can see what the drawer is about to
   change, or which screen you are searching — while the blur stops it
   competing for attention. The scrim lightens to --bg-scrim-soft because a
   blur you cannot see through is just a dark panel; in dark theme the stock
   scrim is 80% black, which leaves nothing to blur.

   Gated on @supports: without blur the scrim must stay at full strength,
   since here the blur is doing part of the separating work. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .palette,.scrim,.modal{background:var(--bg-scrim-soft);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
}
/* See-through layers cost some people legibility, and some report nausea. */
@media (prefers-reduced-transparency: reduce) {
  .palette,.scrim,.modal{background:var(--bg-scrim);
    -webkit-backdrop-filter:none;backdrop-filter:none}
}
.modal__box{width:min(520px,100%);background:var(--bg-overlay);border-radius:var(--r-xl);
  border:1px solid var(--border-strong);box-shadow:var(--shadow-lg);overflow:hidden}
.modal__head{padding:var(--s-4);border-bottom:1px solid var(--border-subtle)}
.modal__body{padding:var(--s-4);font-size:var(--fs-sm);color:var(--text-secondary);
  line-height:var(--lh-relaxed)}
.modal__foot{display:flex;gap:var(--s-2);justify-content:flex-end;padding:var(--s-3) var(--s-4);
  background:var(--bg-sunken);border-top:1px solid var(--border-subtle)}
.input{width:100%;height:32px;padding:0 var(--s-3);background:var(--bg-sunken);
  border:1px solid var(--border-default);border-radius:var(--r-md);outline:none;
  font-size:var(--fs-sm)}
.input:focus{border-color:var(--border-focus)}
textarea.input{height:auto;min-height:72px;padding:var(--s-2) var(--s-3);resize:vertical;
  line-height:var(--lh-relaxed);font-family:inherit}
/* ---- Boxes that size themselves to what they hold ------------------------
   Every free-text field on the department form is one of these (growArea in
   views.department.js). A value is never hidden behind a caret or a scrollbar:
   the box is as tall as the value needs, however long the name.

   min-height is reset because textarea.input sets 72px for real multi-line
   boxes, and a one-row field could never be measured below it. resize is off
   because the box already fits its value, and one the reader had dragged
   smaller would start hiding text again — the exact thing these replace.
   overflow is hidden because there is never anything to scroll to.

   The line-height and vertical padding are PIXELS rather than tokens so that a
   one-row box is exactly as tall as the input it replaces: 20 + 5 + 5 + 2 = 32,
   and 18 + 3 + 3 + 2 = 26 for the small one. Each further line adds exactly one
   line-height, so a two-line value is 52px and looks like a field, not a gap.

   Written as textarea.input.grows, not textarea.grows: this sheet declares
   `.input` and `textarea.input` TWICE — here, and again around line 3019 for
   the form screens — and the later pair silently wins on equal specificity.
   Every growing box carries the `input` class, so one more class in the
   selector settles it wherever the rule happens to sit. */
textarea.input.grows{height:auto;min-height:0;resize:none;overflow:hidden;
  line-height:20px;padding-block:5px;overflow-wrap:anywhere}
textarea.input.grows.input--sm{line-height:18px;padding-block:3px}
/* textarea.input wins over .mono on specificity, so the code boxes would have
   quietly stopped being monospaced the moment they became textareas. */
textarea.input.grows.mono{font-family:var(--font-mono)}
/* Names and labels in lists wrap instead of ellipsing — same rule, applied to
   the places a value is READ rather than typed. */
.dep__wrap{min-width:0;overflow-wrap:anywhere}
.label{display:block;font-size:var(--fs-xs);color:var(--text-secondary);
  margin-bottom:var(--s-1);font-weight:var(--fw-medium)}
.formrow{margin-bottom:var(--s-3)}

/* ===== Toasts ============================================================ */
.toasts{position:fixed;right:var(--s-4);bottom:calc(var(--statusbar-h) + var(--s-4));
  z-index:var(--z-toast);display:flex;flex-direction:column;gap:var(--s-2);
  width:min(380px,90vw)}
.toast{display:flex;gap:var(--s-3);padding:var(--s-3);background:var(--bg-overlay);
  border:1px solid var(--border-strong);border-left-width:3px;border-radius:var(--r-md);
  box-shadow:var(--shadow-md);animation:slidein var(--dur-normal) var(--ease)}
.toast[data-sev="ok"]{border-left-color:var(--sev-ok)}
.toast[data-sev="critical"]{border-left-color:var(--sev-critical)}
.toast[data-sev="info"]{border-left-color:var(--accent)}
.toast__body{flex:1;min-width:0}
.toast__title{font-size:var(--fs-sm);font-weight:var(--fw-medium)}
.toast__msg{font-size:var(--fs-xs);color:var(--text-secondary);margin-top:2px}
@keyframes slidein{from{transform:translateX(16px);opacity:0}to{transform:none;opacity:1}}

/* ===== Status bar ======================================================== */
/* One line, always. The bar is 28px tall, so anything that wraps is clipped
   rather than shown — nowrap plus progressive hiding keeps it honest. */
.statusbar{grid-area:status;display:flex;align-items:center;gap:var(--s-4);
  padding:0 var(--s-4);background:var(--bg-sunken);
  border-top:1px solid var(--border-subtle);font-size:var(--fs-2xs);
  color:var(--text-tertiary);white-space:nowrap;overflow:hidden;
  container-type:inline-size;container-name:sbar}
.statusbar > *{flex:none}
.statusbar__spacer{flex:1}
.statusbar b{color:var(--text-secondary);font-weight:var(--fw-medium)}
.live{display:flex;align-items:center;gap:5px}
.live__dot{width:6px;height:6px;border-radius:50%;background:var(--sev-ok);
  animation:pulse 2.4s ease-in-out infinite}

/* ===== Charts (hand-rolled SVG, zero dependencies) ======================= */
.chart{width:100%;display:block;overflow:visible}
.chart__grid{stroke:var(--border-subtle);stroke-width:1}
/* Chart axis labels were 9px — the single smallest text in the console, on the
   element people squint at hardest. */
.chart__axis{fill:var(--text-secondary);font-size:12px;font-weight:600;font-family:var(--font-mono)}
.chart__line{fill:none;stroke:var(--accent);stroke-width:1.75;
  stroke-linejoin:round;stroke-linecap:round}
.chart__area{fill:var(--accent);opacity:.09}
.chart__bar{fill:var(--accent)}
.chart__bar:hover{fill:var(--accent-hover)}

/* ---- Donut -------------------------------------------------------------- */
.donut{position:relative;flex:0 0 auto}
.donut__svg{width:100%;height:100%;display:block}
.donut__track{stroke:var(--bg-raised)}
.donut__seg{stroke:var(--accent);transition:opacity var(--dur-fast,120ms) ease}
.donut:hover .donut__seg{opacity:.45}
.donut .donut__seg:hover{opacity:1}
/* Severity-coloured segments, for dimensions that genuinely carry severity. */
.donut__seg[data-sev="critical"]{stroke:var(--sev-critical)}
.donut__seg[data-sev="high"]    {stroke:var(--sev-high)}
.donut__seg[data-sev="medium"]  {stroke:var(--sev-medium)}
.donut__seg[data-sev="low"]     {stroke:var(--sev-low)}
.donut__seg[data-sev="ok"]      {stroke:var(--sev-ok)}
.donut__seg[data-sev="info"]    {stroke:var(--text-tertiary)}
/* Categorical segments, for dimensions that do not (department, type). */
.donut__seg[data-cat="0"]{stroke:var(--cat-1)} .donut__seg[data-cat="1"]{stroke:var(--cat-2)}
.donut__seg[data-cat="2"]{stroke:var(--cat-3)} .donut__seg[data-cat="3"]{stroke:var(--cat-4)}
.donut__seg[data-cat="4"]{stroke:var(--cat-5)} .donut__seg[data-cat="5"]{stroke:var(--cat-6)}
.donut__seg[data-cat="6"]{stroke:var(--cat-7)} .donut__seg[data-cat="7"]{stroke:var(--cat-8)}

.donut__mid{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:1px;pointer-events:none;text-align:center}
.donut__mid strong{font-size:var(--fs-xl);font-weight:700;line-height:1;color:var(--text-primary)}
.donut__mid small{font-size:var(--fs-xs);color:var(--text-tertiary)}

/* ---- Series legend (vertical; the flex `.legend` above is a different thing) */
.serieslegend{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  gap:6px;font-size:var(--fs-xs);min-width:0}
.serieslegend__row{display:flex;align-items:center;gap:8px;min-width:0}
.serieslegend__dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;
  background:var(--text-tertiary)}
.serieslegend__dot[data-sev="critical"]{background:var(--sev-critical)}
.serieslegend__dot[data-sev="high"]    {background:var(--sev-high)}
.serieslegend__dot[data-sev="medium"]  {background:var(--sev-medium)}
.serieslegend__dot[data-sev="low"]     {background:var(--sev-low)}
.serieslegend__dot[data-sev="ok"]      {background:var(--sev-ok)}
.serieslegend__dot[data-cat="0"]{background:var(--cat-1)} .serieslegend__dot[data-cat="1"]{background:var(--cat-2)}
.serieslegend__dot[data-cat="2"]{background:var(--cat-3)} .serieslegend__dot[data-cat="3"]{background:var(--cat-4)}
.serieslegend__dot[data-cat="4"]{background:var(--cat-5)} .serieslegend__dot[data-cat="5"]{background:var(--cat-6)}
.serieslegend__dot[data-cat="6"]{background:var(--cat-7)} .serieslegend__dot[data-cat="7"]{background:var(--cat-8)}
.serieslegend__label{color:var(--text-secondary);flex:1 1 auto;min-width:0}
.serieslegend__val{color:var(--text-primary);font-weight:600;font-variant-numeric:tabular-nums}
.serieslegend__pct{color:var(--text-tertiary);font-variant-numeric:tabular-nums;min-width:44px;text-align:right}

/* A donut beside its legend. Wraps to stacked on a narrow panel rather than
   shrinking the donut below the size at which its slices can be told apart. */
.chartrow{display:flex;align-items:center;gap:var(--s-5);flex-wrap:wrap}
.chartrow > .serieslegend{flex:1 1 180px}

.heat{display:grid;grid-template-columns:repeat(24,1fr);gap:2px}
.heat__cell{aspect-ratio:1;border-radius:2px;background:var(--bg-raised)}
.legend{display:flex;gap:var(--s-3);flex-wrap:wrap;font-size:var(--fs-xs);
  color:var(--text-secondary);margin-top:var(--s-2)}
.legend__item{display:flex;align-items:center;gap:5px}
.legend__sw{width:9px;height:9px;border-radius:2px}

.bars{display:flex;flex-direction:column;gap:var(--s-2)}
.bar{display:grid;grid-template-columns:130px 1fr 54px;gap:var(--s-3);align-items:center;
  font-size:var(--fs-sm)}
/* display:block is load-bearing, not tidiness. Both of these are <span>s, and
   width/height do not apply to a non-replaced INLINE element — so the track had
   no height and the fill had neither height nor width, and the bar rendered as
   nothing at all wherever its parent did not happen to blockify it.
   The track also drew --bg-raised, which is #FFFFFF in the light theme: an
   invisible track on a white card even once it had a height. */
.bar__track{display:block;height:8px;width:100%;background:var(--bg-sunken);
  border:1px solid var(--border-subtle);border-radius:var(--r-full);overflow:hidden}
.bar__fill{display:block;height:100%;background:var(--accent);border-radius:var(--r-full);
  transition:width var(--dur-normal) var(--ease)}
.bar__val{text-align:right;font-family:var(--font-mono);font-size:var(--fs-xs);
  color:var(--text-secondary);font-variant-numeric:tabular-nums}

/* ===== Responsive ======================================================== */
/* The header carries a lot: breadcrumb, search, four health SLIs, the elevation
   clock, three icon buttons and the account block. Between 860px and 1240px it
   does not fit, so it sheds the least urgent pieces first rather than
   overflowing. Platform health is the last thing to go above 860px, because it
   is the one item an operator must see on every screen. */
/* Shedding order, driven by the header's OWN width rather than the viewport's.
   That distinction matters: collapsing the rail hands the header 204 extra
   pixels, and a viewport-keyed rule cannot see that, so items stayed hidden on
   a bar that had plenty of room. Widths below come from measuring each part —
   everything visible needs about 1200px of bar.

   Order of sacrifice: individual health SLIs, then health entirely, then the
   account chevron and the elevation label, then the breadcrumb ancestor. The
   page name, the avatar and the elevation clock never go. */
@container hdr (max-width: 1200px) { .health__item:nth-child(n+4){display:none} }
@container hdr (max-width: 1130px) { .health__item:nth-child(n+3){display:none} }
@container hdr (max-width: 1060px) { .whoami{height:30px} }
@container hdr (max-width: 1000px) { .health{display:none} }
@container hdr (max-width: 870px)  {
  .whoami__chev{display:none}
  .elev__label{display:none}
  .elev{padding-inline:var(--s-2)}
}
/* 820, not 700: the breadcrumb was being cut mid-word ("Command Center › Task:")
   long before this fired. Dropping the ancestor leaves the page name whole,
   which is the part a reader needs. */
@container hdr (max-width: 820px)  {
  .header__crumbs .crumb-group,.header__crumbs .crumb-sep{display:none}
  .searchbtn{flex:0 1 150px}
}
/* Below this there is no room for a search FIELD, so it becomes a search
   BUTTON rather than an empty box the width of a field. flex must be reset
   too: a flex-basis of 260px beats any width you set on a flex item. */
@container hdr (max-width: 520px) {
  .searchbtn{flex:none;width:30px;min-width:0;padding:0;justify-content:center}
  .searchbtn > span,.searchbtn kbd{display:none}
}
@container hdr (max-width: 400px) {
  .header__crumbs{display:none}
}

@container sbar (max-width: 900px) { .statusbar__region,.statusbar__audit{display:none} }
@container sbar (max-width: 620px) { .statusbar__hint{display:none} }

@media (max-width:1180px){
  .grid--4{grid-template-columns:repeat(2,1fr)}
  .grid--sidebar{grid-template-columns:1fr}
  .view{padding:var(--s-5)}
}
@media (max-width:860px){
  /* A page action that runs off the right edge is worse than one on its own
     line, so the head stacks rather than overflowing. */
  .view__head{flex-wrap:wrap}
  .view__actions{margin-left:0;width:100%}
  .view__actions .btn{flex:1}
  /* The brand column used to collapse to 0 while still holding the navigation
     toggle AND the wordmark, so both spilled out of their area and landed on
     top of "Command Center". Give the toggle a real column of its own instead:
     the grid then places the header after it and nothing overlaps. The
     wordmark is dropped at this width — it is in the navigation drawer, and
     the screen is needed for the work. */
  .shell{grid-template-columns:52px 1fr}
  .shell[data-nav="open"]{grid-template-columns:var(--nav-w) 1fr}
  /* Scoped to the CLOSED drawer only. The previous version hid these children
     and tried to restore them with `display:revert` when the drawer opened —
     but revert rolls back to the BROWSER's default, which is `block`, not to
     .brand__mark's `display:flex;flex-direction:column`. The wordmark lost its
     column layout and the centred, clipped brand cut the toggle in half. A
     state that is never overridden needs no undoing. */
  .shell:not([data-nav="open"]) .brand{padding-inline:0;justify-content:center;overflow:hidden}
  .shell:not([data-nav="open"]) .brand > *:not(#navtoggle){display:none}
  /* position:fixed is measured from the VIEWPORT, but the shell starts below
     the DEVELOPMENT banner — so a bare top:var(--header-h) put the drawer 31px
     too high, over the brand row: the wordmark was half covered by the
     workspace switcher and the toggle was sliced in half. Same omission as the
     shell height; both now read --devbanner-h (0 in production). */
  .nav{position:fixed;top:calc(var(--devbanner-h, 0px) + var(--header-h));bottom:0;left:0;width:var(--nav-w);
    z-index:var(--z-nav);transform:translateX(-100%);
    transition:transform var(--dur-normal) var(--ease)}
  /* The drawer is an overlay at this width, and the AI dock is fixed above the
     page (z-drawer - 20). Without this the dock sat on top of the drawer and
     covered its last items — Account Center and Sign out were unreachable
     while the navigation was open. An open drawer outranks the dock. */
  .shell[data-nav="open"] .nav{transform:none;z-index:calc(var(--z-drawer) - 10)}
  .main{padding:var(--s-4) var(--s-4) var(--s-10)}
  .grid--4,.grid--3,.grid--2{grid-template-columns:1fr}
  .health{display:none}
  /* Tables get their own scroller rather than breaking the page layout. */
  .tablewrap{overflow-x:auto}
  table{min-width:720px}
  .view{padding:var(--s-4)}
}

/* ===== Module screens ==================================================== */

/* Org tree. Indentation carries the reporting line; a flat table of departments
   loses the one thing a department list is for. */
.tree{display:flex;flex-direction:column}
.tree__row{display:grid;grid-template-columns:1fr 150px 90px 110px;gap:var(--s-3);
  align-items:center;padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--border-subtle);
  font-size:var(--fs-md)}
.tree__row:last-child{border-bottom:0}
.tree__row:hover{background:var(--bg-hover)}
.tree__name{display:flex;align-items:center;gap:var(--s-2);min-width:0}
.tree__rail{color:var(--text-tertiary);font-family:var(--font-mono);flex:none}

/* Workflow step chain. Approval gates are drawn differently from ordinary steps
   because a gate is where a human is required, and that is the only thing an
   operator is scanning this list for. */
.chain{display:flex;flex-wrap:wrap;gap:var(--s-2);align-items:center}
.step{display:inline-flex;align-items:center;gap:6px;padding:4px var(--s-3);
  border-radius:var(--r-full);border:1px solid var(--border-default);
  background:var(--bg-raised);font-size:var(--fs-xs);white-space:nowrap}
.step[data-gate="true"]{border-color:var(--sev-medium);background:var(--sev-medium-bg);
  color:var(--sev-medium);font-weight:var(--fw-semibold)}
.chain__arrow{color:var(--text-secondary);font-size:var(--fs-2xs)}

/* Risk matrix. Position on the grid is the finding; the colour only repeats it. */
.matrix{display:grid;grid-template-columns:70px repeat(3,1fr);gap:2px;font-size:var(--fs-xs)}
.matrix__cell{min-height:64px;padding:var(--s-2);border:1px solid var(--border-subtle);
  border-radius:var(--r-sm);display:flex;flex-direction:column;gap:3px}
.matrix__axis{display:grid;place-items:center;color:var(--text-tertiary);
  text-transform:uppercase;letter-spacing:.06em;font-size:var(--fs-2xs);min-height:24px}
.matrix__cell[data-band="low"]{background:var(--sev-ok-bg)}
.matrix__cell[data-band="med"]{background:var(--sev-medium-bg)}
.matrix__cell[data-band="high"]{background:var(--sev-critical-bg)}
.matrix__chip{align-self:flex-start;background:var(--bg-surface);
  border:1px solid var(--border-default);border-radius:var(--r-sm);padding:2px 6px;
  font-family:var(--font-mono);font-size:var(--fs-2xs);cursor:pointer}
.matrix__chip:hover{border-color:var(--border-focus)}

/* Settings list grouped by domain; locked rows state why they cannot change. */
.setting{display:grid;grid-template-columns:260px 200px 1fr;gap:var(--s-3);
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--border-subtle);
  align-items:start;font-size:var(--fs-sm)}
.setting:last-child{border-bottom:0}
.setting__key{font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--text-primary)}
.setting__val{font-weight:var(--fw-medium)}
.setting__meta{color:var(--text-tertiary);font-size:var(--fs-xs);line-height:var(--lh-relaxed)}

/* Finding cards for cross-domain intelligence: each one ends in an action, or
   it is trivia rather than intelligence. */
.finding{padding:var(--s-4);border-bottom:1px solid var(--border-subtle)}
.finding:last-child{border-bottom:0}
.finding__head{display:flex;gap:var(--s-2);align-items:center;margin-bottom:var(--s-2);
  flex-wrap:wrap}
.finding__title{font-size:var(--fs-md);font-weight:var(--fw-semibold)}
.finding__action{margin-top:var(--s-2);padding:var(--s-3);border-radius:var(--r-md);
  background:var(--accent-quiet);font-size:var(--fs-sm);line-height:var(--lh-relaxed)}
.finding__action b{color:var(--accent);display:block;font-size:var(--fs-2xs);
  text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}

.swatch{width:12px;height:12px;border-radius:3px;flex:none;
  border:1px solid rgba(255,255,255,.18)}

@media (max-width:860px){
  .tree__row{grid-template-columns:1fr 90px}
  .tree__row > :nth-child(n+3){display:none}
  .setting{grid-template-columns:1fr}
  .matrix{grid-template-columns:52px repeat(3,1fr)}
}

/* ===== Settings / Account controls ======================================= */
.setrow{display:flex;align-items:center;gap:var(--s-4);padding:var(--s-3) var(--s-4);
  border-bottom:1px solid var(--border-subtle)}
.setrow:last-child{border-bottom:0}
.setrow > :first-child{flex:1;min-width:0}
.setrow__label{font-size:var(--fs-md);font-weight:var(--fw-medium);color:var(--text-primary)}
.setrow__hint{font-size:var(--fs-xs);color:var(--text-secondary);margin-top:3px;
  line-height:var(--lh-relaxed);max-width:68ch}

.seg{display:inline-flex;padding:2px;background:var(--bg-sunken);
  border:1px solid var(--border-default);border-radius:var(--r-md);flex:none}
.seg__opt{height:24px;padding:0 var(--s-3);border:0;border-radius:var(--r-sm);
  background:none;color:var(--text-secondary);font-size:var(--fs-xs);
  font-weight:var(--fw-medium);cursor:pointer;white-space:nowrap}
.seg__opt:hover{color:var(--text-primary)}
.seg__opt[aria-checked="true"]{background:var(--bg-surface);color:var(--text-primary);
  box-shadow:var(--shadow-sm)}
.seg__opt:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

.switch{width:38px;height:22px;flex:none;border-radius:var(--r-full);padding:2px;
  border:1px solid var(--border-default);background:var(--bg-sunken);cursor:pointer;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
.switch__dot{display:block;width:16px;height:16px;border-radius:50%;
  background:var(--text-tertiary);transition:transform var(--dur-fast) var(--ease),
  background var(--dur-fast) var(--ease)}
.switch[aria-checked="true"]{background:var(--accent);border-color:var(--accent)}
.switch[aria-checked="true"] .switch__dot{transform:translateX(16px);background:var(--bg-surface)}
.switch:disabled{opacity:.45;cursor:not-allowed}
.switch:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

.kbdchip{font-family:var(--font-mono);font-size:var(--fs-xs);color:var(--text-secondary);
  background:var(--bg-sunken);border:1px solid var(--border-default);
  border-radius:var(--r-sm);padding:2px 7px;flex:none;white-space:nowrap}

@media (max-width:720px){
  .setrow{flex-direction:column;align-items:flex-start;gap:var(--s-2)}
}

/* ===== Account menu ======================================================= */
/* Fixed, not absolute. The header sets overflow:hidden so that nothing in it can
   ever spill and reflow the bar — which also means an absolutely positioned
   dropdown inside it is clipped at the header's bottom edge. Fixed positioning
   from the trigger's own rect sidesteps that entirely. */
.accmenu{position:fixed;z-index:var(--z-palette);width:250px;
  background:var(--bg-overlay);border:1px solid var(--border-default);
  border-radius:var(--r-lg);box-shadow:var(--shadow-lg);padding:var(--s-1);
  display:flex;flex-direction:column;gap:2px}
.accmenu__scrim{position:fixed;inset:0;z-index:calc(var(--z-palette) - 1)}
.accmenu__id{display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-3) var(--s-3) var(--s-3);border-bottom:1px solid var(--border-subtle);
  margin-bottom:var(--s-1)}
.accmenu__meta{min-width:0;display:flex;flex-direction:column;gap:2px}
.accmenu__name{font-size:var(--fs-md);font-weight:var(--fw-medium);
  color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.accmenu__mail{font-size:var(--fs-xs);color:var(--text-secondary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.accmenu__item{display:flex;align-items:center;gap:10px;width:100%;
  padding:7px var(--s-3);border:0;border-radius:var(--r-md);background:none;
  cursor:pointer;text-align:left;font-size:var(--fs-md);color:var(--text-secondary)}
.accmenu__item:hover,.accmenu__item:focus-visible{background:var(--bg-hover);
  color:var(--text-primary);outline:none}
.accmenu__item:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.accmenu__item .accmenu__ico{flex:none;display:grid;place-items:center;
  color:var(--text-tertiary)}
.accmenu__item:hover .accmenu__ico{color:var(--text-secondary)}
.accmenu__item kbd{margin-left:auto;font-family:var(--font-mono);
  font-size:var(--fs-2xs);color:var(--text-tertiary);background:var(--bg-raised);
  border:1px solid var(--border-subtle);border-radius:var(--r-sm);padding:1px 5px}
.accmenu__sep{height:1px;background:var(--border-subtle);margin:var(--s-1) var(--s-2)}
.accmenu__item--danger:hover{background:var(--sev-critical-bg);color:var(--sev-critical)}
.accmenu__item--danger:hover .accmenu__ico{color:var(--sev-critical)}

/* ===== Route loading screen ===============================================
   Ported from the supplied React/styled-components `loader-6`. The animation
   is pure CSS; styled-components only scoped it, which a class does here, so
   the console stays a static app with no React runtime pulled in for a
   keyframe.

   Two deliberate changes from the original:

   · --c was #f7971d, a fixed orange. It uses the brand saffron token instead,
     so the loader is the same colour as the rest of the console and follows
     the theme rather than sitting slightly off it in dark.
   · every hard-coded `black` became --loader-cut. Those blacks are not black —
     they are the CUT-OUTS that carve the tick out of the filled circle, and
     they only read as a tick when they match what is behind them. Left as
     black they would draw a black blob on a light page.

   One cycle is --loader-dur. The original 3s is a story: REC, then a laptop,
   then a tick. ROUTE_FLOOR_MS in app.js is held to the same value so the story
   always finishes rather than being cut off mid-sentence. */
.routeloader{
  grid-area:main; z-index:2;
  display:grid; place-items:center;
  background:var(--bg-root);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity var(--dur-instant) var(--ease), visibility 0s linear var(--dur-instant);
}
.routeloader[data-on="true"]{
  opacity:1; visibility:visible;
  transition:opacity var(--dur-instant) var(--ease), visibility 0s;
}

.routeloader .loader{
  --c: var(--v9-saffron);
  --loader-cut: var(--bg-root);
  --loader-dur: 1100ms;
  position:relative; width:14em; height:11em; font-size:10px; flex:none;
}

.routeloader .ph1{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  display:flex; flex-direction:row; align-items:center; gap:8px;
  animation:ph1 var(--loader-dur) ease infinite;
  clip-path:polygon(-4em -1em, 4em -1em, 4em 1em, -4em 1em);
}
.routeloader .record{
  position:absolute; left:50%; top:50%; width:2em; height:2em;
  background:var(--c); border-radius:999px;
  /* The blink is a third of the cycle in the original; kept proportional so
     compressing the cycle does not desynchronise it. */
  animation:blink calc(var(--loader-dur) / 3) step-end infinite;
  transform:translate(-3.5em,-50%);
}
.routeloader .record-text{
  position:absolute; color:var(--c); font-size:2.2em; font-weight:700;
  left:50%; top:50%; transform:translate(-0.5em,-50%); width:2em; height:1.5em;
}

@keyframes blink{50%{opacity:0}75%{opacity:1}}

@keyframes ph1{
  25.5%{translate:0 0; clip-path:polygon(-4em -1em, 4em -1em, 4em 1em, -4em 1em)}
  30%,to{opacity:1; translate:0 3em; clip-path:polygon(-4em 1em, 4em 1em, 4em 1em, -4em 1em)}
  30.1%{opacity:0; translate:0 3em}
  92.4%,to{translate:0 0; opacity:0; clip-path:polygon(-4em -1em, 4em -1em, 4em 1em, -4em 1em)}
  92.5%{opacity:1; clip-path:polygon(-4em -1em, -0.5em -1em, -0.5em 1em, -4em 1em)}
  to{opacity:1; clip-path:polygon(-4em -1em, 4em -1em, 4em 1em, -4em 1em)}
}

.routeloader .ph2{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-4em);
  width:11em; height:7em; perspective:150px; perspective-origin:50% 0%;
  transform-style:preserve-3d;
  animation:ph2 var(--loader-dur) ease-in-out infinite;
}
@keyframes ph2{
  0%,15%{translate:0 4em}
  0%,29%{opacity:0}
  30%{opacity:1}
  40%{translate:0 0}
  50%{translate:0 0.5em; opacity:1}
  50.1%,to{opacity:0}
}

.routeloader .laptop-b{
  position:absolute; left:0; right:0; bottom:0; height:0.5em; background:var(--c);
  border-bottom-left-radius:2em; border-bottom-right-radius:2em;
  animation:ph2b var(--loader-dur) ease infinite;
}
.routeloader .laptop-t{
  /* 11em body minus the 1.25em margins, height from the 42:30 viewBox. Without
     an explicit size an inline SVG falls back to 300x150 and bursts out. */
  display:block; width:8.5em; height:6.07em;
  margin:0 1.25em; color:var(--c); transform-origin:50% 100%;
  animation:ph2t var(--loader-dur) ease infinite;
}
@keyframes ph2t{
  0%,29%{transform:rotateX(-10deg)}
  0%,41.9%{stroke-dasharray:unset}
  42%{transform:rotateX(4deg); stroke-dasharray:0 0 100}
  50%{transform:rotateX(-20deg); stroke-dasharray:0 50 0 100}
}
@keyframes ph2b{42%{scale:1 1}50%{scale:0 1}}

.routeloader .icon{
  position:absolute; width:4em; height:4em; background:var(--c); border-radius:999px;
  top:50%; left:50%; transform:translate(-50%,-50%); transform-origin:center;
  animation:icon var(--loader-dur) ease-in-out infinite;
  isolation:isolate; border-color:var(--c); border-style:solid; z-index:-1;
}
@keyframes icon{
  0%,15%{translate:0 4.5em; width:0; height:0}
  0%,29%{opacity:0}
  30%{opacity:1}
  40%{translate:0 -0.75em; width:4em; height:4em}
  50%{translate:0 0em; opacity:1; background:var(--c)}
  50.1%{border-width:2em; background:var(--loader-cut)}
  65%{width:4em; height:4em; transform:translate(-50%,-50%); border-width:4px}
  80%,to{width:2em; height:2em; translate:0 0; transform:translate(-3.5em,-50%);
         border-width:1em; background:var(--loader-cut)}
  80.1%,to{background:var(--c)}
  84.9%{opacity:1}
  85%,to{opacity:0}
}

.routeloader .icon::before{
  content:""; position:absolute; top:50%; left:50%;
  border:0.8em solid var(--loader-cut); box-sizing:border-box;
  border-left-color:transparent !important; border-bottom-color:transparent !important;
  transform:translate(-50%,2.5em) rotate(-45deg); transform-origin:center;
  animation:iconb var(--loader-dur) ease-in-out infinite; z-index:-1;
}
@keyframes iconb{
  20%{transform:translate(-50%,2.5em) rotate(-45deg)}
  50%{transform:translate(-50%,-25%) rotate(-45deg); border-color:var(--loader-cut)}
  65%,to{transform:translateY(0) scale(1) scaleX(1.5) translate(-60%,-50%) rotate(45deg);
         border-color:var(--c)}
  85%,to{transform:translate(-40%,-50%) scale(0) scaleX(1.5) translate(-75%,-50%) rotate(45deg)}
}

.routeloader .icon::after{
  content:""; position:absolute; top:50%; left:50%; background:var(--loader-cut);
  width:1em; height:2em; box-sizing:border-box;
  border-left-color:transparent; border-bottom-color:transparent;
  animation:icona var(--loader-dur) ease-in-out infinite;
}
@keyframes icona{
  20%{transform:translate(-50%,2.5em)}
  50%{transform:translate(-50%,0.4em)}
  65%,to{transform:translate(-50%,2.5em)}
}

/* An animation this size is exactly what reduced-motion is asking about, so
   loader-6 is dropped entirely. The static spinner below stands in for it —
   see the rule at the end of this file, which is what stops the overlay being
   completely empty in `long` mode. */
@media (prefers-reduced-motion: reduce){
  .routeloader .loader{display:none}
}

/* ===== Short-load spinner: 12 fading bars ================================
   Ported from the supplied React `spinner-1`. It replaces the gooey dots for
   short waits: at ~28px next to the label it reads as "a moment", where a
   200px animation reads as "an event".

   The supplied CSS also carried --ds-blue-100, --context-card-border, a .dark
   block and friends. None of that belongs to the spinner — it is the source
   project's design tokens, and adopting it would stand a second token system
   up beside tokens.css with its own idea of what "background" means. Only the
   keyframes and the 1.2s linear timing came across.

   The 12 bars are one rule, not twelve. Each carries --i and derives both its
   angle and its delay from it, so the stagger cannot drift out of step with
   the rotation the way twelve hand-written rules eventually do.

   The +0.0001deg is from the original and is kept deliberately: a bar at
   exactly 0deg can take a different rasterisation path from its rotated
   siblings and render visibly crisper. */
.routeloader .spinner{
  /* The component's props, as custom properties. */
  --spinner-size:28px;
  /* Supplied default was #8f8f8f, which measures 3.02:1 on the light page —
     just over the line. The console's own secondary text colour is theme-aware
     and comfortable (7.22:1 light, 8.19:1 dark), and matches the label. */
  --spinner-color:var(--text-secondary);
  width:var(--spinner-size); height:var(--spinner-size); flex:none;
}
/* Offset by half the box so the bars radiate from the centre of the square. */
.routeloader .spinner__rel{
  position:relative; top:50%; left:50%;
  width:var(--spinner-size); height:var(--spinner-size);
}
.routeloader .spinner__bar{
  position:absolute; height:8%; width:24%; left:-10%; top:-3.9%;
  border-radius:5px; background:var(--spinner-color);
  transform:rotate(calc(var(--i) * 30deg + 0.0001deg)) translate(146%);
  animation:fade-spin 1.2s linear infinite;
  animation-delay:calc(-1.2s + var(--i) * 0.1s);
}
@keyframes fade-spin{0%{opacity:.15}100%{opacity:1}}

/* ===== Which loader is on screen =========================================
   Short loads get the spinner: a LOOP, so truncating it at any frame looks
   deliberate. Long loads get loader-6: a STORY, which needs time to play and
   gives someone waiting something that visibly progresses.

   The stage is a fixed square so the 28px spinner and the much larger loader-6
   occupy the same footprint, and swapping one for the other mid-wait does not
   shift anything on screen. */
.routeloader__stage{position:relative;width:200px;height:200px;
  display:grid;place-items:center;flex:none}
.routeloader[data-mode="short"] .loader{display:none}
.routeloader[data-mode="long"]  .spinner{display:none}

@media (prefers-reduced-motion: reduce){
  /* loader-6 is suppressed for reduced motion, so the spinner has to cover BOTH
     modes or a long wait would show an empty screen. Static, but present. */
  .routeloader[data-mode="long"] .spinner{display:block}
  .routeloader .spinner__bar{animation:none;opacity:.55}
}

/* ===== Tasks workspace ====================================================
   Added for the Tasks section. Everything here reuses the existing tokens and
   severity scale, so a CRITICAL task is the same red as a CRITICAL alert on
   the Command Center rather than a second, slightly different red. */

/* The dashboard strip. .metric already exists; this only lays them out. */
.metrics{display:grid;gap:var(--s-3);margin-bottom:var(--s-5);
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}

/* A blocked task states its reason at the top of the workspace, because the
   reason is the only thing that unblocks it. */
.callout{border:1px solid var(--border-subtle);border-left:3px solid var(--text-secondary);
  background:var(--bg-surface);border-radius:var(--r-md);
  padding:var(--s-3) var(--s-4);margin-bottom:var(--s-4);
  font-size:var(--fs-sm);line-height:var(--lh-relaxed)}
.callout[data-sev="critical"]{border-left-color:var(--sev-critical)}
.callout[data-sev="high"]{border-left-color:var(--sev-high)}

.checklist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.checklist__item{font-size:var(--fs-sm);line-height:var(--lh-relaxed)}
.checklist__item input{accent-color:var(--accent);width:16px;height:16px;margin-top:2px;flex:none}

/* AI output is boxed and tinted so it cannot be mistaken for a system fact at
   a glance — Tasks §43 requires the distinction to be visible, not implied. */
.aicard{border:1px dashed var(--border-strong);border-radius:var(--r-md);
  background:var(--bg-raised);padding:var(--s-3) var(--s-4);margin-bottom:var(--s-3)}
.aicard:last-child{margin-bottom:0}

.deplist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2);
  font-size:var(--fs-sm)}

.thread{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-4)}
.thread__meta{display:flex;align-items:center;gap:var(--s-2);margin-bottom:4px}
.thread__body{margin:0;font-size:var(--fs-sm);line-height:var(--lh-relaxed);
  color:var(--text-secondary);
  /* Comment bodies are untrusted text: wrap anything, never let a long token
     from a paste stretch the card and break the grid. */
  overflow-wrap:anywhere}
.avatar--sm{width:22px;height:22px;font-size:var(--fs-2xs)}

/* The activity stream is colour-keyed by ACTOR KIND, which is the one thing a
   reader needs to see instantly: did a person do this, or did the AI? */
.timeline__item[data-actor="AI_WORKER"] .timeline__dot{background:var(--sev-info,var(--accent))}
.timeline__item[data-actor="SYSTEM"] .timeline__dot{background:var(--text-tertiary)}

@media (max-width:1000px){
  .grid--2{grid-template-columns:1fr}
}

/* The activity stream's own item/dot. The older .tl pattern is left alone —
   it is used by the Command Center and has a different shape. */
.timeline__item{position:relative;padding:0 0 var(--s-4) 0;display:flex;gap:var(--s-3)}
.timeline__item:last-child{padding-bottom:0}
.timeline__dot{position:absolute;left:calc(var(--s-6) * -1 + 4px);top:6px;
  width:7px;height:7px;border-radius:50%;background:var(--text-secondary);
  box-shadow:0 0 0 3px var(--bg-surface);flex:none}

/* ═══════════════════════════════════════════════════════════════════════════
   Sign-in (login.html)

   Built from the same tokens as the rest of the console, so it inherits dark
   mode and the density setting without a second palette to keep in sync.
   ═══════════════════════════════════════════════════════════════════════════ */
.authbody{background:var(--bg-root);margin:0}

.auth{min-height:100vh;min-height:100dvh;display:grid;place-items:center;
  padding:var(--s-6) var(--s-4)}
.auth__card{position:relative;width:100%;max-width:400px;background:var(--bg-surface);
  border:1px solid var(--border-subtle);border-radius:var(--r-xl);
  box-shadow:var(--shadow-lg);padding:var(--s-8) var(--s-6)}

.auth__head{text-align:center;margin-bottom:var(--s-6)}
.auth__logo{display:block;margin:0 auto var(--s-3);border-radius:var(--r-md)}
.auth__brand{font-size:var(--fs-xl);font-weight:var(--fw-bold);
  color:var(--text-primary);margin:0;font-family:var(--font-te)}
.auth__sub{font-size:var(--fs-sm);color:var(--text-secondary);margin:2px 0 0;
  font-family:var(--font-te)}

/* Language switch. Two real buttons; the active one is pressed, which is what
   a screen reader announces, rather than relying on colour alone. */
.auth__langs{display:inline-flex;gap:2px;margin-top:var(--s-4);padding:2px;
  background:var(--bg-sunken);border:1px solid var(--border-subtle);
  border-radius:var(--r-full)}
.auth__lang{border:0;background:transparent;cursor:pointer;
  padding:4px var(--s-3);border-radius:var(--r-full);
  font-size:var(--fs-xs);font-weight:var(--fw-medium);color:var(--text-secondary);
  font-family:var(--font-te)}
.auth__lang:hover{color:var(--text-primary)}
.auth__lang[aria-pressed="true"]{background:var(--bg-surface);color:var(--text-primary);
  box-shadow:var(--shadow-sm)}

.auth__title{font-size:var(--fs-lg);font-weight:var(--fw-semibold);
  color:var(--text-primary);margin:0 0 2px;font-family:var(--font-te)}
.auth__lead{font-size:var(--fs-sm);color:var(--text-secondary);
  margin:0 0 var(--s-5);font-family:var(--font-te)}

/* The live region. tabindex=-1 lets script move focus here on failure so the
   reason is where the operator is, sighted or not. */
/* NO `display` here. A class rule carrying one outranks the user-agent's
   [hidden]{display:none}, which made this empty box visible on page load —
   an error panel announcing nothing. A div is display:block anyway. */
.auth__error{padding:var(--s-3);margin-bottom:var(--s-4);
  background:var(--sev-critical-bg,#FEF2F2);
  border:1px solid var(--sev-critical,#DC2626);
  border-left-width:3px;border-radius:var(--r-md);
  font-size:var(--fs-sm);color:var(--text-primary);font-family:var(--font-te)}
.auth__error:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.auth__hint--ok{color:var(--ok-fg,var(--text-secondary))}
.auth__hint--bad{color:var(--danger-fg,var(--text-secondary))}
.auth__ok{padding:var(--s-3);margin-bottom:var(--s-4);border-radius:var(--radius-2);
  border:1px solid var(--ok-border,var(--border));background:var(--ok-bg,var(--surface-2));
  color:var(--ok-fg,var(--text));font-size:var(--fs-sm);line-height:1.5}
.auth__ok[hidden]{display:none}
.auth__ok:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.auth__errorHead{display:block;font-weight:var(--fw-semibold);margin-bottom:2px}

.auth__pw{position:relative;display:flex}
.auth__pw .input{padding-right:36px}
.auth__pwToggle{position:absolute;right:0;top:0;height:32px;width:32px;
  border:0;background:transparent;cursor:pointer;color:var(--text-secondary);
  display:grid;place-items:center;font-size:13px;border-radius:var(--r-sm)}
.auth__pwToggle:hover{color:var(--text-primary)}

/* A one-time code is read back digit by digit; spacing it out makes a
   mistyped character findable without counting. */
.auth__code{letter-spacing:.35em;font-family:var(--font-mono);text-align:center}

.auth__hint{font-size:var(--fs-xs);color:var(--text-tertiary);
  margin:var(--s-2) 0 0;font-family:var(--font-te)}
.auth__submit{width:100%;height:36px;margin-top:var(--s-4);font-family:var(--font-te)}
.auth__link{width:100%;margin-top:var(--s-2);font-family:var(--font-te)}
.auth__note{font-size:var(--fs-xs);color:var(--text-secondary);
  margin:var(--s-3) 0 0;padding:var(--s-3);background:var(--bg-sunken);
  border-radius:var(--r-md);font-family:var(--font-te)}
.auth__note:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.auth__captcha{margin-top:var(--s-4);justify-content:center}
.auth__captcha:not([hidden]){display:flex}   /* same [hidden] trap as above */

/* Focus must stay visible on every control: an operator navigating this form by
   keyboard has no other way of knowing where they are. */
.auth__lang:focus-visible,.auth__pwToggle:focus-visible,
.auth .btn:focus-visible,.auth .input:focus-visible{
  outline:2px solid var(--border-focus);outline-offset:2px}

@media (max-width:420px){
  .auth{padding:var(--s-4) var(--s-3)}
  .auth__card{padding:var(--s-6) var(--s-4);border-radius:var(--r-lg)}
}

/* ── Authenticator enrolment ───────────────────────────────────────────────── */
.auth__qrBox{display:flex;justify-content:center;padding:var(--s-4);
  margin-bottom:var(--s-4);background:#FFFFFF;   /* a QR needs white quiet zone
     regardless of theme — scanners read contrast, not our palette */
  border:1px solid var(--border-subtle);border-radius:var(--r-md)}
.auth__qr{display:block;width:180px;height:180px;image-rendering:pixelated}

.auth__reveal{margin-bottom:var(--s-4);font-size:var(--fs-sm);
  font-family:var(--font-te);color:var(--text-secondary)}
.auth__reveal summary{cursor:pointer;padding:var(--s-2) 0;color:var(--text-link)}
.auth__reveal summary:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

.auth__secretRow{display:flex;align-items:center;gap:var(--s-2);margin-top:var(--s-2)}
/* The seed is read character by character off the screen, so it gets a mono
   face, generous tracking and the ability to wrap rather than overflow. */
.auth__secret{flex:1;font-family:var(--font-mono);font-size:var(--fs-sm);
  letter-spacing:.06em;background:var(--bg-sunken);padding:var(--s-2) var(--s-3);
  border-radius:var(--r-sm);word-break:break-all;user-select:all;
  border:1px solid var(--border-subtle)}
.auth__copied{display:inline-block;margin-top:var(--s-2);font-size:var(--fs-xs);
  color:var(--sev-ok)}

/* Unlike .auth__note this one is always shown, so it carries no [hidden]. */
.auth__note--always{display:block}

/* ── Recovery codes ────────────────────────────────────────────────────────── */
.auth__codes{list-style:none;margin:0 0 var(--s-3);padding:var(--s-3);
  display:grid;grid-template-columns:1fr 1fr;gap:var(--s-2);
  background:var(--bg-sunken);border:1px solid var(--border-subtle);
  border-radius:var(--r-md)}
/* Mono and spaced: these get read aloud and typed by hand under pressure. */
.auth__code1{font-family:var(--font-mono);font-size:var(--fs-sm);
  letter-spacing:.04em;color:var(--text-primary);user-select:all;text-align:center}
.auth__codeActions{display:flex;align-items:center;gap:var(--s-2);
  margin-bottom:var(--s-3);flex-wrap:wrap}
.auth__ack{display:flex;align-items:flex-start;gap:var(--s-2);cursor:pointer;
  font-size:var(--fs-sm);font-family:var(--font-te);color:var(--text-primary);
  margin-top:var(--s-3)}
.auth__ack input{margin-top:2px;flex:none;width:16px;height:16px;cursor:pointer}
.auth__ack input:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
@media (max-width:420px){ .auth__codes{grid-template-columns:1fr} }

/* Demonstration-data banner. Deliberately loud: these screens look finished,
   and an editor acting on fixture figures is the failure this prevents. */
.demobanner{display:block;padding:var(--s-3) var(--s-4);margin-bottom:var(--s-4);
  background:var(--sev-medium-bg,#FFFBEB);border:1px solid var(--sev-medium,#D97706);
  border-left-width:3px;border-radius:var(--r-md);
  font-size:var(--fs-sm);color:var(--text-primary);line-height:1.5}
.demobanner strong{display:inline-block;margin-right:var(--s-2)}

/* ── Social sign-in and the passwordless divider ───────────────────────────── */
/* :not([hidden]) — this is the THIRD time in this file that a class rule
   carrying `display` silently beat the user agent's [hidden]{display:none}.
   Script sets .hidden to move between steps, and the social block simply kept
   rendering on top of the authenticator screen. Any rule here that sets
   `display` on a element script toggles must guard it this way. */
.auth__social{flex-direction:column;gap:var(--s-2);margin-bottom:var(--s-4)}
.auth__social:not([hidden]){display:flex}
.auth__social-btn{width:100%;height:38px;justify-content:center;gap:var(--s-2);
  font-family:var(--font-te);text-decoration:none}
/* The real brand marks, inline. They cannot be <img> tags: the CSP allows
   images only from 'self', *.varahi9media.com and data:, so a CDN logo would
   be blocked and render as nothing. Inline SVG also keeps them crisp on
   high-DPI screens and needs no extra request on the sign-in path.
   Each keeps its official colours in BOTH themes — a Google mark recoloured
   to match a dark UI stops being the Google mark, and users identify these
   buttons by the logo before they read the label. */
.auth__social-svg{width:18px;height:18px;flex:none;display:block}
/* A rule with the word sitting in it. Two elements (line, word, line) would be
   three things to keep aligned; one border and a padded span is enough. */
.auth__or{align-items:center;gap:var(--s-3);margin:var(--s-4) 0;
  color:var(--text-tertiary);font-size:var(--fs-xs);font-family:var(--font-te)}
.auth__or:not([hidden]){display:flex}
.auth__or::before,.auth__or::after{content:'';flex:1;height:1px;background:var(--border-subtle)}

/* ═══════════════════════════════════════════════════════════════════════════
   Split-panel auth layout.

   Adapted from a React/Tailwind reference, rebuilt in this project's own CSS —
   the console is vanilla ES modules with no build step, and pulling in React +
   Tailwind + shadcn to render one screen would have reintroduced the whole
   toolchain that was deliberately removed.

   The visual panel carries the brand plate rather than the reference's stock
   photography and invented statistics. A login screen inventing figures is a
   small lie in the first thing anyone sees.
   ═══════════════════════════════════════════════════════════════════════════ */
.auth__shell{width:100%;max-width:1040px;display:grid;grid-template-columns:1fr;
  background:var(--bg-surface);border:1px solid var(--border-subtle);
  border-radius:var(--r-xl);box-shadow:var(--shadow-lg);overflow:hidden;
  position:relative}

/* Two columns only where there is room for the image to mean anything. Below
   this the panel is HIDDEN, not shrunk: a 200px-wide slice of a photograph is
   decoration that costs a phone 126KB. */
@media (min-width:900px){
  .auth__shell{grid-template-columns:1.1fr 1fr}
}

/* Dark ground, not the surface colour. The caption is white, and if the image
   fails to load — slow link, blocked request, typo in a path — white on a light
   panel is invisible text. The contrast auditor reads it the same way, and it
   was right to: it cannot see the photograph, and neither can a user whose
   photograph never arrived. */
.auth__visual{display:none;position:relative;background:#14171F;min-height:560px}
/* The plate is a BACKGROUND, not an <img>, and the rule that names it lives
   inside this media query. `display:none` on a parent does NOT reliably stop a
   browser fetching an <img> — measured: a phone downloaded all 126KB of a panel
   it never showed. A background-image declared only above 900px is never even
   requested below it. The panel is aria-hidden and decorative, so nothing is
   lost by moving it out of the DOM. */
@media (min-width:900px){
  .auth__visual{display:block;
    background-image:url('../../web/img/404-newsroom.webp');
    background-size:cover;background-position:center 62%;background-repeat:no-repeat}
}
/* Scrim only across the bottom, where the caption sits — a full overlay would
   dull the whole plate to make one line readable. */
.auth__visual::after{content:'';position:absolute;inset:auto 0 0 0;height:45%;
  background:linear-gradient(to top, rgba(10,10,10,.82), rgba(10,10,10,0))}
.auth__visualText{position:absolute;left:0;right:0;bottom:0;z-index:1;
  padding:var(--s-6);color:#fff}
.auth__visualTitle{font-family:var(--font-te);font-size:var(--fs-xl);
  font-weight:var(--fw-bold);margin:0 0 4px;line-height:1.25}
.auth__visualSub{font-family:var(--font-te);font-size:var(--fs-sm);
  margin:0;opacity:.85;line-height:1.5}

/* The card loses its own frame inside the shell — the shell is the frame now. */
.auth__shell .auth__card{max-width:none;border:0;border-radius:0;box-shadow:none;
  padding:var(--s-8) var(--s-6);align-self:center;width:100%}

/* Sits opposite the theme toggle in the card's top corner. Absolute so it
   does not push the logo down, and given a real hit area because on a phone
   this is the escape route from a screen someone did not mean to open. */
.auth__back{position:absolute;top:var(--s-3);left:var(--s-3);z-index:2;
  display:inline-flex;align-items:center;gap:6px;padding:6px 10px;
  border-radius:var(--r-full);font-size:var(--fs-sm);line-height:1;
  color:var(--text-secondary);text-decoration:none;
  background:transparent;border:1px solid transparent}
.auth__back:hover{color:var(--text-primary);background:var(--bg-sunken);
  border-color:var(--border-subtle)}
.auth__back:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* Full-width, because on the screen where it appears it is the primary action:
   the QR above it cannot be used on the device that is showing it. */
.auth__openApp{width:100%;justify-content:center;margin-bottom:var(--s-3);
  font-family:var(--font-te);text-decoration:none}
.auth__openApp[hidden]{display:none}

.auth__themeToggle{position:absolute;top:var(--s-3);right:var(--s-3);z-index:2;
  width:32px;height:32px;display:grid;place-items:center;border-radius:var(--r-full);
  border:1px solid var(--border-subtle);background:var(--bg-surface);
  color:var(--text-secondary);cursor:pointer}
.auth__themeToggle:hover{color:var(--text-primary);background:var(--bg-hover)}
.auth__themeToggle:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* Entrance. Runs once on load and is purely presentational, so it is switched
   off entirely for anyone who has asked for reduced motion — for some people
   this kind of movement causes nausea, not delight. */
/* Transform only — this animation never touches opacity.
   Twice now a paused or deferred animation has left the page sitting at its
   FROM state, and with opacity in the keyframes that state is an invisible
   sign-in screen. Movement is the one property where being stuck at the start
   costs nothing: the page is simply eight pixels low and perfectly readable. */
@keyframes v9-auth-in{from{transform:translateY(8px) scale(.995)}
                      to{transform:none}}
/* NO fill-mode. `both` pins the element at the keyframe's FROM state whenever
   the animation does not actually run — a background tab, a throttled renderer,
   anything that defers it — and the from state here is opacity:0. Observed
   exactly that: a fully loaded sign-in page sitting invisible at opacity 0 six
   seconds after load.

   Without fill-mode the element keeps its normal, visible styles before and
   after; the animation is purely an entrance. The rule is worth stating
   plainly: this page must be readable with no animation at all. */
@media (prefers-reduced-motion:no-preference){
  .auth__shell{animation:v9-auth-in .45s ease-out}
  .auth__shell .auth__card{animation:v9-auth-in .5s ease-out .1s}
}

@media (max-width:420px){
  .auth__shell{border-radius:var(--r-lg)}
  .auth__shell .auth__card{padding:var(--s-6) var(--s-4)}
}

/* ===== Tasks board ======================================================= */
/* Laid out for an operations screen: a dense header, a KPI strip that wraps
   rather than shrinking its numbers, and a two-column body that collapses to
   one on a narrow viewport. Nothing here is Tasks-specific styling for its own
   sake — the tokens, chips, tags and cards are the console's existing ones. */
.taskspage{display:flex;flex-direction:column;gap:var(--s-5)}

.taskshead{display:flex;align-items:flex-start;gap:var(--s-4);flex-wrap:wrap}
.taskshead__title{flex:1 1 260px;min-width:0}
.taskshead__title h1{margin:0;font-size:var(--fs-2xl);font-weight:700;letter-spacing:-.01em}
.taskshead__title p{margin:2px 0 0;color:var(--text-tertiary);font-size:var(--fs-sm)}
.taskshead__actions{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.datechip{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 var(--s-3);
  border:1px solid var(--border-default);border-radius:var(--r-sm,4px);
  background:var(--bg-card);color:var(--text-secondary);font-size:var(--fs-xs)}

/* The AI action is visually distinct from a normal primary button, because it
   proposes rather than commits (§68). */
.btn--ai{background:linear-gradient(135deg,#6D4AE0,#8B5CF6);color:#fff;border-color:transparent}
.btn--ai:hover{filter:brightness(1.08)}
.btn--block{width:100%;justify-content:center}

.langswitch{display:inline-flex;flex:none;border:1px solid var(--border-default);border-radius:var(--r-sm,4px);
  overflow:hidden}
/* flex:none + nowrap: in a crowded header the English button was shrinking to
   a 4px sliver of blue with its label clipped away. */
.langswitch__btn{flex:none;white-space:nowrap;height:30px;padding:0 var(--s-3);background:transparent;border:0;cursor:pointer;
  color:var(--text-secondary);font-size:var(--fs-xs)}
.langswitch__btn+.langswitch__btn{border-left:1px solid var(--border-default)}
.langswitch__btn.is-on{background:var(--accent);color:var(--text-inverse);font-weight:600}

/* ---- KPI strip ---------------------------------------------------------- */
.kpistrip{display:grid;gap:var(--s-3);
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.kpi{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3) var(--s-4);
  background:var(--bg-card);border:1px solid var(--border-subtle);border-radius:var(--r-md,8px)}
.kpi--wide{grid-column:span 2;min-width:0}
@media (max-width:900px){.kpi--wide{grid-column:span 1}}
.kpi__ico{width:34px;height:34px;flex:0 0 auto;display:grid;place-items:center;border-radius:8px;
  font-size:15px;background:var(--bg-raised);color:var(--text-secondary)}
.kpi__ico[data-sev="critical"]{background:var(--sev-critical-bg);color:var(--sev-critical)}
.kpi__ico[data-sev="high"]    {background:var(--sev-high-bg);    color:var(--sev-high)}
.kpi__ico[data-sev="medium"]  {background:var(--sev-medium-bg);  color:var(--sev-medium)}
.kpi__ico[data-sev="ok"]      {background:var(--sev-ok-bg);      color:var(--sev-ok)}
.kpi__ico[data-sev="low"]     {background:var(--sev-low-bg);     color:var(--sev-low)}
.kpi__text{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.kpi__text strong{font-size:var(--fs-xl);font-weight:700;font-variant-numeric:tabular-nums}
.kpi__text span{font-size:var(--fs-xs);color:var(--text-tertiary)}
.kpi__warn{color:var(--sev-critical)!important;font-weight:600}

/* ---- Body --------------------------------------------------------------- */
.tasksmain{display:grid;gap:var(--s-4);grid-template-columns:minmax(0,1fr) 320px;align-items:start}
@media (max-width:1100px){.tasksmain{grid-template-columns:minmax(0,1fr)}}
.tasksmain__content{display:flex;flex-direction:column;gap:var(--s-4);min-width:0}
.tasksmain__rail{display:flex;flex-direction:column;gap:var(--s-4);min-width:0}
.tabs--inset{margin:0;padding:0 var(--s-3)}
.tabs__pane{min-width:0}
.tasksfoot{display:flex;justify-content:space-between;gap:var(--s-4);flex-wrap:wrap;
  margin:0;padding-top:var(--s-3);border-top:1px solid var(--border-subtle);
  font-size:var(--fs-xs);color:var(--text-tertiary)}

.tasklink{color:var(--accent);text-decoration:none;font-size:var(--fs-2xs)}
.tasklink:hover{text-decoration:underline}
.progcell{display:flex;align-items:center;gap:8px}
.progcell .bar__track{flex:1}
.progcell__val{font-size:var(--fs-2xs);min-width:30px;text-align:right}

/* ---- AI assistant ------------------------------------------------------- */
.assistant__head{display:flex;align-items:center;gap:var(--s-2);
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--border-subtle)}
.assistant__head h2{margin:0;font-size:var(--fs-sm);font-weight:600}
.assistant__ico{width:22px;height:22px;display:grid;place-items:center;border-radius:6px;
  background:linear-gradient(135deg,#6D4AE0,#8B5CF6);color:#fff;font-size:var(--fs-2xs)}
.assistant__body{display:flex;flex-direction:column;gap:var(--s-3);padding:var(--s-4)}
.assistant__chips{display:flex;flex-direction:column;gap:6px}
.chip{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  padding:8px var(--s-3);border:1px solid var(--border-subtle);border-radius:var(--r-sm,6px);
  background:var(--bg-raised);color:var(--text-secondary);font-size:var(--fs-xs);cursor:pointer}
.chip:hover{border-color:var(--accent);color:var(--text-primary)}
.assistant__log:empty{display:none}
.assistant__q{font-size:var(--fs-xs);font-weight:600;color:var(--text-primary);margin-bottom:6px}
.assistant__a{font-size:var(--fs-xs);color:var(--text-secondary);line-height:var(--lh-relaxed);
  background:var(--bg-raised);border-left:2px solid #8B5CF6;border-radius:0 6px 6px 0;
  padding:var(--s-3)}
.assistant__a p{margin:0 0 6px}
.assistant__a.is-wait{color:var(--text-tertiary)}
.assistant__a.is-err{border-left-color:var(--sev-critical);color:var(--sev-critical)}
/* An AI answer must never look like an authoritative system value (§68). */
.assistant__prov{margin:0!important;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-style:italic}

/* ---- Deadlines rail ----------------------------------------------------- */
.deadlines{list-style:none;margin:0;padding:var(--s-2);display:flex;flex-direction:column;gap:2px}
.deadlines__none{padding:var(--s-4);text-align:center;color:var(--text-tertiary);font-size:var(--fs-xs)}
.deadline{display:flex;align-items:center;gap:var(--s-3);width:100%;text-align:left;
  padding:var(--s-2) var(--s-3);border:0;border-radius:var(--r-sm,6px);
  background:transparent;cursor:pointer;min-width:0}
.deadline:hover{background:var(--bg-raised)}
.deadline__bar{width:3px;align-self:stretch;border-radius:2px;flex:0 0 auto;background:var(--text-tertiary)}
.deadline__bar[data-sev="critical"]{background:var(--sev-critical)}
.deadline__bar[data-sev="high"]{background:var(--sev-high)}
.deadline__bar[data-sev="medium"]{background:var(--sev-medium)}
.deadline__bar[data-sev="low"]{background:var(--sev-low)}
.deadline__text{display:flex;flex-direction:column;gap:1px;flex:1 1 auto;min-width:0}
.deadline__text strong{font-size:var(--fs-xs);font-weight:500;color:var(--text-primary)}
.deadline__text small{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.deadline__left{font-size:var(--fs-2xs);font-weight:600;white-space:nowrap;flex:0 0 auto;
  padding:2px 7px;border-radius:10px;background:var(--bg-raised);color:var(--text-secondary)}
.deadline__left[data-sev="critical"]{background:var(--sev-critical-bg);color:var(--sev-critical)}
.deadline__left[data-sev="medium"]{background:var(--sev-medium-bg);color:var(--sev-medium)}

/* Per-bar colour, so a five-stage lifecycle does not read as one series. */
.chart__bar[data-sev="critical"]{fill:var(--sev-critical)}
.chart__bar[data-sev="high"]    {fill:var(--sev-high)}
.chart__bar[data-sev="medium"]  {fill:var(--sev-medium)}
.chart__bar[data-sev="low"]     {fill:var(--sev-low)}
.chart__bar[data-sev="ok"]      {fill:var(--sev-ok)}
.chart__bar[data-cat="0"]{fill:var(--cat-1)} .chart__bar[data-cat="1"]{fill:var(--cat-2)}
.chart__bar[data-cat="2"]{fill:var(--cat-3)} .chart__bar[data-cat="3"]{fill:var(--cat-4)}
.chart__bar[data-cat="4"]{fill:var(--cat-5)} .chart__bar[data-cat="5"]{fill:var(--cat-6)}
.chart__bar[data-cat="6"]{fill:var(--cat-7)} .chart__bar[data-cat="7"]{fill:var(--cat-8)}
.chart__val{fill:var(--text-primary);font-size:13px;font-weight:700;
  font-family:var(--font-mono);font-variant-numeric:tabular-nums}

/* ---- Row action menu ---------------------------------------------------- */
.rowmenu{position:relative;display:flex;justify-content:center}
.rowmenu__btn{width:34px;height:34px;border-radius:var(--r-md);border:1px solid transparent;
  background:transparent;color:var(--text-secondary);cursor:pointer;font-size:var(--fs-md);
  display:grid;place-items:center;line-height:1}
.rowmenu__btn:hover{background:var(--bg-raised);border-color:var(--border-default);color:var(--text-primary)}
.rowmenu__btn:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.rowmenu__list{position:absolute;right:0;top:calc(100% + 4px);z-index:20;min-width:190px;
  background:var(--bg-overlay);border:1px solid var(--border-default);
  border-radius:var(--r-md);box-shadow:var(--shadow-lg);padding:var(--s-1);
  display:flex;flex-direction:column}
.rowmenu__item{text-align:left;padding:9px var(--s-3);border:0;background:transparent;
  border-radius:var(--r-sm);color:var(--text-primary);font-size:var(--fs-xs);
  font-weight:var(--fw-medium);cursor:pointer;white-space:nowrap}
.rowmenu__item:hover{background:var(--bg-hover)}
.rowmenu__item.is-danger{color:var(--sev-critical)}

/* ---- Tasks: additions for exact parity ---------------------------------- */
/* A tab label never wraps to two lines ("List / View"); the strip scrolls. */
.tab{display:inline-flex;flex:none;white-space:nowrap;align-items:center;gap:7px;font-size:var(--fs-sm);
  font-weight:var(--fw-semibold);padding:var(--s-3) var(--s-3)}
.tab svg{opacity:.75}
.tab[aria-selected="true"] svg{opacity:1}
.tabs{gap:var(--s-1);overflow-x:auto;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}

.kpi__delta{font-weight:var(--fw-semibold)!important}
.kpi__delta[data-dir="up"]{color:var(--sev-ok)!important}
.kpi__delta[data-dir="down"]{color:var(--sev-critical)!important}
.kpi__muted{color:var(--text-tertiary)!important}

.linkbtn{background:none;border:0;padding:0;cursor:pointer;color:var(--text-link);
  font-size:var(--fs-xs);font-weight:var(--fw-semibold)}
.linkbtn:hover{text-decoration:underline}

.insights{padding:var(--s-4);display:flex;flex-direction:column;gap:var(--s-4)}
.insights__lead{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--text-primary)}
.insights__none{margin:0;color:var(--text-secondary);font-size:var(--fs-xs)}
.insightlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-3)}
.insightlist li{display:flex;flex-direction:column;gap:3px}
.insightlist li > span{font-size:var(--fs-xs);color:var(--text-secondary);line-height:var(--lh-normal)}
.insightlist li > strong{font-size:var(--fs-sm);color:var(--text-primary)}
.insights__notice{display:flex;align-items:center;gap:8px;margin:0;padding:var(--s-3);
  border-radius:var(--r-md);background:var(--bg-sunken);border:1px solid var(--border-subtle);
  font-size:var(--fs-2xs);color:var(--text-secondary)}

/* ===== Responsive: one UI, every width ==================================
   The board is a three-band layout (header / KPI strip / content+rail) and each
   band collapses on its own terms rather than the page switching to a separate
   mobile design. Breakpoints are placed where THIS content stops fitting, not
   at device names.

   The table is the one thing that cannot reflow: eight columns of operational
   data do not become readable by being squeezed. Below 900px it scrolls
   horizontally inside its own container with the first column pinned, so the
   task you are tracking stays on screen while you read across. ====================== */

@media (max-width:1280px){
  .tasksmain{grid-template-columns:minmax(0,1fr) 300px}
  .kpistrip{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
}

@media (max-width:1100px){
  /* The rail moves below the content rather than shrinking further: at under
     280px the assistant's prompt chips wrap to three lines each. */
  .tasksmain{grid-template-columns:minmax(0,1fr)}
  .tasksmain__rail{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
    gap:var(--s-4);align-items:start}
}

@media (max-width:900px){
  .taskshead{gap:var(--s-3)}
  .taskshead__title h1{font-size:var(--fs-xl)}
  .kpistrip{grid-template-columns:repeat(2,minmax(0,1fr))}
  .kpi--wide{grid-column:span 2}
  .grid--3,.grid--2{grid-template-columns:minmax(0,1fr)}
  /* Horizontal scroll with a pinned first column. min-width keeps the columns
     at their real widths instead of crushing them. */
  .tabs__pane tbody td:nth-child(2),
  .tabs__pane thead th:nth-child(2){position:sticky;left:0;z-index:2;
    background:var(--bg-surface);box-shadow:1px 0 0 var(--border-subtle)}
  .tabs__pane thead th:nth-child(2){background:var(--bg-sunken);z-index:3}
  .toolbar{gap:var(--s-2)}
  .field--search{min-width:0;flex:1 1 160px}
}

@media (max-width:640px){
  .taskspage{gap:var(--s-4)}
  .taskshead{flex-direction:column;align-items:stretch}
  /* In a column the 260px flex-basis above would become a 260px HEIGHT and
     leave a blank band under the title; size to content here. */
  .taskshead__title{flex:0 0 auto}
  .taskshead__actions{justify-content:flex-start;flex-wrap:wrap}
  .datechip{order:3}
  .kpistrip{grid-template-columns:minmax(0,1fr)}
  .kpi--wide{grid-column:span 1}
  .tablefoot{justify-content:center;row-gap:var(--s-2)}
  .tablefoot .toolbar__spacer{display:none}
  .tasksfoot{flex-direction:column;gap:var(--s-1);text-align:center}
  /* Below this width a saved-view row of four chips is taller than the table
     header it sits above; it scrolls instead. */
  .savedviews{overflow-x:auto;scrollbar-width:none;flex:1 1 100%}
  .savedviews::-webkit-scrollbar{display:none}
}

/* Coarse pointer: every interactive target reaches the 44px recommended
   minimum, regardless of viewport width — a stylus or a thumb on a 1280px
   tablet needs it as much as a phone does. */
@media (pointer:coarse){
  .btn,.field,.input{min-height:44px}
  .btn--sm{min-height:38px}
  .pager__btn{min-width:38px;height:38px}
  .rowmenu__btn{width:42px;height:42px}
  .tab{padding:var(--s-3) var(--s-4)}
  .chip{padding:12px var(--s-3)}
}

@media (prefers-reduced-motion:reduce){
  .donut__seg{transition:none}
}

/* The board's table is dense by nature. Cell padding is trimmed from the
   console default so ten columns fit a 1440px desk without scrolling, and the
   first data column stays pinned whenever it DOES scroll — at any width, not
   only on a phone. An operator tracking one task must not lose which row they
   are reading as they scan across to its SLA. */
.tabs__pane thead th,
.tabs__pane tbody td{padding-left:var(--s-2);padding-right:var(--s-2)}
.tabs__pane tbody td:nth-child(2),
.tabs__pane thead th:nth-child(2){position:sticky;left:0;z-index:2;
  background:var(--bg-surface)}
.tabs__pane thead th:nth-child(2){background:var(--bg-sunken);z-index:3}
.tabs__pane tbody tr:hover td:nth-child(2){background:var(--bg-sunken)}

/* Fixed layout so the declared column widths are honoured exactly and the one
   column WITHOUT a width — Task — absorbs whatever is left. Under `auto`
   layout the browser was widening several columns past their declaration to
   fit content, pushing the table 76px past its container and producing a
   horizontal scrollbar on a 1520px display. Scoped to this pane so no other
   screen's table changes. */
/* 1240px is not arbitrary. The checkbox and Actions columns are declared in
   PIXELS (32 + 64) while the data columns are percentages, so the percentages
   resolve against the table width MINUS those 96px — 12% of a 1140px table is
   125px of usable cell, not 137. The widest chip in the Status column,
   "Pending Review", is the constraint. It is set tighter inside a table cell
   (below) so it measures ~112px, which fits the ~120px the 12% column yields at
   this min-width — and 1100 is under the ~1130px of content a 1520px display
   actually offers beside the rail, so a full-size desktop shows the whole table
   with no horizontal scroll at all.
   Below this width the table scrolls inside its container rather than cutting a
   word in half, with the Task column pinned so the row stays identifiable. */
.tabs__pane table{table-layout:fixed;width:100%;min-width:1100px}
.tabs__pane td{overflow:hidden}
/* Below the min-width the table scrolls inside its container with the Task
   column pinned, rather than crushing ten columns into a phone. The proportions
   above then hold at every width above it. */

/* Chips inside the task table are set one step tighter than a standalone chip.
   A status pill in a dense grid is read as a coloured label, not as prose, and
   the extra padding a standalone chip needs is what pushed "Pending Review"
   past its column. The ellipsis is a guarantee, not a plan: if a future status
   name is longer still, it truncates visibly with the full text in the title
   attribute rather than being sliced mid-letter by overflow:hidden. */
.tabs__pane .tag,
.tabs__pane .sev{padding:3px 10px;font-size:var(--fs-2xs);gap:5px;max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-flex}

/* Raster icons from the supplied sheet. `vertical-align` keeps one sitting on
   the text baseline beside a label instead of lifting the line box, which is
   the usual reason an icon+label pair looks a pixel off. */
.imgicon{display:inline-block;vertical-align:-0.18em;flex:0 0 auto;
  object-fit:contain;image-rendering:auto}

/* ═══════════════════════════════════════════════════════════════════════════
   TASKS DESIGN SYSTEM — typography (§6/§7), applied last so it wins.

   Every rule below quotes the section it implements. Sizes and line-heights are
   taken in PAIRS from the token layer, so a size can never be changed without
   its leading coming with it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* §4: tabular numerals everywhere a number is compared to another number —
   KPI cards, tables, dates, percentages, SLA. Without them the digits change
   width and a column stops being scannable. */
.kpi__text strong, .metric__value, .num, .mono, .progcell__val,
.serieslegend__val, .serieslegend__pct, .pager__btn, .kb__n, .kb__count,
.chart__val, .chart__axis, .donut__mid strong, td .mono {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* §6 page title */
.taskshead__title h1{font-size:var(--fs-2xl);line-height:var(--lh-2xl);
  font-weight:var(--fw-semibold);letter-spacing:var(--track-tight)}
/* §6 page subtitle */
.taskshead__title p{font-size:var(--fs-sm);line-height:var(--lh-sm);
  font-weight:var(--fw-regular);letter-spacing:0;color:var(--text-secondary)}
/* §6 KPI number + label */
.kpi__text strong{font-size:var(--fs-3xl);line-height:var(--lh-3xl);
  font-weight:var(--fw-semibold);letter-spacing:var(--track-tight)}
.kpi__text span{font-size:var(--fs-xs);line-height:18px;font-weight:var(--fw-strong);
  color:var(--text-secondary)}
/* §6 section heading */
.card__head h2,.card__head h3,.assistant__head h2,.section__title{
  font-size:var(--fs-lg);line-height:var(--lh-lg);
  font-weight:var(--fw-semibold);letter-spacing:var(--track-snug)}
/* §6 task title + identifier */
.tabs__pane tbody td .truncate{font-size:var(--fs-sm);line-height:var(--lh-sm);
  font-weight:var(--fw-strong);color:var(--text-primary)}
.tasklink{font-size:var(--fs-2xs);line-height:var(--lh-2xs);
  font-weight:var(--fw-strong);letter-spacing:0}
/* §6 table header — 12px/600, +0.01em, and NOT uppercase */
thead th{font-size:var(--fs-2xs);line-height:var(--lh-2xs);
  font-weight:var(--fw-semibold);letter-spacing:var(--track-label);
  color:var(--text-secondary);text-transform:none}
/* §6 table body */
table{font-size:var(--fs-xs);line-height:var(--lh-xs);font-weight:var(--fw-regular)}
/* §6 button */
.btn{font-size:var(--fs-xs);line-height:var(--lh-xs);font-weight:var(--fw-strong)}
/* §6 small metadata */
.cellstack small,.kb__foot,.kb__who,.deadline__text small,.insights__notice,
.tasksfoot,.tablefoot{font-size:var(--fs-2xs);line-height:var(--lh-2xs);
  font-weight:var(--fw-regular)}
/* §6 tooltip (§40) */
.tip{font-size:var(--fs-2xs);line-height:var(--lh-2xs);font-weight:var(--fw-strong);
  background:var(--text-primary);color:#fff;border-radius:var(--r-xs);padding:8px 10px;
  box-shadow:var(--shadow-md)}

/* §17: one icon language. 1.5 stroke, rounded caps and joins — already how
   icons.js draws — and a single set of sizes so an icon is never arbitrary. */
.tab svg,.btn svg,.chip svg{width:16px;height:16px}
.btn--sm svg{width:14px;height:14px}
.kpi__ico svg{width:22px;height:22px}

/* §13/§14: surface and card. Radius from §15, shadow from §14. */
.card,.kpi,.tablewrap{border-radius:var(--r-lg);border:1px solid var(--border-default);
  background:var(--bg-surface);box-shadow:var(--shadow-sm)}
.btn,.field,.input,.savedview,.chip{border-radius:var(--r-md)}
.modal{border-radius:var(--r-xl)}

/* ═══════════════════════════════════════════════════════════════════════════
   MOTION (§20-§60). Transform and opacity only (§59) — never top/left/width.
   ═══════════════════════════════════════════════════════════════════════════ */

/* §21 page entry, §23 table rows. The delay is set per element by stagger(). */
@keyframes v9-enter{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.enter{animation:v9-enter var(--dur-normal) var(--ease) both;
  animation-delay:var(--enter-delay,0ms)}

/* §20 KPI hover: -2px, shadow up, border warms toward its semantic colour.
   Max scale 1.01 per the spec — so no scale at all here; translate reads as
   more responsive and costs no text re-rasterisation. */
.kpi{transition:transform var(--dur-fast) var(--ease),
     box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease)}
.kpi:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.kpi:hover .kpi__ico[data-sev="critical"]{box-shadow:inset 0 0 0 1px var(--sev-critical-bd)}
.kpi:hover .kpi__ico[data-sev="ok"]{box-shadow:inset 0 0 0 1px var(--sev-ok-bd)}

/* §48 buttons: no movement at rest, background transition on hover, 1px down
   when pressed. No scale — the spec is explicit about not scaling every button. */
.btn{transition:background var(--dur-micro) var(--ease),
     border-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease)}
.btn:active{transform:translateY(1px)}
.btn:focus-visible,.tab:focus-visible,.chip:focus-visible,.savedview:focus-visible{
  outline:2px solid var(--border-focus);outline-offset:2px}

/* §49 tabs: one indicator that slides, rather than each tab fading its own
   underline — the continuity is what tells you where you came from. */
.tabs{position:relative}
.tab{position:relative;transition:color var(--dur-fast) var(--ease);border-bottom-color:transparent}
/* left:0, not auto. With `left` unset the indicator starts at the strip's
   PADDING edge while moveIndicator() translates it by offsetLeft, which is
   measured from the BORDER box — so it sat one padding-width (12px) to the
   right of the tab it marks. */
.tabs__ind{position:absolute;left:0;bottom:-1px;height:2px;background:var(--accent);
  border-radius:2px;transition:transform var(--dur-fast) var(--ease),
  width var(--dur-fast) var(--ease);pointer-events:none}
.tab[aria-selected="true"]{border-bottom-color:transparent;color:var(--accent)}

/* §50 filter chips */
@keyframes v9-chip-in{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}
.savedview[aria-pressed="true"]{animation:v9-chip-in 150ms var(--ease)}

/* §51 search: a border and shadow transition, not a resize of the toolbar. */
.field{transition:border-color var(--dur-micro) var(--ease),box-shadow var(--dur-micro) var(--ease)}
.field:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-quiet)}

/* §24-§26 one-shot row acknowledgement. Fades out on its own; never repeats. */
@keyframes v9-flash{from{background:var(--flash-bg)}to{background:transparent}}
tbody tr[data-flash]{animation:v9-flash 600ms var(--ease) 1}
tbody tr[data-flash="ok"]{--flash-bg:var(--sev-ok-bg)}
tbody tr[data-flash="critical"]{--flash-bg:var(--sev-critical-bg)}
tbody tr[data-flash="medium"]{--flash-bg:var(--sev-medium-bg)}

/* §28 AI panel entry, §29 processing dots — the ONE continuous animation in the
   product, and it exists only while a request is in flight. */
@keyframes v9-panel-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.assistant{animation:v9-panel-in var(--dur-panel) var(--ease) both}
@keyframes v9-dot{0%,80%,100%{opacity:.25}40%{opacity:1}}
.aidots{display:inline-flex;gap:5px;align-items:center}
.aidots i{width:6px;height:6px;border-radius:50%;background:var(--color-ai);
  animation:v9-dot 1.2s var(--ease) infinite}
.aidots i:nth-child(2){animation-delay:.15s} .aidots i:nth-child(3){animation-delay:.3s}
/* §30: no fake streaming — a completed answer fades in as one piece. */
.assistant__a{animation:v9-enter var(--dur-normal) var(--ease) both}

/* §31 AI insight card: purple left border, tinted ground, never styled to look
   like authoritative system state. */
.assistant__a{border-left-color:var(--color-ai);background:var(--color-ai-soft)}
.assistant__ico{background:var(--color-ai)}
.btn--ai{background:var(--color-ai);border-color:var(--color-ai);color:#fff}
.btn--ai:hover{background:#6D28D9;border-color:#6D28D9}
.insights__notice{border-left:3px solid var(--color-ai);background:var(--color-ai-soft)}

/* §39 chart hover: the hovered element holds, the rest step back. */
.chart:hover .chart__bar{opacity:.6;transition:opacity var(--dur-micro) var(--ease)}
.chart .chart__bar:hover{opacity:1}
.donut__seg{transition:opacity var(--dur-micro) var(--ease)}

/* §52 modal, §53 drawer, §64 toast */
@keyframes v9-modal-in{from{opacity:0;transform:translateY(4px) scale(.98)}to{opacity:1;transform:none}}
.modal[data-open="true"]{animation:v9-modal-in 200ms var(--ease) both}
@keyframes v9-drawer-in{from{opacity:0;transform:translateX(24px)}to{opacity:1;transform:none}}
.drawer[data-open="true"]{animation:v9-drawer-in var(--dur-panel) var(--ease) both}
@keyframes v9-toast-in{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.toast{animation:v9-toast-in 200ms var(--ease) both}

/* §61 skeleton: a low-frequency shimmer, not an animated gradient. */
@keyframes v9-skel{0%,100%{opacity:.55}50%{opacity:.85}}

/* §58 REDUCED MOTION. Everything above becomes a no-op; nothing becomes
   unreadable or unreachable, because no state in this UI is carried by motion
   alone. The JS side checks the same query — a stylesheet cannot stop a
   requestAnimationFrame counter. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
  .kpi:hover{transform:none}
  .aidots i{animation:none;opacity:.7}
}

/* §57 mobile: shorter, cheaper motion on a device that is likely slower and
   held in one hand. */
@media (max-width:640px){
  :root{--dur-normal:160ms;--dur-panel:180ms;--dur-count:600ms;--dur-chart:600ms;--stagger:24ms}
  .kpi:hover{transform:none}
}

/* Chip LABELS use the darker text variant; the chip's border and any icon keep
   the §10 identity colour. Two tokens, one hue — see tokens.css for why. */
.sev[data-sev="critical"],.tag[data-sev="critical"]{color:var(--sev-critical-tx)}
.sev[data-sev="high"],    .tag[data-sev="high"]    {color:var(--sev-high-tx)}
.sev[data-sev="medium"],  .tag[data-sev="medium"]  {color:var(--sev-medium-tx)}
.sev[data-sev="low"],     .tag[data-sev="low"]     {color:var(--sev-low-tx)}
.sev[data-sev="ok"],      .tag[data-sev="ok"]      {color:var(--sev-ok-tx)}
.sev[data-sev="info"],    .tag[data-sev="info"]    {color:var(--sev-info-tx)}
.deadline__left[data-sev="critical"]{color:var(--sev-critical-tx)}
.deadline__left[data-sev="medium"]{color:var(--sev-medium-tx)}
.kpi__delta[data-dir="up"]{color:var(--sev-ok-tx)!important}
.kpi__delta[data-dir="down"]{color:var(--sev-critical-tx)!important}
.kpi__warn{color:var(--sev-critical-tx)!important}

/* Icons are CHROME, not content. Nothing about the interaction should reveal
   that they happen to be raster files: no drag ghost, no selection, no
   right-click "Save image as", no iOS long-press save sheet.

   pointer-events:none is the load-bearing one — every pointer event, the
   context menu included, passes through to the control underneath, so the
   browser never treats the icon as an image target at all. It also means an
   icon inside a button can never swallow that button's click. */
.imgicon{
  display:inline-block;vertical-align:-0.22em;flex:0 0 auto;object-fit:contain;
  pointer-events:none;
  user-select:none;-webkit-user-select:none;
  -webkit-user-drag:none;-khtml-user-drag:none;-moz-user-drag:none;
  -webkit-touch-callout:none;
}
/* §17 sizes, one scale. Declared here as well as on the element so an icon
   cannot be sized arbitrarily by a caller passing a stray number. */
/* The sheet's ink covers only ~45% of each canvas, so an 18-20px box was
   drawing a 7-9px glyph — a speck beside a 13px label. scale() grows the
   ARTWORK without changing the box, so nothing reflows and the transparent
   margin that overhangs is exactly that: transparent, and already
   pointer-events:none. Measured after: ~12-13px of visible glyph. */
.tab .imgicon{width:20px;height:20px;transform:scale(1.55)}
.btn .imgicon{width:18px;height:18px;transform:scale(1.55)}
.datechip .imgicon{transform:scale(1.55)}
.chip .imgicon{transform:scale(1.5)}
.assistant__ico .imgicon{width:22px;height:22px}
/* Icon legibility, measured (WCAG 1.4.11 wants 3:1 for graphics). The sheet's
   grey glyphs sat at 2.0–2.8:1 on white and its coloured KPI glyphs at
   1.5–2.9:1 on their tinted tiles; darkening them a step lifts every one past
   3:1 without changing the artwork. On the AI/primary buttons a dark glyph on
   purple or blue was invisible (1.35:1), so there the glyph is turned white. */
/* Specificity ladder, deliberately: (0,1,0) light default → (0,2,0) white
   glyph on coloured buttons → (0,2,1) dark theme undoes the darkening →
   (0,3,1) white glyph again in dark. Each step must outrank the one before. */
.imgicon{filter:brightness(.72) saturate(1.25)}
.btn--ai .imgicon,.btn--primary .imgicon,.assistant__ico .imgicon{filter:brightness(0) invert(1)}
:root[data-theme="dark"] .imgicon{filter:none}
:root[data-theme="dark"] .btn--ai .imgicon,:root[data-theme="dark"] .btn--primary .imgicon,
:root[data-theme="dark"] .assistant__ico .imgicon{filter:brightness(0) invert(1)}
/* Dark theme: a deeper purple under the white glyph and label (7.5:1). */
:root[data-theme="dark"] .btn--ai{background:#6D28D9}
:root[data-theme="dark"] .assistant__ico{background:#6D28D9;border-radius:8px}
/* Icon tile, matched to the reference: a 40px rounded square, radius 10, with
   the glyph at 24px inside it and a pastel ground tinted to the glyph's own
   colour. The tile is what makes six different icons read as one row rather
   than six loose pictures. */
.kpi__ico{width:40px;height:40px;border-radius:10px;flex:0 0 auto;
  display:grid;place-items:center}
/* The tinted 40px square and its radius are deliberately unchanged; only the
   artwork inside grows.

   WHY THE BOX IS BIGGER THAN THE TILE. Measured on the sheet: each PNG's actual
   ink covers only 41-53% of its 128px canvas, the rest being transparent
   margin. So a 34px box drew a ~15px glyph and still read as a dot. Sizing the
   box to 52px puts ~21-28px of real ink on screen; the tile clips the empty
   margin, which costs nothing because there is no artwork out there. The
   honest fix is to re-crop the sheet so a stated size means that many pixels
   of glyph everywhere — that is a change to all 64 files and every call site,
   and is worth doing deliberately rather than in passing. */
/* SIZE WITH transform, NOT width. A 52px image in a 40px grid track overflows
   it, and CSS resolves `center` as `start` for an overflowing item — so every
   glyph sat exactly (52-40)/2 = 6px right and 6px down of the tile's centre.
   Keeping the layout box at 40px means the grid centres it normally, and
   scale() grows it about its own centre, so the glyph is centred by
   construction at any scale. */
.kpi__ico{overflow:hidden}
.kpi__ico .imgicon{width:40px;height:40px;transform:scale(1.3)}

/* ---- Stats as controls -------------------------------------------------- */
/* A KPI card is a button now, so it must read as one: a pointer, a focus ring,
   and a pressed state. It keeps the card's own geometry — the affordance is the
   cursor and the hover lift, not a border that would add noise to six cards. */
.kpi{cursor:pointer;text-align:left;width:100%;font:inherit;color:inherit;
  appearance:none;-webkit-appearance:none}
.kpi:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.kpi:active{transform:translateY(0)}

/* §39: the hovered element holds, its siblings step back. Driven by one class
   on the container rather than a style write per sibling. */
.chart.is-hovering .chart__bar,
.donut.is-hovering .donut__seg{opacity:.55}
.chart .chart__bar.is-active,
.donut .donut__seg.is-active{opacity:1}
.chart__bar:focus-visible,.donut__seg:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

.serieslegend__row.is-pickable{cursor:pointer;border-radius:var(--r-xs);
  padding:3px 6px;margin:-3px -6px;transition:background var(--dur-micro) var(--ease)}
.serieslegend__row.is-pickable:hover{background:var(--bg-sunken)}
.serieslegend__row.is-pickable:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

/* §40 tooltip */
.tip{position:fixed;left:0;top:0;z-index:var(--z-toast);pointer-events:none;
  display:flex;flex-direction:column;gap:2px;max-width:240px;
  background:var(--text-primary);color:#fff;border-radius:var(--r-xs);
  padding:8px 10px;box-shadow:var(--shadow-md);
  font-size:var(--fs-2xs);line-height:var(--lh-2xs);font-weight:var(--fw-strong)}
.tip strong{font-weight:var(--fw-semibold)}
.tip__v{font-variant-numeric:tabular-nums;font-weight:var(--fw-bold)}
.tip small{opacity:.75;font-weight:var(--fw-regular)}

/* The "you are looking at a subset" strip */
.activefilters{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;
  padding:var(--s-3) var(--s-3) 0}
.activefilters__lead{font-size:var(--fs-2xs);color:var(--text-secondary);font-weight:var(--fw-strong)}
.fchip{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 var(--s-2) 0 var(--s-3);
  border-radius:var(--r-full);border:1px solid var(--accent);background:var(--accent-quiet);
  color:var(--sev-info-tx);font-size:var(--fs-2xs);font-weight:var(--fw-semibold);cursor:pointer;
  animation:v9-chip-in 150ms var(--ease)}
.fchip:hover{background:#DBEAFE}
.fchip__x{font-size:14px;line-height:1;opacity:.7}
.fchip:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* Popover for the overflow filters */
.popover{position:fixed;z-index:var(--z-modal);min-width:240px;display:flex;
  flex-direction:column;gap:var(--s-3);padding:var(--s-4);
  background:var(--bg-overlay);border:1px solid var(--border-default);
  border-radius:var(--r-panel);box-shadow:var(--shadow-lg);
  animation:v9-modal-in 180ms var(--ease) both}
.popover__group{display:flex;flex-direction:column;gap:6px}
.popover__label{font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-secondary)}
.popover__empty{margin:0;font-size:var(--fs-xs);color:var(--text-secondary)}


/* §12 gives Pending Review its own colour. It was borrowing the amber of
   Medium/Warning, which made a task awaiting sign-off look like a task at
   risk — two different situations for whoever is triaging the board.
   This is the ONE non-AI use of the purple, and it is what §12 asks for. */
:root{ --sev-review:#7C3AED; --sev-review-bg:#F5F3FF; --sev-review-bd:#DDD6FE;
       --sev-review-tx:#5B21B6; }              /* 7.4:1 on #F5F3FF */
:root[data-theme="dark"]{ --sev-review:#A78BFA; --sev-review-bg:#17102B;
       --sev-review-bd:#2E2150; --sev-review-tx:var(--sev-review); }
.kpi__ico[data-sev="review"]{background:var(--sev-review-bg);color:var(--sev-review)}
.tag[data-sev="review"],.sev[data-sev="review"]{
  background:var(--sev-review-bg);color:var(--sev-review-tx);border-color:var(--sev-review-bd)}
.donut__seg[data-sev="review"]{stroke:var(--sev-review)}
.chart__bar[data-sev="review"]{fill:var(--sev-review)}
.serieslegend__dot[data-sev="review"]{background:var(--sev-review)}
.kpi:hover .kpi__ico[data-sev="review"]{box-shadow:inset 0 0 0 1px var(--sev-review-bd)}
.kpi:hover .kpi__ico[data-sev="high"]{box-shadow:inset 0 0 0 1px var(--sev-high-bd)}
.kpi:hover .kpi__ico[data-sev="info"]{box-shadow:inset 0 0 0 1px var(--sev-info-bd)}
.kpi__ico[data-sev="info"]{background:var(--sev-info-bg);color:var(--sev-info)}

/* §12 grey, for Draft / Cancelled / Archived. These are states with no urgency
   and no outcome; giving them a semantic colour would put them in competition
   with the states that need attention. */
:root{ --sev-muted:#64748B; --sev-muted-bg:#F1F5F9; --sev-muted-bd:#CBD5E1;
       --sev-muted-tx:#334155; }                /* 9.0:1 on #F1F5F9 */
:root[data-theme="dark"]{ --sev-muted:#94A3B8; --sev-muted-bg:#14181F;
       --sev-muted-bd:#2A2A2E; --sev-muted-tx:var(--sev-muted); }
.tag[data-sev="muted"],.sev[data-sev="muted"]{
  background:var(--sev-muted-bg);color:var(--sev-muted-tx);border-color:var(--sev-muted-bd)}
.kpi__ico[data-sev="muted"]{background:var(--sev-muted-bg);color:var(--sev-muted)}
.chart__bar[data-sev="muted"]{fill:var(--sev-muted)}
.donut__seg[data-sev="muted"]{stroke:var(--sev-muted)}
.serieslegend__dot[data-sev="muted"]{background:var(--sev-muted)}

/* Completion gauge: one hue, three steps. The arc is given a round cap so the
   leading edge reads as a drawn stroke rather than a slice cut out of a ring. */
.donut[data-ring] .donut__track{stroke:var(--gauge-track)}
.donut[data-ring] .donut__seg{stroke:var(--gauge-arc);stroke-linecap:round}
.donut[data-ring] .donut__mid strong{color:var(--gauge-text);letter-spacing:-0.02em}
.kpi--wide{background:linear-gradient(180deg,var(--bg-surface),var(--sev-ok-bg))}

/* ═══ Kanban (§37, §54) ══════════════════════════════════════════════════
   Built to the Founder's reference: a filter bar, then a horizontally
   scrolling row of tinted stages.

   THE STAGES ARE DATA (migration 0110), so their colour arrives as a tone name
   on the column and everything inside — the dot, the tint, the header rule —
   reads it from one custom property. Adding a stage adds no CSS. ======== */
.kb{display:flex;flex-direction:column;min-width:0}

/* ---- Filter bar -------------------------------------------------------- */
.kb__bar{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--border-subtle)}
.kb__searchwrap{position:relative;display:flex;align-items:center;flex:1 1 200px;
  max-width:280px;min-width:150px}
.kb__searchwrap img{position:absolute;left:9px;opacity:.5;pointer-events:none}
.kb__search{width:100%;padding-left:30px}
.kb__bar .input--sm{height:32px;font-size:var(--fs-2xs)}
.kb__bar select.input--sm{width:auto;min-width:112px}
.kb__count{font-size:var(--fs-2xs);color:var(--text-tertiary);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.kb__loading{margin:var(--s-5) auto;color:var(--text-tertiary);font-size:var(--fs-xs)}
.kb__fallback{margin:0;padding:var(--s-2) var(--s-4) var(--s-3);
  font-size:var(--fs-2xs);color:var(--sev-medium-tx)}

/* ---- The board scrolls sideways --------------------------------------- */
/* A newsroom can add stages until they no longer fit, and a column that
   shrinks to hold one more is a column nobody can read. The board scrolls
   instead; each stage keeps a fixed, legible width. */
.kb__board{display:flex;align-items:flex-start;gap:var(--s-3);
  padding:var(--s-4);overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity}
.kb__board::-webkit-scrollbar{height:8px}
.kb__board::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:4px}

.kb__col{--kbtone:var(--text-tertiary);
  flex:0 0 272px;width:272px;display:flex;flex-direction:column;
  border:1px solid var(--border-subtle);border-radius:var(--r-lg);
  background:var(--bg-sunken);
  background:color-mix(in srgb,var(--kbtone) 7%,var(--bg-surface));
  scroll-snap-align:start;max-height:min(72vh,780px)}
.kb__col[data-tone="critical"]{--kbtone:var(--sev-critical)}
.kb__col[data-tone="high"]    {--kbtone:var(--sev-high,var(--sev-critical))}
.kb__col[data-tone="medium"]  {--kbtone:var(--sev-medium)}
.kb__col[data-tone="low"]     {--kbtone:var(--sev-low,var(--sev-ok))}
.kb__col[data-tone="ok"]      {--kbtone:var(--sev-ok)}
.kb__col[data-tone="info"]    {--kbtone:var(--sev-info,var(--accent))}
.kb__col[data-tone="review"]  {--kbtone:var(--sev-review,var(--color-ai))}
.kb__col[data-tone="muted"]   {--kbtone:var(--sev-muted)}
.kb__board.is-dragging .kb__col.is-blocked{opacity:.34}
.kb__board.is-dragging .kb__col:not(.is-blocked){border-color:var(--kbtone)}

.kb__head{display:flex;align-items:center;gap:8px;padding:10px var(--s-3);
  border-bottom:1px solid color-mix(in srgb,var(--kbtone) 26%,transparent)}
.kb__dot{width:9px;height:9px;flex:0 0 auto;border-radius:50%;background:var(--kbtone)}
.kb__name{flex:1 1 auto;min-width:0;font-size:var(--fs-xs);
  font-weight:var(--fw-semibold);color:var(--text-primary)}
.kb__n{flex:0 0 auto;min-width:20px;height:19px;padding:0 6px;border-radius:10px;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--bg-surface);border:1px solid var(--border-subtle);
  font-size:10px;font-weight:var(--fw-bold);color:var(--text-secondary);
  font-variant-numeric:tabular-nums}
.kb__menu{flex:0 0 auto;width:22px;height:22px;border:0;background:none;cursor:pointer;
  border-radius:var(--r-xs);color:var(--text-secondary);font-size:13px;line-height:1;
  letter-spacing:-1px}
.kb__menu:hover{background:var(--bg-surface);color:var(--text-primary)}
.kb__menu:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

.kb__lane{flex:1 1 auto;display:flex;flex-direction:column;gap:var(--s-2);
  padding:var(--s-2);overflow-y:auto;min-height:72px}
.kb__lane.is-over{background:color-mix(in srgb,var(--kbtone) 14%,transparent);
  border-radius:var(--r-md);outline:2px dashed var(--kbtone);outline-offset:-3px}
/* A column that will refuse this card says so while the card is still in hand,
   rather than swallowing the drop and looking broken. */
.kb__col.is-refusing{outline:2px solid var(--sev-critical);outline-offset:-1px}
.kb__col.is-refusing .kb__head{background:var(--sev-critical-bg)}
.kb__empty{margin:auto;padding:var(--s-3);text-align:center;
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.kb__add{margin:0 var(--s-2) var(--s-2);padding:7px;border:0;border-radius:var(--r-md);
  background:none;cursor:pointer;font:inherit;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);color:var(--text-secondary);text-align:left}
.kb__add:hover{background:var(--bg-surface);color:var(--text-link)}
.kb__add:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}

/* ---- A card ------------------------------------------------------------ */
.kb__card{display:flex;flex-direction:column;gap:7px;text-align:left;width:100%;
  padding:10px 11px;border:1px solid var(--border-subtle);border-radius:var(--r-md);
  background:var(--bg-surface);cursor:grab;
  box-shadow:0 1px 2px rgb(0 0 0 / .04);
  transition:box-shadow var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.kb__card:hover{box-shadow:var(--shadow-md)}
.kb__card:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
/* §54: elevation and a 1.01 lift while dragging — physical, not bouncy. */
.kb__card.is-dragging{opacity:.45;cursor:grabbing;transform:scale(1.01);
  box-shadow:var(--shadow-lg)}
/* Move arrows. Present on every card in the same place, and faint until the
   card is hovered or something inside it has focus — so they are discoverable
   without turning a column of cards into a wall of chevrons. A keyboard user
   gets them the moment they tab in. */
.kb__arrows{display:flex;justify-content:space-between;gap:var(--s-2);
  margin:-2px -3px 1px;opacity:.28;
  transition:opacity var(--dur-fast) var(--ease)}
.kb__card:hover .kb__arrows,
.kb__card:focus-within .kb__arrows{opacity:1}
.kb__arrow{width:22px;height:20px;border:0;border-radius:var(--r-xs);cursor:pointer;
  background:var(--bg-sunken);color:var(--text-secondary);
  font:inherit;font-size:14px;line-height:1;padding:0}
.kb__arrow:hover:not([disabled]){background:var(--accent-quiet);color:var(--sev-info-tx)}
.kb__arrow:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px;opacity:1}
.kb__arrow[disabled]{opacity:.3;cursor:not-allowed}
@media (prefers-reduced-motion: reduce){ .kb__arrows{transition:none} }
/* Touch has no hover, so there is nothing to reveal — show them outright. */
@media (hover: none){ .kb__arrows{opacity:1} }

/* The owner is a control, not a caption. */
.kb__who--btn{border:0;background:none;padding:2px 4px;margin:-2px -4px;
  border-radius:var(--r-xs);cursor:pointer;font:inherit;text-align:left}
.kb__who--btn:hover{background:var(--bg-sunken)}
.kb__who--btn:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

/* Several faces, overlapped so the group reads as one object and the width
   does not grow linearly with the number of people. */
.kb__faces{display:inline-flex;align-items:center;flex:0 0 auto}
.kb__faces > *{margin-left:-7px;box-shadow:0 0 0 2px var(--bg-surface)}
.kb__faces > *:first-child{margin-left:0}
.kb__av--more{width:24px;height:24px;display:inline-flex;align-items:center;
  justify-content:center;border-radius:50%;background:var(--bg-sunken);
  border:1px solid var(--border-subtle);font-size:9px;font-weight:var(--fw-bold);
  color:var(--text-secondary)}

/* A selected holder, and the one who leads. */
.kb__person[data-on]{background:var(--accent-quiet)}
.kb__person[data-lead]{box-shadow:inset 0 0 0 1.5px var(--accent)}
.kb__lead{margin-left:auto;width:26px;height:26px;flex:0 0 auto;border:0;
  border-radius:50%;background:none;cursor:pointer;font-size:13px;line-height:1;
  color:var(--border-default)}
.kb__person[data-on] .kb__lead{color:var(--text-tertiary)}
.kb__person[data-on] .kb__lead:hover{color:var(--sev-medium);background:var(--bg-sunken)}
.kb__person[data-lead] .kb__lead{color:var(--sev-medium)}
.kb__lead:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.kb__chosen{font-weight:var(--fw-regular);color:var(--text-tertiary);
  text-transform:none;letter-spacing:0}

.kb__unassign{margin-top:var(--s-2);width:100%;padding:9px;border-radius:var(--r-md);
  cursor:pointer;font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  border:1px solid var(--border-default);background:none;color:var(--text-secondary)}
.kb__unassign:hover{border-color:var(--sev-critical-bd);color:var(--sev-critical-tx);
  background:var(--sev-critical-bg)}
.kb__unassign:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

.kb__cardtitle{margin:0;font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary);line-height:1.35;
  overflow-wrap:anywhere}
.kb__desc{margin:0;font-size:var(--fs-2xs);color:var(--text-tertiary);line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
/* Where inside the stage. A quiet control: it is frequent, small, and must not
   compete with the title for attention. */
.kb__substage{width:100%;height:26px;padding:0 6px;border-radius:var(--r-xs);
  border:1px dashed var(--border-default);background:var(--bg-sunken);
  color:var(--text-secondary);font:inherit;font-size:var(--fs-2xs);cursor:pointer}
.kb__substage:hover{border-style:solid;color:var(--text-primary)}
.kb__substage:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

/* The sub-stage editor inside the stage drawer. */
/* ---- New Task form -------------------------------------------------- */
.nt__assign{display:flex;flex-direction:column;gap:8px}
.nt__chips{display:flex;flex-wrap:wrap;gap:6px}
.nt__chip{display:inline-flex;align-items:center;gap:6px;padding:3px 4px 3px 3px;
  border-radius:var(--r-pill,999px);background:var(--bg-sunken);
  border:1px solid var(--border-subtle);font-size:var(--fs-2xs);color:var(--text-primary)}
.nt__chipx{width:20px;height:20px;border:0;border-radius:50%;cursor:pointer;
  background:none;color:var(--text-tertiary);font-size:11px;line-height:1}
.nt__chipx:hover{background:var(--sev-critical-bg);color:var(--sev-critical-tx)}
.nt__chipx:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

/* Extra steps read as part of the sequence, not as a detached list. */
.nt__stepslead{margin:var(--s-2) 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.nt__steps{display:flex;flex-direction:column;gap:4px}
.nt__steps:empty{display:none}
.nt__step{display:flex;align-items:center;gap:8px;padding:6px 8px;
  border-radius:var(--r-md);background:var(--bg-sunken)}
/* The step's place in the sequence, which is the thing being edited: a number
   that moves when something above it is left out says more than a bullet. */
.nt__stepno{flex:0 0 auto;min-width:18px;text-align:right;font-size:var(--fs-2xs);
  color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.nt__steptag{flex:0 0 auto;font-size:10px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--text-tertiary)}
.nt__step[data-own]{background:var(--accent-quiet)}
.nt__step[data-own] .nt__stepno{color:var(--text-link)}
/* A step left out stays on screen and reversible — one that vanished would make
   a mis-click unrecoverable without starting the form again. */
.nt__step--off{opacity:.62}
.nt__step--off .nt__steptxt{text-decoration:line-through}
.nt__steplock{flex:0 0 auto;font-size:11px;opacity:.5;cursor:default}
.nt__stepcount{margin:6px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.nt__steptxt{flex:1 1 auto;min-width:0;font-size:var(--fs-2xs);color:var(--text-primary)}
.nt__steprow{display:flex;gap:var(--s-2);align-items:center}
.nt__steprow .input{flex:1 1 auto;min-width:0}

.nt__depts{display:flex;flex-wrap:wrap;gap:6px;min-height:34px;align-items:center;
  padding:6px 8px;border-radius:var(--r-md);background:var(--bg-sunken);
  border:1px dashed var(--border-default)}
.nt__dept{padding:3px 9px;border-radius:var(--r-pill,999px);font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);background:var(--accent-quiet);color:var(--sev-info-tx)}
.nt__deptnone{font-size:var(--fs-2xs);color:var(--text-tertiary)}
/* Three names fit a line at every width this drawer is used at. The rest go
   behind one control, so the field keeps its height instead of growing into a
   block that buries what comes after it. */
.nt__depts{position:relative}
.nt__deptmore{padding:3px 9px;border-radius:var(--r-pill,999px);font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);background:transparent;color:var(--text-link);
  border:1px dashed var(--border-strong);cursor:pointer}
.nt__deptmore:hover{background:var(--accent-quiet)}
.nt__deptpop{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:30;
  display:flex;flex-wrap:wrap;gap:6px;padding:8px;border-radius:var(--r-md);
  background:var(--bg-elevated);border:1px solid var(--border-default);
  box-shadow:var(--shadow-lg,0 10px 30px rgba(0,0,0,.18))}


.kb__subs{display:flex;flex-direction:column;gap:6px}
.kb__sub{display:flex;align-items:center;gap:8px}
.kb__sub .input{flex:1 1 auto;min-width:0}
.kb__subdot{width:7px;height:7px;flex:0 0 auto;border-radius:50%;
  background:var(--kbtone,var(--text-tertiary))}
.kb__subdot--new{background:none;border:1.5px dashed var(--border-default)}
.kb__subdel{color:var(--text-tertiary);padding-inline:9px}
.kb__subdel:hover{color:var(--sev-critical-tx);border-color:var(--sev-critical-bd)}

.kb__tags{display:flex;flex-wrap:wrap;gap:5px}
.kb__tag{padding:2px 8px;border-radius:var(--r-pill,999px);font-size:10px;
  font-weight:var(--fw-semibold);white-space:nowrap;max-width:100%;
  overflow:hidden;text-overflow:ellipsis;
  background:color-mix(in srgb,var(--tone) 15%,var(--bg-surface));
  color:color-mix(in srgb,var(--tone) 55%,var(--text-primary))}
.kb__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);
  min-width:0}
.kb__who{display:inline-flex;align-items:center;gap:6px;min-width:0;
  font-size:var(--fs-2xs);color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.kb__av{flex:0 0 auto;border-radius:50%;position:relative;overflow:hidden;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--bg-sunken);border:1px solid var(--border-subtle);
  font-size:9px;font-weight:var(--fw-bold);color:var(--text-secondary)}
/* The photo covers the initials when it loads; if it fails it is removed and
   the initials are simply still there underneath. */
.kb__av img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.kb__av--none{border-style:dashed;color:var(--text-tertiary)}
/* The profile avatar takes a photo the same way the board's does. */
/* A DRAWN glyph next to SHEET icons. `.btn svg` caps svgs at 16px, but the
   sheet's PNGs render about 28px with roughly half of that as visible ink —
   so a 16px full-bleed svg beside them looks like a different size class.
   Sized to match the INK, not the box. */
.glyph{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}
.btn .glyph svg,.glyph svg{width:20px;height:20px}
.avatar--photo{position:relative;overflow:hidden}
.avatar--photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.kb__name2{min-width:0;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  color:var(--text-secondary)}
.kb__when{display:flex;justify-content:flex-end;font-size:var(--fs-2xs);
  color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.kb__when small[data-over]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}
.kb__done{width:20px;height:20px;flex:0 0 auto;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  /* --sev-ok is green-600; a white tick on it measures 3.3:1, which passes as a
     graphic but not as the glyph it actually is. green-800 takes it to 7.4. */
  background:var(--sev-low-tx);color:#fff;font-size:12px;font-weight:var(--fw-bold)}
/* A decision waiting on the reader gets buttons; everyone else gets a line of
   text. The difference is deliberate — a card full of buttons nobody can press
   trains people to ignore them. */
.kb__accept{display:flex;flex-direction:column;gap:6px;padding:7px 8px;
  border-radius:var(--r-sm,5px);background:var(--accent-quiet);
  border:1px solid color-mix(in srgb,var(--accent) 34%,transparent)}
.kb__accept-lead{font-size:10px;font-weight:var(--fw-bold);letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--accent)}
.kb__accept-btns{display:flex;gap:6px}
.kb__accept-yes,.kb__accept-no{flex:1 1 auto;padding:5px 8px;border-radius:var(--r-xs);
  cursor:pointer;font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-semibold)}
.kb__accept-yes{border:0;background:var(--accent);color:var(--text-inverse)}
.kb__accept-yes:hover{filter:brightness(1.08)}
.kb__accept-no{border:1px solid var(--border-default);background:var(--bg-surface);
  color:var(--text-secondary)}
.kb__accept-no:hover{border-color:var(--sev-critical-bd);color:var(--sev-critical-tx)}
.kb__accept-yes:focus-visible,.kb__accept-no:focus-visible{
  outline:2px solid var(--border-focus);outline-offset:1px}
.kb__waiting{display:block;font-size:10px;color:var(--sev-medium-tx);
  background:var(--sev-medium-bg);border:1px solid var(--sev-medium-bd);
  border-radius:var(--r-xs);padding:3px 7px}

.kb__prog{display:flex;align-items:center;gap:8px}
.kb__track{flex:1 1 auto;height:5px;border-radius:3px;background:var(--bg-sunken);
  overflow:hidden}
.kb__track i{display:block;height:100%;border-radius:3px;background:var(--accent)}
.kb__prog small{flex:0 0 auto;font-size:10px;color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}

/* ---- Add-a-stage rail -------------------------------------------------- */
.kb__addcol{flex:0 0 200px;align-self:stretch;display:flex}
.kb__addcolbtn{flex:1 1 auto;min-height:110px;border:1.5px dashed var(--border-default);
  border-radius:var(--r-lg);background:none;cursor:pointer;font:inherit;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-tertiary)}
.kb__addcolbtn:hover{border-color:var(--accent);color:var(--sev-info-tx);
  background:var(--accent-quiet)}
.kb__addcolbtn:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* ---- The stage editor -------------------------------------------------- */
.kb__swatches{display:flex;flex-wrap:wrap;gap:8px}
.kb__swatch{width:30px;height:30px;border-radius:50%;cursor:pointer;
  border:2px solid transparent;background:var(--sev-muted)}
.kb__swatch[data-sev="critical"]{background:var(--sev-critical)}
.kb__swatch[data-sev="high"]    {background:var(--sev-high,var(--sev-critical))}
.kb__swatch[data-sev="medium"]  {background:var(--sev-medium)}
.kb__swatch[data-sev="low"]     {background:var(--sev-low,var(--sev-ok))}
.kb__swatch[data-sev="ok"]      {background:var(--sev-ok)}
.kb__swatch[data-sev="info"]    {background:var(--sev-info,var(--accent))}
.kb__swatch[data-sev="review"]  {background:var(--sev-review,var(--color-ai))}
.kb__swatch[data-sev="muted"]   {background:var(--sev-muted)}
.kb__swatch.is-on{border-color:var(--text-primary);
  box-shadow:0 0 0 2px var(--bg-surface) inset}
.kb__swatch:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

.kb__statuses{display:flex;flex-direction:column;gap:1px;max-height:260px;overflow-y:auto;
  border:1px solid var(--border-subtle);border-radius:var(--r-md);padding:4px}
.kb__statusrow{display:flex;align-items:center;gap:9px;padding:6px 8px;
  border-radius:var(--r-xs);cursor:pointer;font-size:var(--fs-2xs);
  color:var(--text-primary)}
.kb__statusrow:hover{background:var(--bg-sunken)}
.kb__statusrow.is-taken{cursor:not-allowed;color:var(--text-tertiary)}
.kb__taken{margin-left:auto;font-size:10px;color:var(--text-tertiary);white-space:nowrap}
/* ---- Who owns this? ---------------------------------------------------- */
.kb__ownerlead{margin:0 0 var(--s-2);font-size:var(--fs-xs);color:var(--text-secondary)}
.kb__self{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:11px 12px;border-radius:var(--r-md);cursor:pointer;font:inherit;
  border:1.5px solid var(--accent);background:var(--accent-quiet);color:var(--text-primary)}
.kb__self:hover{background:color-mix(in srgb,var(--accent) 16%,var(--bg-surface))}
.kb__self:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.kb__people{display:flex;flex-direction:column;gap:2px;max-height:280px;overflow-y:auto}
.kb__person{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:8px 10px;border:0;border-radius:var(--r-md);background:none;cursor:pointer;
  font:inherit;color:var(--text-primary)}
.kb__person:hover{background:var(--bg-sunken)}
.kb__person:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.kb__persontxt{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.kb__persontxt b{font-size:var(--fs-xs);font-weight:var(--fw-semibold)}
.kb__persontxt small{font-size:var(--fs-2xs);color:var(--text-secondary)}
.kb__moverow{display:flex;gap:var(--s-2)}
.fld__hint{margin:0 0 4px;font-size:var(--fs-2xs);color:var(--text-tertiary)}

@media (max-width:640px){
  .kb__col{flex-basis:248px;width:248px}
  .kb__bar{padding:var(--s-2) var(--s-3)}
  .kb__searchwrap{max-width:none}
}

/* ═══ Calendar (§36, §55) ═══════════════════════════════════════════════ */
.cal{padding:var(--s-4)}
.cal__bar{display:flex;align-items:center;gap:var(--s-2);margin-bottom:var(--s-3);flex-wrap:wrap}
.cal__title{margin:0;font-size:var(--fs-lg);font-weight:var(--fw-semibold);
  letter-spacing:var(--track-snug);min-width:170px}
.cal__count{font-size:var(--fs-2xs);color:var(--text-secondary)}
.cal__grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:1px;
  background:var(--border-subtle);border:1px solid var(--border-subtle);
  border-radius:var(--r-lg);overflow:hidden}
/* §55: a slide only, and only in the direction the month actually moved. */
@keyframes cal-next{from{opacity:0;transform:translateX(12px)}to{opacity:1;transform:none}}
@keyframes cal-prev{from{opacity:0;transform:translateX(-12px)}to{opacity:1;transform:none}}
.cal__grid[data-dir="next"]{animation:cal-next var(--dur-panel) var(--ease) both}
.cal__grid[data-dir="prev"]{animation:cal-prev var(--dur-panel) var(--ease) both}
.cal__dow{background:var(--bg-sunken);padding:8px;text-align:center;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-secondary)}
.cal__cell{background:var(--bg-surface);min-height:108px;padding:6px;
  display:flex;flex-direction:column;gap:3px}
.cal__cell.is-pad{background:var(--bg-sunken)}
.cal__cell[data-today]{box-shadow:inset 0 0 0 2px var(--accent)}
/* --text-inverse, never a literal #fff: the accent inverts between themes
   (blue on light, near-white on dark), so hardcoded white is invisible on the
   dark theme's own accent. Same mistake the --accent-on fallback made earlier;
   the token exists precisely to stop it. */
.cal__cell[data-today] .cal__num{background:var(--accent);color:var(--text-inverse)}
.cal__num{align-self:flex-start;min-width:22px;height:22px;display:grid;place-items:center;
  border-radius:50%;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  color:var(--text-secondary);font-variant-numeric:tabular-nums}
.cal__task{display:flex;align-items:center;gap:5px;width:100%;text-align:left;
  padding:3px 6px;border:0;border-radius:var(--r-xs);background:var(--bg-sunken);
  font-size:var(--fs-2xs);color:var(--text-primary);cursor:pointer;min-width:0}
.cal__task:hover{background:var(--accent-quiet)}
.cal__taskdot{width:6px;height:6px;border-radius:50%;flex:0 0 auto;background:var(--text-tertiary)}
.cal__task[data-sev="critical"] .cal__taskdot{background:var(--sev-critical)}
.cal__task[data-sev="high"] .cal__taskdot{background:var(--sev-high)}
.cal__task[data-sev="medium"] .cal__taskdot{background:var(--sev-medium)}
.cal__task[data-sev="low"] .cal__taskdot{background:var(--sev-low)}
.cal__more{align-self:flex-start;border:0;background:none;cursor:pointer;padding:2px 6px;
  font-size:var(--fs-2xs);color:var(--text-link);font-weight:var(--fw-semibold)}
.daylist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.daylist__row{display:flex;align-items:center;gap:var(--s-3);width:100%;text-align:left;
  padding:8px var(--s-3);border:0;border-radius:var(--r-md);background:transparent;cursor:pointer}
.daylist__row:hover{background:var(--bg-sunken)}


/* Upcoming Deadlines stands BESIDE ANALYTICS and nowhere else. On every other
   tab it flows below the card: still one scroll away, no longer taking 320px
   from a month grid or a Gantt that needs the width to be readable at all. */
.tasksmain[data-rail="below"]{grid-template-columns:minmax(0,1fr)}
.tasksmain[data-rail="below"] .tasksmain__rail{grid-column:1}

/* ═══ Calendar, to the Founder's reference (§36, §55) ════════════════════
   A left rail carrying a mini month and the switchable calendars, a week of
   hour slots as the main pane, and the view's controls at the right-hand end
   of the tab row.

   THE CALENDAR PALETTE IS NOT THE SEVERITY PALETTE. These seven colours say
   which KIND of work a task is; red here means "compliance", not "breached".
   Mixing the two would make the calendar argue with every other view in the
   console, so they are separate token sets and never share a variable. ==== */
:root{
  --calx-c1:#3B82F6; --calx-c2:#8B5CF6; --calx-c3:#F43F5E; --calx-c4:#F59E0B;
  --calx-c5:#10B981; --calx-c6:#06B6D4; --calx-c7:#94A3B8;
}
:root[data-theme="dark"]{
  --calx-c1:#60A5FA; --calx-c2:#A78BFA; --calx-c3:#FB7185; --calx-c4:#FBBF24;
  --calx-c5:#34D399; --calx-c6:#22D3EE; --calx-c7:#94A3B8;
}
[data-tone="c1"]{--tone:var(--calx-c1)} [data-tone="c2"]{--tone:var(--calx-c2)}
[data-tone="c3"]{--tone:var(--calx-c3)} [data-tone="c4"]{--tone:var(--calx-c4)}
[data-tone="c5"]{--tone:var(--calx-c5)} [data-tone="c6"]{--tone:var(--calx-c6)}
[data-tone="c7"]{--tone:var(--calx-c7)}

/* ---- The tab row now carries the active view's controls ---------------- */
.tabrow{display:flex;align-items:center;gap:var(--s-3);min-width:0;
  border-bottom:1px solid var(--border-subtle)}
/* The strip's own bottom border and margin move to the ROW, or the row's rule
   would sit 16px below a second one. */
.tabrow .tabs{flex:1 1 auto;min-width:0;border-bottom:0;margin-bottom:0}
.tabs__actions{flex:0 0 auto;display:flex;align-items:center;
  padding-right:var(--s-3)}
.tabs__actions:empty{display:none}

.calx__controls{display:flex;align-items:center;gap:var(--s-2)}
.calx__step{display:flex;align-items:center;gap:2px}
.calx__nav{width:30px;height:30px;flex:0 0 auto;border:1px solid var(--border-default);
  border-radius:var(--r-md);background:var(--bg-surface);color:var(--text-secondary);
  cursor:pointer;font-size:16px;line-height:1;display:flex;align-items:center;
  justify-content:center}
.calx__nav:hover{background:var(--bg-sunken);color:var(--text-primary)}
.calx__nav:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}

/* A segmented control: one pill, the active segment filled. */
.seg{display:inline-flex;padding:2px;border-radius:var(--r-md);
  background:var(--bg-sunken);border:1px solid var(--border-subtle)}
.seg__btn{border:0;background:none;cursor:pointer;font:inherit;
  padding:4px 14px;border-radius:calc(var(--r-md) - 2px);
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-secondary)}
.seg__btn:hover{color:var(--text-primary)}
.seg__btn:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
/* --accent is #2563EB in light but #FAFAFA in dark, so a hard-coded white
   foreground is invisible in one of the two themes. --text-inverse is the
   token for text sitting on an accent fill and flips with it. */
.seg__btn.is-on{background:var(--accent);color:var(--text-inverse)}

/* ---- Shell ------------------------------------------------------------- */
.calx{display:grid;grid-template-columns:224px minmax(0,1fr);align-items:start}
.calx__rail{padding:var(--s-4);border-right:1px solid var(--border-subtle);
  display:flex;flex-direction:column;gap:var(--s-4);min-width:0}
.calx__pane{min-width:0}
.calx__railtitle{margin:var(--s-2) 0 0;font-size:var(--fs-sm);
  font-weight:var(--fw-semibold);color:var(--text-primary)}

/* ---- The mini month ---------------------------------------------------- */
.mini{display:flex;flex-direction:column;gap:var(--s-2)}
.mini__bar{display:flex;align-items:center;gap:4px}
.mini__title{flex:1 1 auto;margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mini__navs{flex:0 0 auto;display:flex;gap:1px}
.mini__nav{width:24px;height:24px;border:0;border-radius:var(--r-xs);
  background:none;color:var(--text-tertiary);cursor:pointer;font-size:15px;line-height:1}
.mini__nav:hover{background:var(--bg-sunken);color:var(--text-primary)}
.mini__nav:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.mini__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.mini__dow{display:flex;align-items:center;justify-content:center;height:24px;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-tertiary)}
.mini__day{position:relative;height:28px;border:0;border-radius:50%;cursor:pointer;
  background:none;color:var(--text-primary);font:inherit;font-size:var(--fs-2xs);
  font-variant-numeric:tabular-nums;display:flex;align-items:center;justify-content:center}
.mini__day:hover{background:var(--bg-sunken)}
.mini__day:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.mini__day[data-out]{color:var(--text-tertiary);opacity:.55}
.mini__day[data-today]{font-weight:var(--fw-bold);color:var(--accent)}
.mini__day[data-on]{background:var(--accent);color:var(--text-inverse);
  font-weight:var(--fw-semibold)}
.mini__dot{position:absolute;bottom:2px;left:50%;transform:translateX(-50%);
  width:3px;height:3px;border-radius:50%;background:currentColor;opacity:.7}

/* ---- The calendar switches -------------------------------------------- */
.callist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.callist__row{display:flex;align-items:center;gap:10px;padding:6px 4px;
  border-radius:var(--r-xs);cursor:pointer;font-size:var(--fs-xs);min-width:0}
.callist__row:hover{background:var(--bg-sunken)}
.callist__row--mine{margin-top:var(--s-2);padding-top:var(--s-3);
  border-top:1px solid var(--border-subtle);border-radius:0}
.callist__box{width:17px;height:17px;flex:0 0 auto;margin:0;cursor:pointer;
  accent-color:var(--tone,var(--accent))}
.callist__dot{width:9px;height:9px;flex:0 0 auto;border-radius:50%;background:var(--tone);
  opacity:.75}
.callist__name{flex:1 1 auto;min-width:0;color:var(--text-primary)}

/* ---- The time grid (day and week) ------------------------------------- */
/* A DAY COLUMN HAS A FLOOR AND THE GRID SCROLLS PAST IT. Seven days divided
   into whatever is left after the nav and the rail gives 90px a day on a
   laptop, and a 90px column renders "Anchor audit chain" as "Anc". Below the
   floor the week scrolls sideways instead of shrinking; above it the columns
   share the extra space evenly. */
.tg{display:flex;flex-direction:column;min-width:0;--tg-daymin:136px;
  /* ONE scroller for BOTH axes. With the vertical scroll on .tg__body, the
     hour ruler's `position:sticky;left:0` had nothing to stick against —
     sticky resolves against the nearest scrolling ancestor, and that one
     scrolls only vertically — so the ruler slid away with the week. */
  overflow:auto;max-height:clamp(420px,58vh,660px);overscroll-behavior:contain}
.tg__gutterhead{position:sticky;left:0;z-index:6;background:var(--bg-surface);
  border-right:1px solid var(--border-subtle)}
.tg__head{display:grid;grid-template-columns:62px repeat(var(--tg-cols),minmax(0,1fr));
  width:max(100%,calc(62px + var(--tg-cols) * var(--tg-daymin)));
  position:sticky;top:0;z-index:5;background:var(--bg-surface);
  border-bottom:1px solid var(--border-default)}
.tg__dayhead{display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:10px 2px 12px;border:0;border-left:1px solid var(--border-subtle);
  background:none;cursor:pointer;font:inherit}
.tg__dayhead:hover{background:var(--bg-sunken)}
.tg__dayhead:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.tg__dow{font-size:var(--fs-2xs);font-weight:var(--fw-medium,500);color:var(--text-tertiary)}
.tg__dnum{font-size:var(--fs-lg);font-weight:var(--fw-bold);color:var(--text-primary);
  font-variant-numeric:tabular-nums;line-height:1.1}
/* Today is a tinted COLUMN, header included — the reference marks the day by
   lighting its whole strip rather than by ringing the number. */
.tg__dayhead[data-today]{background:var(--accent-quiet)}
.tg__dayhead[data-today] .tg__dow{color:var(--accent)}

/* An hour is a FIXED height (--tg-h is the whole span) and the body scrolls.
   A percentage-sized hour collapses to nothing the moment the day is long. */
.tg__body{display:grid;grid-template-columns:62px repeat(var(--tg-cols),minmax(0,1fr));
  width:max(100%,calc(62px + var(--tg-cols) * var(--tg-daymin)));
  align-items:start}
.tg__gutter{position:sticky;left:0;z-index:4;height:var(--tg-h);
  background:var(--bg-surface);border-right:1px solid var(--border-subtle)}
.tg__hour{position:absolute;right:10px;transform:translateY(-50%);white-space:nowrap;
  font-size:var(--fs-2xs);color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.tg__col{position:relative;border-left:1px solid var(--border-subtle);min-width:0;
  height:var(--tg-h)}
.tg__col[data-today]{background:var(--accent-quiet)}
.tg__line{position:absolute;left:0;right:0;height:1px;background:var(--border-subtle);
  pointer-events:none}
.tg__now{position:absolute;left:0;right:0;height:2px;background:var(--sev-critical);
  pointer-events:none;z-index:2}
.tg__now::before{content:'';position:absolute;left:0;top:-3px;width:8px;height:8px;
  border-radius:50%;background:var(--sev-critical)}

/* A block is a tinted card with a thick leading bar in its calendar's colour:
   the time on one line, the title on the next. */
.tg__ev{position:absolute;margin-left:4px;overflow:hidden;cursor:pointer;text-align:left;
  display:flex;flex-direction:column;gap:2px;padding:5px 8px;border:0;font:inherit;
  border-radius:var(--r-sm,5px);border-left:4px solid var(--tone);
  background:var(--bg-sunken);
  background:color-mix(in srgb,var(--tone) 14%,var(--bg-surface));
  color:var(--text-primary);z-index:1}
.tg__ev:hover{background:color-mix(in srgb,var(--tone) 24%,var(--bg-surface))}
.tg__ev:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px;z-index:3}
/* A deadline marker is not a booked hour, and must not look like one. */
.tg__ev[data-marker]{border-left-style:dashed}
.tg__ev[data-late]{box-shadow:inset 0 0 0 1px var(--sev-critical)}
.tg__evtime{font-size:var(--fs-2xs);font-weight:var(--fw-medium,500);
  /* Half the tone, half the text colour. At 72% the lighter tones (emerald,
     amber) fell to 3.7:1 on their own tinted ground. */
  color:color-mix(in srgb,var(--tone) 50%,var(--text-primary));
  font-variant-numeric:tabular-nums;white-space:nowrap}
.tg__evtitle{font-size:var(--fs-xs);font-weight:var(--fw-semibold);line-height:1.3;
  overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}

/* ---- The month grid, in the calendar palette -------------------------- */
.cal__num{border:0;background:none;cursor:pointer;padding:1px 5px;border-radius:var(--r-xs);
  font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  color:var(--text-secondary);font-variant-numeric:tabular-nums;align-self:flex-start}
.cal__num:hover{background:var(--bg-sunken);color:var(--text-primary)}
.cal__num:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.cal__task[data-tone]{background:color-mix(in srgb,var(--tone) 12%,var(--bg-surface));
  border-left:3px solid var(--tone);border-radius:var(--r-xs)}
.cal__task[data-tone]:hover{background:color-mix(in srgb,var(--tone) 22%,var(--bg-surface))}
.cal__task[data-tone] .cal__taskdot{background:var(--tone)}
.cal__task[data-late]{box-shadow:inset 0 0 0 1px var(--sev-critical)}

@media (max-width:980px){
  /* The rail is a companion, not the view. Below this width it would take a
     third of the screen from the grid it exists to describe. */
  .calx{grid-template-columns:minmax(0,1fr)}
  .calx__rail{flex-direction:row;flex-wrap:wrap;align-items:flex-start;
    border-right:0;border-bottom:1px solid var(--border-subtle)}
  .mini{flex:0 0 204px}
  .calx__railtitle{flex-basis:100%}
  .callist{flex:1 1 200px}
}
@media (max-width:760px){
  /* The controls drop out of the tab row before the tab row starts scrolling
     the tabs themselves off the screen. */
  .tabrow{flex-wrap:wrap}
  .tabs__actions{width:100%;padding:0 var(--s-3) var(--s-2);justify-content:flex-end}
  .tg{max-height:clamp(320px,46vh,460px);--tg-daymin:118px}
}

/* ═══ Timeline / Gantt, rebuilt (§39, §56) ═══════════════════════════════
   A task table on the left and a scrolling chart on the right, to the
   Founder's reference.

   THE TWO PANES MUST LINE UP TO THE PIXEL. They are separate elements with
   separate scrollers, so every row in both takes its height from --grow-h, and
   an expanded row adds exactly --gdetail-h on each side. A row that grows on
   one side only throws every row below it out of register.
   ==================================================================== */
/* The table takes a SHARE of the width, not a fixed slab. Pinned at 432px it
   starved titles on a wide screen and the chart on a narrow one; as a share
   with a floor and a ceiling, both stay readable across the range. */
.gantt{--grow-h:38px;--gdetail-h:72px;--gtable-w:clamp(360px,52%,600px);
  display:flex;flex-direction:column;min-width:0}
.gantt__bar{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--border-subtle)}
.gantt__heading{min-width:0;margin-right:var(--s-2)}
.gantt__sub{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.gantt__when{min-width:104px;text-align:center;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);color:var(--text-secondary);white-space:nowrap}
.gantt__fullico{display:flex;align-items:center;justify-content:center}

.gantt__grid{display:grid;grid-template-columns:var(--gtable-w) minmax(0,1fr);min-width:0}

/* ---- The table --------------------------------------------------------- */
.gtable{display:flex;flex-direction:column;min-width:0;
  border-right:1px solid var(--border-default);background:var(--bg-surface)}
.gtable__head,.gtable__row{display:grid;
  grid-template-columns:minmax(0,1fr) 84px 58px 58px;align-items:center}
.gtable__head{height:34px;border-bottom:1px solid var(--border-default);
  font-size:10px;font-weight:var(--fw-semibold);letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--text-tertiary)}
.gtable__body{overflow:hidden}
.gtable__c{padding:0 var(--s-3);min-width:0;display:flex;align-items:center;gap:6px}
/* The task cell pays for its own furniture — chevron, dot, number, late chip —
   out of the space the TITLE needs, so its gaps and padding are the tightest
   in the table rather than the default. */
.gtable__c--task{gap:5px;padding:0 var(--s-2) 0 4px}
.gtable__c--d{font-size:var(--fs-2xs);color:var(--text-secondary);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.gtable__row{height:var(--grow-h);border-bottom:1px solid var(--border-subtle)}
.gtable__row:hover{background:var(--bg-sunken)}
.gtable__chev{width:16px;height:16px;flex:0 0 auto;border:0;background:none;cursor:pointer;
  color:var(--text-tertiary);font-size:15px;line-height:1;border-radius:var(--r-xs);
  transition:transform var(--dur-micro) var(--ease)}
.gtable__chev[aria-expanded="true"]{transform:rotate(90deg);color:var(--text-primary)}
.gtable__chev:hover{background:var(--bg-sunken);color:var(--text-primary)}
.gtable__chev:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.gtable__dot{width:8px;height:8px;flex:0 0 auto;border-radius:50%;background:var(--tone)}
.gtable__num{flex:0 0 auto;font-size:10px;font-weight:var(--fw-bold);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.gtable__title{flex:1 1 auto;min-width:0;border:0;background:none;padding:0;cursor:pointer;
  font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-strong);color:var(--text-primary);
  text-align:left}
.gtable__title:hover{color:var(--text-link);text-decoration:underline}
.gtable__title:focus-visible{outline:2px solid var(--border-focus);outline-offset:1px}
.gtable__late{flex:0 0 auto;padding:1px 4px;border-radius:var(--r-xs);font-size:9px;
  font-weight:var(--fw-bold);text-transform:uppercase;letter-spacing:.02em;
  background:var(--sev-critical-bg);color:var(--sev-critical-tx);
  border:1px solid var(--sev-critical-bd)}

.gbar{flex:1 1 auto;min-width:0;height:6px;border-radius:3px;background:var(--bg-sunken);
  overflow:hidden}
.gbar i{display:block;height:100%;border-radius:3px;background:var(--tone)}
.gbar__n{flex:0 0 auto;width:32px;text-align:right;font-size:var(--fs-2xs);
  color:var(--text-secondary);font-variant-numeric:tabular-nums}

.gtable__detail{height:var(--gdetail-h);display:flex;flex-wrap:wrap;align-content:center;
  gap:6px;padding:0 var(--s-3) 0 34px;border-bottom:1px solid var(--border-subtle);
  background:var(--bg-sunken);overflow:hidden}
.gchip{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:var(--r-xs);
  background:var(--bg-surface);border:1px solid var(--border-subtle);
  font-size:var(--fs-2xs);color:var(--text-tertiary);white-space:nowrap;max-width:100%}
.gchip b{color:var(--text-primary);font-weight:var(--fw-strong);
  overflow:hidden;text-overflow:ellipsis}
.gchip--dep b{color:var(--text-link)}

/* ---- The chart --------------------------------------------------------- */
.gchart{overflow-x:auto;overflow-y:hidden;min-width:0;position:relative}
.gchart__canvas{position:relative;min-width:100%}
.gchart__axis{position:relative;height:34px;border-bottom:1px solid var(--border-default)}
.gchart__tick{position:absolute;top:11px;padding-left:5px;white-space:nowrap;
  font-size:var(--fs-2xs);color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.gchart__nowcap{position:absolute;top:8px;transform:translateX(-50%);display:flex;
  flex-direction:column;align-items:center;gap:2px;color:var(--accent);
  font-size:10px;font-weight:var(--fw-bold);white-space:nowrap;z-index:3}
.gchart__nowcap i{width:8px;height:8px;border-radius:50%;background:var(--accent);display:block}

.gchart__lanes{position:relative}
.gchart__grid{position:absolute;top:0;bottom:0;width:1px;background:var(--border-subtle);
  pointer-events:none}
.gchart__now{position:absolute;top:0;bottom:0;width:0;pointer-events:none;z-index:2;
  border-left:2px dashed var(--accent);opacity:.8}
.gchart__lane{position:relative;height:var(--grow-h);
  border-bottom:1px solid var(--border-subtle)}
.gchart__lane[data-open]{height:calc(var(--grow-h) + var(--gdetail-h));
  border-bottom:1px solid var(--border-subtle)}
.gchart__lane[data-open]::after{content:'';position:absolute;left:0;right:0;
  top:var(--grow-h);bottom:0;background:var(--bg-sunken)}

.gchart__bar{position:absolute;top:7px;height:24px;border:0;border-radius:var(--r-xs);
  cursor:pointer;overflow:hidden;display:flex;align-items:center;padding:0 8px;
  background:var(--bg-sunken);
  background:color-mix(in srgb,var(--tone) 22%,var(--bg-surface));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--tone) 42%,transparent);
  color:var(--text-primary);font:inherit;z-index:1}
.gchart__bar:hover{background:color-mix(in srgb,var(--tone) 32%,var(--bg-surface))}
.gchart__bar:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;z-index:4}
.gchart__bar[data-late]{box-shadow:inset 0 0 0 1.5px var(--sev-critical)}
.gchart__bar[data-done]{opacity:.72}
.gchart__fill{position:absolute;left:0;top:0;bottom:0;pointer-events:none;
  background:var(--tone);opacity:.3}
.gchart__label{position:relative;font-size:var(--fs-2xs);font-weight:var(--fw-strong);
  min-width:0}
.gchart__out{position:absolute;top:7px;height:24px;display:flex;align-items:center;
  max-width:260px;pointer-events:none;font-size:var(--fs-2xs);color:var(--text-secondary)}

/* A moment, not a span. */
.gchart__ms{position:absolute;top:12px;width:14px;height:14px;margin-left:-7px;
  transform:rotate(45deg);border:0;border-radius:2px;cursor:pointer;padding:0;
  background:var(--tone);box-shadow:0 0 0 2px var(--bg-surface);z-index:1}
.gchart__ms:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.gchart__ms[data-late]{background:var(--sev-critical)}
.gchart__mslabel{position:absolute;top:7px;height:24px;display:flex;align-items:center;
  max-width:260px;pointer-events:none;font-size:var(--fs-2xs);
  font-weight:var(--fw-strong);color:var(--text-primary)}

.gchart__arrows{position:absolute;left:0;top:0;pointer-events:none;overflow:visible;z-index:3}
.gchart__arrow{fill:none;stroke:var(--text-tertiary);stroke-width:1.3;opacity:.85}
.gchart__arrowhead{fill:var(--text-tertiary);opacity:.85}

/* ---- Full screen ------------------------------------------------------- */
.gantt.is-full{position:fixed;inset:var(--devbanner-h,0px) 0 0 0;z-index:70;
  background:var(--bg-surface);padding-bottom:var(--s-4);overflow:auto}
body.has-fullchart{overflow:hidden}
.gantt.is-full .gtable__body,.gantt.is-full .gchart{max-height:none}

/* ---- Kept from the previous pass -------------------------------------- */
.tl__pan{display:inline-flex;align-items:center;gap:4px}
.tl__late{display:inline-flex;align-items:center;gap:6px}
.tl__latedot{width:8px;height:8px;border-radius:50%;background:var(--sev-critical);
  flex:0 0 auto}
.tl__latecount{font-variant-numeric:tabular-nums;font-weight:var(--fw-bold);
  color:var(--sev-critical)}
.tl__latecount:empty{display:none}
.tl__late.is-on{background:var(--sev-critical-bg);border-color:var(--sev-critical-bd);
  color:var(--sev-critical-tx)}
.tl__late.is-on .tl__latecount{color:inherit}
.tl__late[disabled]{opacity:.45;pointer-events:none}
.tl__outside{margin:0;padding:var(--s-2) var(--s-4) var(--s-3);
  font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* What gets dropped as the window narrows, in order of how little it costs:
   first the task NUMBER — it is in the tooltip, in the expanded row and in
   every other view — then the progress BAR, since the percentage beside it
   carries the same fact in a fifth of the width. The title is never traded,
   because a row whose title is three characters is not a row. */
@media (max-width:1240px){ .gtable__num{display:none} }
@media (max-width:1040px){
  .gtable__head,.gtable__row{grid-template-columns:minmax(0,1fr) 42px 56px 56px}
  .gtable__c--pct .gbar{display:none}
}
@media (max-width:860px){
  /* Below this width the table and the chart cannot both be useful. The table
     keeps the names and the dates; the chart scrolls under it. */
  .gantt__grid{grid-template-columns:minmax(0,1fr)}
  .gtable{border-right:0;border-bottom:1px solid var(--border-default)}
  .gtable__head,.gtable__row{grid-template-columns:minmax(0,1fr) 92px 66px 66px}
}

/* ═══ Forms (§66) ═══════════════════════════════════════════════════════ */
.form{display:flex;flex-direction:column;gap:var(--s-3)}
.form__sec{margin:var(--s-2) 0 0;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  letter-spacing:var(--track-label);color:var(--text-tertiary);text-transform:uppercase}
.fld{display:flex;flex-direction:column;gap:5px;min-width:0}
.fld__label{font-size:var(--fs-2xs);font-weight:var(--fw-strong);color:var(--text-secondary)}
.fld__err{margin:0;padding:var(--s-2) var(--s-3);border-radius:var(--r-xs);
  background:var(--sev-critical-bg);color:var(--sev-critical-tx);border:1px solid var(--sev-critical-bd);
  font-size:var(--fs-xs);font-weight:var(--fw-strong)}
.input{height:40px;padding:0 var(--s-3);border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-surface);color:var(--text-primary);font:inherit;font-size:var(--fs-xs);
  transition:border-color var(--dur-micro) var(--ease),box-shadow var(--dur-micro) var(--ease)}
textarea.input{height:auto;padding:var(--s-2) var(--s-3);line-height:var(--lh-normal);resize:vertical}
.input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-quiet)}
.input:disabled{background:var(--bg-sunken);color:var(--text-tertiary)}
.muted{color:var(--text-secondary)}

/* ═══ Workspace (§40) ═══════════════════════════════════════════════════ */
.ws__head{display:flex;align-items:flex-start;gap:var(--s-4);flex-wrap:wrap;margin-bottom:var(--s-4)}
.ws__title{flex:1 1 320px;min-width:0;display:flex;flex-direction:column;gap:2px}
.ws__title h1{margin:0;font-size:var(--fs-xl);line-height:var(--lh-xl);font-weight:var(--fw-semibold);
  letter-spacing:var(--track-snug)}
.ws__chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.ws__pane{padding:var(--s-4)}
.ws__desc{margin:var(--s-4) 0 0;color:var(--text-secondary);line-height:var(--lh-relaxed);
  font-size:var(--fs-sm);white-space:pre-wrap}
.ws__actions{display:flex;flex-direction:column;gap:var(--s-3)}
.ws__hint{margin:0;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.ws__btns{display:flex;gap:8px;flex-wrap:wrap}
.ws__row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3)}
.who--private .avatar--private{background:var(--sev-muted-bg);color:var(--sev-muted);
  border:1px dashed var(--sev-muted-bd)}
.who--private{color:var(--text-tertiary);font-style:italic}

.checklist,.deplist,.comments,.proposals{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.check{display:flex;align-items:center;gap:var(--s-3);padding:8px var(--s-2);border-radius:var(--r-xs);cursor:pointer}
.check:hover{background:var(--bg-sunken)}
.check input{width:18px;height:18px;accent-color:var(--accent)}
.check .is-done{text-decoration:line-through;color:var(--text-tertiary)}
.check small{margin-left:auto;color:var(--text-tertiary);font-size:var(--fs-2xs)}
.deplist li{display:flex;align-items:center;gap:var(--s-3);padding:8px 0;border-bottom:1px solid var(--border-subtle)}
.comments li{padding:var(--s-3);background:var(--bg-sunken);border-radius:var(--r-md)}
.comment__meta{display:flex;justify-content:space-between;gap:var(--s-2);margin-bottom:4px;font-size:var(--fs-2xs)}
.comment__meta small{color:var(--text-tertiary)}
.comments p{margin:0;font-size:var(--fs-sm);line-height:var(--lh-normal);white-space:pre-wrap}
.comment__new{display:flex;flex-direction:column;gap:var(--s-2);margin-top:var(--s-3);align-items:flex-end}
.comment__new textarea{width:100%}
.proposal{padding:var(--s-3);border-radius:var(--r-md);border-left:3px solid var(--color-ai);
  background:var(--color-ai-soft);display:flex;flex-direction:column;gap:var(--s-2)}
.proposal__head{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;font-size:var(--fs-2xs)}
.proposal__list{margin:0;padding-left:20px;font-size:var(--fs-xs);line-height:1.7}
.proposal[data-state="REJECTED"]{opacity:.6}
.aipane{display:flex;flex-direction:column;gap:var(--s-4)}
.timeline{list-style:none;margin:0;padding:0 0 0 var(--s-2)}
.timeline__item{display:flex;gap:var(--s-3);padding:var(--s-2) 0;position:relative}
.timeline__item small{display:block;color:var(--text-tertiary);font-size:var(--fs-2xs)}
.timeline__dot{width:10px;height:10px;border-radius:50%;background:var(--accent);flex:0 0 auto;margin-top:5px}
.timeline__item[data-actor="AI_WORKER"] .timeline__dot{background:var(--color-ai)}
.timeline__item[data-actor="SYSTEM"] .timeline__dot{background:var(--text-tertiary)}
@media (max-width:640px){.ws__row{grid-template-columns:1fr}}

/* ═══ Shell, matched to the reference ═══════════════════════════════════
   Navy sidebar carrying the brand and its tagline, a header with a real
   search field, the language toggle, a bell with a live count and the
   operator's name and role. None of the 51 screens or their permissions
   change — this is the same shell in the clothes the reference shows. */
.brand{background:#0F172A;color:#fff;border-bottom:1px solid rgba(255,255,255,.06)}
.brand__mark{display:flex;flex-direction:column;line-height:1.15;min-width:0}
.brand__name{font-size:var(--fs-lg);font-weight:var(--fw-bold);letter-spacing:-.01em;white-space:nowrap}
.brand__name em{font-style:normal;color:#EF4444}
.brand__tag{font-size:11px;color:#94A3B8;white-space:nowrap}
.shell[data-nav="collapsed"] .brand__mark{display:none}
.nav{background:#0F172A;border-right:1px solid rgba(255,255,255,.06);color:#CBD5E1}
/* 3.75:1 on the slab before — slate-500 is a light-theme grey being used on a
   permanently dark ground. */
.nav .nav__grouphead{color:#94A3B8}
.nav a{color:#CBD5E1}
.nav__quote{margin:var(--s-3);padding:var(--s-4);border-radius:var(--r-lg);
  background:linear-gradient(160deg,#1E293B,#0B1220);border:1px solid rgba(255,255,255,.08);
  color:#E2E8F0;font-size:var(--fs-xs);line-height:1.5;text-align:center;white-space:pre-line;
  font-weight:var(--fw-strong)}
.shell[data-nav="collapsed"] .nav__quote{display:none}
.header{background:var(--bg-surface);border-bottom:1px solid var(--border-default)}
/* The search is the header's primary control: it asks for 360px, never drops
   below 220px, and the telemetry chips are what give way first (see the
   container queries that follow) — not the search. */
.gsearch{display:flex;align-items:center;gap:var(--s-2);flex:1 1 360px;min-width:220px;max-width:640px;height:40px;
  padding:0 var(--s-3);border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-sunken)}
/* Order of sacrifice with the search present: Queue/Edge SLIs go first, then
   the whole health strip and the elevation label, so that at a 1440px laptop
   the header reads search · language · alerts · account, as designed. */
@container hdr (max-width: 1320px) { .health__item:nth-child(n+3){display:none} }
@container hdr (max-width: 1200px) { .health{display:none} .elev__label{display:none} .elev{padding-inline:var(--s-2)} }
.gsearch:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-quiet);background:var(--bg-surface)}
.gsearch input{flex:1;border:0;background:none;outline:none;font:inherit;font-size:var(--fs-xs);min-width:0;color:var(--text-primary)}
.gsearch input::placeholder{color:var(--text-tertiary)}
.gsearch kbd{font:inherit;font-size:11px;color:var(--text-tertiary);border:1px solid var(--border-default);
  border-radius:4px;padding:1px 6px;background:var(--bg-surface)}
.gresults{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:var(--z-modal);
  background:var(--bg-overlay);border:1px solid var(--border-default);border-radius:var(--r-panel);
  box-shadow:var(--shadow-lg);padding:var(--s-2);display:flex;flex-direction:column;gap:2px;max-height:60vh;overflow:auto}
.gresult{display:grid;grid-template-columns:auto 1fr auto;gap:var(--s-3);align-items:center;
  padding:8px var(--s-3);border:0;border-radius:var(--r-md);background:transparent;text-align:left;cursor:pointer;
  color:var(--text-primary);font:inherit}
.gresult:hover,.gresult:focus-visible{background:var(--bg-sunken);outline:none}
.gresult .mono{color:var(--text-tertiary);font-size:var(--fs-2xs)}
.gresults__none{padding:var(--s-3);color:var(--text-tertiary);font-size:var(--fs-xs)}
.whoami{gap:var(--s-2);height:40px;padding:0 var(--s-2) 0 4px;border-radius:var(--r-md)}
.whoami__text{display:flex;flex-direction:column;line-height:1.2;text-align:left;min-width:0}
.whoami__name{font-size:var(--fs-xs);font-weight:var(--fw-semibold);color:var(--text-primary);white-space:nowrap}
.whoami__role{font-size:11px;color:var(--text-tertiary);white-space:nowrap}
.iconbtn__badge{background:var(--sev-critical);color:#fff}
@media (max-width:900px){.whoami__text{display:none}.gsearch{flex-basis:200px}.gsearch kbd{display:none}}
@media (max-width:640px){
  .langswitch--hdr{display:none}
  /* Phone header: search · elevation · alerts · avatar. Density and theme
     stay reachable in Settings; the account chip must not be pushed off-screen. */
  #density,#theme,.header__spacer{display:none}
  .gsearch{min-width:0;flex:1 1 120px}
}
.devbanner{position:sticky;top:0;z-index:calc(var(--z-toast) + 1);display:flex;gap:8px;
  align-items:center;justify-content:center;padding:6px 12px;font-size:var(--fs-2xs);
  background:#FEF3C7;color:#78350F;border-bottom:1px solid #FDE68A}
.devbanner strong{letter-spacing:.06em}
.devbanner a{color:#78350F;font-weight:var(--fw-semibold)}

/* ===== Tasks AI composer ==================================================
   A port of the Founder's prompt-input reference into this console's tokens.
   Geometry follows the reference: a 48px collapsed pill, a 24px radius, the
   action row inset bottom-left, a 32px circular action button bottom-right,
   and the spring curve on every size change. Colour comes from --color-ai,
   which §31 reserves for AI surfaces, so the composer reads as the assistant
   and never as authoritative system state. ========================== */
/* .card clips to its radius, which would slice the top off a popover that
   opens upward. The composer's own corners are on .aic__card, so this section
   has nothing to clip. */
.aic{overflow:visible}

/* ---- The dock ----------------------------------------------------------
   Fixed to the bottom of the content area, tracking the navigation's width so
   it never covers the sidebar, and sitting above the status bar. It is a
   conversation surface, so it takes the full width rather than a rail column.
   z-index sits below the drawer and modal: an approval dialog must still come
   out on top of a chat. */
.aic--dock{position:fixed;left:var(--nav-w);right:0;bottom:var(--statusbar-h);
  z-index:calc(var(--z-drawer) - 20);
  display:flex;flex-direction:column;
  border-radius:var(--r-lg) var(--r-lg) 0 0;border-bottom:0;
  box-shadow:0 -8px 32px -16px rgb(15 23 42 / .28);
  transition:left var(--dur-normal) var(--ease),top var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275)}
/* NOT when parked. This rule is more specific than the bubble's own
   `left:auto`, so a collapsed nav dragged the floating button back to the
   left edge and sat it on top of the icon rail. The nav offset is only
   meaningful for the states that SPAN the content area — dock and full. */
.shell[data-nav="collapsed"] .aic--dock:not([data-mode="bubble"]){left:var(--nav-w-collapsed)}
@keyframes aic-dock-in{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:none}}
.aic--dock[data-mode="dock"]{animation:aic-dock-in .32s cubic-bezier(0.175,0.885,0.32,1.275) both}
:root[data-theme="dark"] .aic--dock{box-shadow:0 -8px 32px -14px rgb(0 0 0 / .6)}

/* The board must be able to scroll clear of the dock, whose height the dock
   itself publishes as --aic-dock-h (see ai-composer.js). */
.tasksmain{padding-bottom:calc(var(--aic-dock-h,136px) + var(--s-4))}
/* Collapsed, the dock is a slim bar. It is fixed over the board, so every
   pixel it takes is a pixel of task list the operator cannot see — at 158px it
   was eating a fifth of a laptop screen just to show a closed input. Closed it
   is now ~104px: a compact title row and the field, nothing else. */
.aic--dock:not([data-full="true"]):not([data-expanded="true"]) .aic__suggest{display:none}
.aic--dock:not([data-full="true"]):not([data-expanded="true"]) .aic__head{
  height:36px;min-height:0;padding-block:0}
.aic--dock:not([data-full="true"]):not([data-expanded="true"]) .aic__head h2{font-size:var(--fs-xs)}
.aic--dock:not([data-full="true"]):not([data-expanded="true"]) .aic__body{padding:10px var(--s-4)}
/* An empty context tray is still a flex item, so it was contributing a 12px
   gap to a dock that had nothing in it. */
.aic:not([data-hasctx="true"]) .aic__tray{display:none}

.aic__head{cursor:default}
.aic__spacer{flex:1}
/* ---- Window controls, macOS traffic lights -----------------------------
   Amber minimises, green zooms, sitting at the left of the title bar. The
   12px dot inside a 20px button gives the 8px spacing the platform uses while
   keeping a hit target twice the size of the dot. Glyphs appear on hover or
   keyboard focus of the GROUP, exactly as the platform does — the colour is
   what identifies the control at rest. */
.aic__tl{display:flex;align-items:center;gap:0;flex:none;margin-right:var(--s-2)}
.aic__tl-btn{display:grid;place-items:center;width:26px;height:26px;flex:none;
  padding:0;border:0;background:none;cursor:pointer;border-radius:50%}
.aic__tl-dot{position:relative;display:block;width:16px;height:16px;border-radius:50%;
  box-shadow:inset 0 0 0 .5px rgb(0 0 0 / .22);
  transition:filter var(--dur-micro) var(--ease)}
.aic__tl-btn--min  .aic__tl-dot{background:#FEBC2E}
.aic__tl-btn--zoom .aic__tl-dot{background:#28C840}
.aic__tl-btn:active .aic__tl-dot{filter:brightness(.88)}
.aic__tl-btn:focus-visible{outline:2px solid var(--color-ai);outline-offset:1px}

.aic__tl-dot::before,.aic__tl-dot::after{content:'';position:absolute;opacity:0;
  transition:opacity var(--dur-micro) var(--ease)}
.aic__tl:hover .aic__tl-dot::before,.aic__tl:hover .aic__tl-dot::after,
.aic__tl:focus-within .aic__tl-dot::before,.aic__tl:focus-within .aic__tl-dot::after{opacity:1}

/* Minimise: a single bar. */
.aic__tl-btn--min .aic__tl-dot::before{left:3.4px;right:3.4px;top:7px;height:2px;
  border-radius:1px;background:#8B5A00}
.aic__tl-btn--min .aic__tl-dot::after{content:none}

/* Zoom: two triangles pointing apart, flipping to point together once the
   panel already fills the screen. */
.aic__tl-btn--zoom .aic__tl-dot::before{top:3.4px;left:3.4px;width:4.8px;height:4.8px;
  background:#0B6B1C;clip-path:polygon(0 0,100% 0,0 100%)}
.aic__tl-btn--zoom .aic__tl-dot::after{bottom:3.4px;right:3.4px;width:4.8px;height:4.8px;
  background:#0B6B1C;clip-path:polygon(100% 100%,100% 0,0 100%)}
.aic[data-full="true"] .aic__tl-btn--zoom .aic__tl-dot::before{clip-path:polygon(100% 0,100% 100%,0 100%)}
.aic[data-full="true"] .aic__tl-btn--zoom .aic__tl-dot::after{clip-path:polygon(0 0,100% 0,0 100%)}

/* ---- Parked: the assistant as a floating bubble ------------------------
   The same element collapsed to a circle in the corner. Everything inside is
   still mounted — an unfinished draft, attached tasks and the last answer all
   come back when it is re-opened. The board's --aic-dock-h goes to 0, so the
   screen is genuinely given back rather than merely covered by something
   smaller. */
.aic__bubble{display:none;position:relative;width:100%;height:100%;
  place-items:center;border:0;background:none;cursor:pointer;padding:0}
.aic__bubble .aimark{width:30px;height:30px}
.aic__bubble-dot{position:absolute;top:9px;right:9px;display:none;
  width:10px;height:10px;border-radius:50%;
  background:var(--sev-critical);border:2px solid var(--color-ai)}
.aic[data-unread="true"] .aic__bubble-dot{display:block}

@keyframes aic-pop{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:none}}
.aic--dock[data-mode="bubble"]{
  overflow:hidden;
  left:auto;right:var(--s-4);bottom:calc(var(--statusbar-h) + var(--s-4));
  width:56px;height:56px;padding:0;
  border:0;border-radius:50%;background:var(--color-ai);
  box-shadow:0 10px 30px -8px rgb(124 58 237 / .55);
  animation:aic-pop .32s cubic-bezier(0.175,0.885,0.32,1.275) both}
.aic--dock[data-mode="bubble"]:hover{background:#6D28D9}
.aic[data-mode="bubble"] .aic__head,
.aic[data-mode="bubble"] .aic__body{display:none}
.aic[data-mode="bubble"] .aic__bubble{display:grid}
.aic--dock[data-mode="bubble"]:focus-within{outline:2px solid var(--color-ai);outline-offset:3px}

/* ---- Full screen -------------------------------------------------------
   The same element, grown to fill the content area. Nothing is re-mounted, so
   the draft, the attached tasks and the answer all survive the change. */
.aic[data-full="true"].aic--dock{top:var(--header-h);border-radius:0;
  box-shadow:0 0 0 1px var(--border-subtle)}
.aic[data-full="true"] .aic__body{flex:1;min-height:0}
.aic[data-full="true"] .aic__conv{flex:1;min-height:0;overflow-y:auto}
/* width:100% is not redundant with max-width here: margin-inline:auto cancels
   the flex container's default stretch, and these boxes have no intrinsic
   width of their own (the textarea inside the card is absolutely positioned),
   so without it the card collapses to a 2px sliver. */
.aic[data-full="true"] .aic__card,
.aic[data-full="true"] .aic__suggest,
.aic[data-full="true"] .assistant__q,
.aic[data-full="true"] .assistant__a{width:100%;max-width:820px;margin-inline:auto}
/* The tray is absolutely positioned inside its own track; centre the track. */
.aic[data-full="true"] .aic__tray{width:100%;max-width:820px;margin-inline:auto}
.aic[data-full="true"] .aic__suggest{flex-direction:row;flex-wrap:wrap;justify-content:center}
.aic[data-full="true"] .aic__sugg{width:auto;border-color:var(--border-default);border-radius:999px}

.aic__conv{display:flex;flex-direction:column;min-height:0}
.aic__conv:empty{display:none}
.aic__body{display:flex;flex-direction:column;padding:var(--s-4);gap:var(--s-3);min-height:0}

/* ---- Context tray: slides up from behind the card ---------------------- */
.aic__tray{position:relative;z-index:0;height:0;overflow:hidden;
  transition:height var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275)}
.aic[data-expanded="true"][data-hasctx="true"] .aic__tray{height:50px}
.aic__trayinner{position:absolute;bottom:-12px;left:14px;right:14px;
  display:flex;align-items:center;gap:6px;height:58px;padding:8px 8px 18px;
  border:1px solid var(--border-default);border-bottom:0;border-radius:14px 14px 0 0;
  background:var(--bg-sunken);overflow-x:auto;scrollbar-width:none;
  transform:translateY(100%);opacity:0;
  transition:transform var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275),opacity .3s var(--ease)}
.aic__trayinner::-webkit-scrollbar{height:0}
.aic[data-expanded="true"][data-hasctx="true"] .aic__trayinner{transform:none;opacity:1}
@keyframes aic-chip-in{from{opacity:0;transform:translateY(-8px) scale(.9)}to{opacity:1;transform:none}}
.aic__ctx{display:inline-flex;align-items:center;flex:none;max-width:190px;
  border:1px solid var(--border-default);border-radius:999px;background:var(--bg-surface);
  animation:aic-chip-in .4s cubic-bezier(0.175,0.885,0.32,1.275) both}
.aic__ctx-open{display:flex;align-items:center;gap:6px;min-width:0;max-width:158px;
  padding:5px 4px 5px 10px;background:none;border:0;cursor:pointer;
  font:inherit;font-size:var(--fs-2xs);color:var(--text-secondary)}
.aic__ctx-open b{flex:none;font-weight:700;color:var(--color-ai);font-variant-numeric:tabular-nums}
.aic__ctx-open span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.aic__ctx-open:hover{color:var(--text-primary)}
.aic__ctx-x{display:grid;place-items:center;width:20px;height:20px;margin-right:4px;flex:none;
  border:0;border-radius:50%;background:none;cursor:pointer;color:var(--text-tertiary);
  transition:background var(--dur-micro) var(--ease),color var(--dur-micro) var(--ease)}
.aic__ctx-x:hover{background:var(--bg-sunken);color:var(--text-primary)}

/* ---- The card --------------------------------------------------------- */
.aic__card{position:relative;z-index:1;height:48px;overflow:hidden;
  border:1px solid var(--border-default);border-radius:24px;background:var(--bg-surface);
  box-shadow:var(--shadow-sm);
  transition:height var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275),
             border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.aic[data-expanded="true"] .aic__card{overflow:visible;cursor:text}
.aic__card:hover{border-color:var(--border-strong,var(--border-default))}
.aic__card:focus-within{border-color:var(--color-ai);box-shadow:0 0 0 3px var(--color-ai-soft)}

.aic__ta{position:absolute;inset:0 0 auto 0;z-index:1;width:100%;height:68px;resize:none;
  padding:14px 48px 14px 16px;border:0;outline:none;background:transparent;
  font:inherit;font-size:var(--fs-sm);line-height:22px;color:var(--text-primary);
  overflow-y:hidden;scrollbar-width:thin;
  opacity:0;transform:scale(.95) translateY(-4px);pointer-events:none;
  transition:opacity .3s var(--ease),transform .3s var(--ease),
             height var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275)}
.aic__ta.is-scrolling{overflow-y:auto}
.aic__ta::placeholder{color:var(--text-tertiary);font-weight:var(--fw-medium)}
.aic[data-expanded="true"] .aic__ta{opacity:1;transform:none;pointer-events:auto}
.aic__ta:disabled{color:var(--text-secondary)}

.aic__fade{position:absolute;left:16px;right:48px;height:32px;z-index:2;pointer-events:none;opacity:0}
.aic__fade--top{top:0;background:linear-gradient(to bottom,var(--bg-surface),transparent)}
.aic__fade--bot{top:36px;background:linear-gradient(to top,var(--bg-surface),transparent);
  transition:top var(--dur-panel,.4s) cubic-bezier(0.175,0.885,0.32,1.275)}

.aic__ph{position:absolute;inset:0 0 auto 0;z-index:1;width:100%;text-align:left;
  padding:15px 48px 15px 16px;border:0;background:none;cursor:text;
  font:inherit;font-size:var(--fs-sm);font-weight:var(--fw-medium);line-height:17px;
  color:var(--text-tertiary);
  transition:opacity .3s var(--ease),transform .3s var(--ease)}
.aic[data-expanded="true"] .aic__ph{opacity:0;transform:scale(1.05) translateY(4px);pointer-events:none}

/* ---- Action row ------------------------------------------------------- */
.aic__actions{position:absolute;left:12px;right:48px;bottom:8px;z-index:10;
  display:flex;align-items:center;gap:2px;
  opacity:0;filter:blur(2px);transform:translateY(8px);pointer-events:none;
  transition:opacity .3s var(--ease),filter .3s var(--ease),transform .3s cubic-bezier(0.175,0.885,0.32,1.275)}
.aic[data-expanded="true"]:not([data-recording="true"]) .aic__actions{
  opacity:1;filter:none;transform:none;pointer-events:auto}
.aic__sel{position:relative}
.aic__sel--right{margin-left:auto}
.aic__chip{display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 8px;
  border:0;border-radius:999px;background:none;cursor:pointer;
  font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-tertiary);
  transition:background var(--dur-micro) var(--ease),color var(--dur-micro) var(--ease)}
.aic__chip:hover,.aic__chip[aria-expanded="true"]{background:var(--bg-sunken);color:var(--text-primary)}
.aic__plus{display:grid;place-items:center;width:28px;height:28px;flex:none;
  border:0;border-radius:50%;background:none;cursor:pointer;color:var(--text-tertiary);
  transition:background var(--dur-micro) var(--ease),color var(--dur-micro) var(--ease)}
.aic__plus:hover{background:var(--bg-sunken);color:var(--text-primary)}
.aic__plus:disabled{opacity:.4;pointer-events:none}
.aic__svg{display:inline-flex;align-items:center}

/* Morphing label: the box animates to the measured width of the new text. */
.aic__morph{position:relative;display:inline-flex;align-items:center;justify-content:center;
  overflow:hidden;transition:width .3s cubic-bezier(0.175,0.885,0.32,1.275)}
.aic__morph-ghost{visibility:hidden;white-space:nowrap;padding:0 1px}
@keyframes aic-morph-in{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:none}}
.aic__morph-live{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  white-space:nowrap;animation:aic-morph-in .3s var(--ease) both}

/* ---- Popovers --------------------------------------------------------- */
.aic__menu{position:absolute;bottom:100%;left:0;z-index:50;margin-bottom:10px;
  display:flex;flex-direction:column;gap:2px;width:210px;padding:4px;
  border:1px solid var(--border-default);border-radius:16px;background:var(--bg-overlay);
  box-shadow:var(--shadow-lg);transform-origin:bottom left;
  opacity:0;transform:translateY(12px) scale(.95);pointer-events:none;
  transition:opacity .2s var(--ease),transform .3s cubic-bezier(0.34,1.56,0.64,1)}
.aic__menu.is-open{opacity:1;transform:none;pointer-events:auto}
/* Eight domains is taller than the rail; scroll inside rather than run off
   the top of the viewport. */
.aic__menu{max-height:min(296px,44vh);overflow-y:auto;scrollbar-width:thin}
.aic__menu--ctx{left:auto;right:0;width:280px;max-height:244px;overflow-y:auto;transform-origin:bottom right}
.aic__menuhover{position:absolute;left:4px;right:4px;top:4px;height:32px;z-index:-1;
  border-radius:10px;background:var(--bg-sunken);opacity:0;pointer-events:none;
  transition:transform .3s cubic-bezier(0.175,0.885,0.32,1.275),opacity .15s var(--ease)}
.aic__mi{display:flex;align-items:center;gap:8px;height:32px;flex:none;padding:0 10px;
  border:0;border-radius:10px;background:none;cursor:pointer;text-align:left;
  font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-medium);color:var(--text-secondary)}
.aic__mi:hover:not(:disabled){color:var(--text-primary)}
.aic__mi.is-on{color:var(--color-ai)}
.aic__mi.is-off{cursor:not-allowed;color:var(--text-tertiary)}
.aic__mi.is-off .imgicon{opacity:.45}
.aic__mi-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aic__mi-off{flex:none;font-size:10px;text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-tertiary)}
.aic__mi--task{height:auto;min-height:32px;padding:7px 10px}
.aic__mi-num{flex:none;font-size:10px;font-weight:700;color:var(--color-ai);font-variant-numeric:tabular-nums}
.aic__mi-none{margin:0;padding:10px;font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* ---- Audio meter + action button -------------------------------------- */
.aic__wave{position:absolute;right:48px;bottom:8px;z-index:10;
  display:flex;align-items:center;justify-content:flex-end;gap:3px;height:32px;width:0;
  opacity:0;transform:translateX(12px);pointer-events:none;
  transition:width .4s cubic-bezier(0.175,0.885,0.32,1.275),opacity .3s var(--ease),transform .3s var(--ease)}
.aic[data-recording="true"] .aic__wave{width:64px;opacity:1;transform:none}
.aic__wave i{width:4px;height:4px;border-radius:999px;background:var(--color-ai);
  transition:height 75ms linear}
.aic__go{position:absolute;right:8px;bottom:8px;z-index:10;
  display:grid;place-items:center;width:32px;height:32px;
  border:0;border-radius:50%;background:var(--color-ai);color:#fff;cursor:pointer;
  transition:opacity var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease)}
.aic__go:hover{background:#6D28D9}
.aic__go:disabled{opacity:.45;cursor:default}
.aic__go:focus-visible{outline:2px solid var(--color-ai);outline-offset:2px}
.aic__go-stack{position:relative;display:block;width:16px;height:16px}
.aic__go-face{position:absolute;inset:0;display:grid;place-items:center;
  opacity:0;transform:scale(.5) rotate(45deg);filter:blur(1px);pointer-events:none;
  transition:opacity .3s var(--ease),transform .3s cubic-bezier(0.175,0.885,0.32,1.275),filter .3s var(--ease)}
.aic__go-face.is-mic{transform:scale(.5) rotate(-45deg)}
.aic__go[data-state="arrow"] .is-arrow,
.aic__go[data-state="mic"] .is-mic,
.aic__go[data-state="stop"] .is-stop{opacity:1;transform:none;filter:none}

/* ---- Suggestions ------------------------------------------------------- */
.aic__suggest{display:flex;flex-direction:row;flex-wrap:wrap;gap:4px}
.aic__sugg{display:flex;align-items:center;gap:7px;padding:7px 10px;
  border:1px solid transparent;border-radius:var(--r-md);background:none;cursor:pointer;
  text-align:left;font:inherit;font-size:var(--fs-2xs);color:var(--text-secondary);
  transition:background var(--dur-micro) var(--ease),border-color var(--dur-micro) var(--ease),
             color var(--dur-micro) var(--ease)}
.aic__sugg:hover{background:var(--color-ai-soft);border-color:var(--color-ai);color:var(--text-primary)}
.aic__sugg-caret{flex:none;color:var(--color-ai);font-weight:700}

@media (max-width:900px){
  /* Docked and full-screen span the content area, so below 900px they start
     at the window edge. The PARKED BUBBLE does not span anything — it is a
     56px circle that belongs in the corner, and left:0 dragged it on top of
     the navigation. */
  .aic--dock:not([data-mode="bubble"]),
  .shell[data-nav="collapsed"] .aic--dock:not([data-mode="bubble"]){left:0}
}
/* Dictation is offered only where the browser can actually do it. */
.aic[data-nomic="true"] .aic__go[data-state="mic"]{display:none}

/* ===== [hidden] means hidden ==============================================
   The user agent's [hidden]{display:none} is an ordinary author-weight-zero
   rule, so ANY class rule carrying `display` silently beats it. This file had
   already worked around that three times with per-class :not([hidden]) hacks
   (.auth__ok, .auth__social, .auth__or) — and two live components still had
   the bug:

     .tip{display:flex}          → hideTip() set the attribute and the chart
                                   tooltip stayed on screen. This is the
                                   "Editorial 4 · 50%" box the Founder saw
                                   floating over the filter bar.
     .rowmenu__list{display:flex} → every row's action menu was rendered
                                   visible at all times, on top of the table.

   Fixing them one at a time invites a fourth. This states the invariant once,
   for every element in both apps: if it carries the hidden attribute, it is
   not displayed. Components that want an animated close use a class, not the
   attribute, so nothing here fights a transition. ====================== */
[hidden]{display:none!important}

/* ===== Model choice and the usage meter ==================================
   The Founder selects the model per task (18 Sept 2026), so the chip that
   shows it is the most important control in the action row and is placed
   first. Vendor is carried by a small coloured dot rather than a logo: eight
   third-party marks would mean eight more network origins in the CSP, and the
   name is already written next to it. ==================================== */
.aic__chip--model{font-weight:var(--fw-bold)}
.aic__vendor{display:inline-flex;align-items:center;flex:none}

/* ---- Vendor marks -------------------------------------------------------
   Inline SVG or a lettermark, never a fetched logo: a control plane should
   not open a third-party origin to decorate a dropdown, and the picker has to
   work with the network down. --vlogo-c carries the brand colour. */
/* The assistant's own mark: a chat bubble with a robot face. Takes
   currentColor, so the same element serves the purple badge, the white-on-
   purple bubble and a dark title bar. */
.aimark{display:inline-flex;align-items:center;justify-content:center;flex:none;line-height:0}
.aimark svg{display:block;width:100%;height:100%}
.assistant__ico .aimark{color:#fff}
.aic__bubble .aimark{color:#fff}
.aic__chip .aimark{color:var(--color-ai)}
.aic__mi .aimark{color:var(--color-ai)}
.aic__mi.is-off .aimark{opacity:.45}

.vlogo{display:inline-flex;align-items:center;justify-content:center;flex:none;
  line-height:0;border-radius:4px;overflow:hidden}
.vlogo svg,.vlogo img{display:block;width:100%;height:100%;object-fit:contain;
  pointer-events:none;user-select:none;-webkit-user-drag:none}
/* A single-dark-colour mark disappears on a dark ground; invert only those. */
:root[data-theme="dark"] .vlogo--mono img{filter:invert(1) brightness(1.7)}
.vlogo--letter{border-radius:5px;background:var(--vlogo-c);color:#fff;
  font-size:8px;font-weight:800;letter-spacing:.02em;line-height:1;
  font-family:var(--font-ui,inherit)}
/* xAI's drawn X is near-black; lift it on a dark ground. */
:root[data-theme="dark"] .vlogo svg path[fill="#111827"]{fill:#E5E7EB}

.aic__menu--models{width:340px;max-height:min(460px,60vh)}
.aic__mcount{position:sticky;top:0;z-index:1;margin:0;padding:8px 10px 6px;
  background:var(--bg-overlay);
  font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  font-weight:var(--fw-bold);color:var(--text-tertiary)}
.aic__mrow{display:flex;flex-direction:column;gap:3px;width:100%;padding:8px 10px;
  border:0;border-radius:10px;background:none;cursor:pointer;text-align:left;font:inherit}
.aic__mrow:hover{background:var(--bg-sunken)}
.aic__mrow.is-on{background:var(--color-ai-soft);box-shadow:inset 0 0 0 1px var(--color-ai)}
.aic__mrow-top{display:flex;align-items:center;gap:8px}
.aic__mrow-top b{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:var(--fs-xs);font-weight:var(--fw-semibold);color:var(--text-primary)}
.aic__mvendor{font-weight:var(--fw-semibold);color:var(--text-secondary)}
.aic__tier{flex:none;padding:1px 7px;border-radius:999px;
  font-size:10px;font-weight:var(--fw-bold);letter-spacing:.04em;text-transform:uppercase}
/* The badge follows the price band, so the colour ramp reads as spend. */
.aic__tier[data-tier="budget"]  {background:var(--sev-ok-bg);      color:var(--sev-ok-tx,var(--sev-ok))}
.aic__tier[data-tier="standard"]{background:var(--sev-info-bg);    color:var(--sev-info-tx,var(--sev-info))}
.aic__tier[data-tier="premium"] {background:var(--sev-medium-bg);  color:var(--sev-medium-tx,var(--sev-medium))}
.aic__tier[data-tier="frontier"]{background:var(--sev-critical-bg);color:var(--sev-critical-tx,var(--sev-critical))}
.aic__mrow-meta{display:flex;gap:10px;padding-left:25px;font-size:var(--fs-2xs);
  color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.aic__mrow-price{margin-left:auto}

/* ---- The per-answer meter ---------------------------------------------- */
.assistant__meter{display:flex!important;flex-wrap:wrap;align-items:center;gap:4px 10px;
  margin:6px 0 0!important;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.assistant__meter b{color:var(--text-primary);font-weight:var(--fw-bold)}
.assistant__meter-m{padding:1px 7px;border-radius:999px;
  background:var(--color-ai-soft);color:var(--color-ai);font-weight:var(--fw-semibold)}

/* ---- The header chip: today's running total ---------------------------- */
.aic__usagechip{display:inline-flex;align-items:baseline;gap:6px;flex:none;height:26px;padding:0 10px;
  border:1px solid var(--border-default);border-radius:999px;background:none;cursor:pointer;
  font:inherit;font-size:var(--fs-2xs);color:var(--text-secondary);
  font-variant-numeric:tabular-nums;
  transition:background var(--dur-micro) var(--ease),border-color var(--dur-micro) var(--ease)}
.aic__usagechip:hover{background:var(--bg-sunken);color:var(--text-primary)}
.aic__usagechip[aria-pressed="true"]{background:var(--color-ai-soft);border-color:var(--color-ai);color:var(--color-ai)}
.aic__usagechip-v{font-weight:var(--fw-bold)}
.aic__usagechip-l{color:var(--text-tertiary)}

/* ---- The usage view ----------------------------------------------------- */
.aic__usage{display:none;flex:1;min-height:0;overflow-y:auto}
.aic[data-view="usage"] .aic__usage{display:block}
/* The composer and its conversation step aside while the meter is on screen;
   they are not unmounted, so the draft survives a look at the numbers. */
.aic[data-view="usage"] .aic__conv,
.aic[data-view="usage"] .aic__suggest,
.aic[data-view="usage"] .aic__tray,
.aic[data-view="usage"] .aic__card{display:none}
.aic__usagebody{max-width:900px;margin-inline:auto;padding:var(--s-2) 0 var(--s-4);
  display:flex;flex-direction:column;gap:var(--s-4)}
.aic__usage-wait{display:grid;place-items:center;padding:var(--s-6,32px)}
.aic__usage-note{margin:0;padding:8px 12px;border-radius:var(--r-md);
  background:var(--color-ai-soft);color:var(--text-secondary);font-size:var(--fs-2xs)}
.aic__usagehead{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.aic__usagehead h3{margin:0;font-size:var(--fs-md);font-weight:var(--fw-semibold);color:var(--text-primary)}
.aic__scope{padding:2px 9px;border-radius:999px;font-size:10px;font-weight:var(--fw-bold);
  letter-spacing:.04em;text-transform:uppercase;
  background:var(--bg-sunken);color:var(--text-secondary)}
.aic__scope[data-scope="all"]{background:var(--sev-high-bg);color:var(--sev-high-tx,var(--sev-high))}
.aic__range{display:inline-flex;border:1px solid var(--border-default);border-radius:999px;overflow:hidden}
.aic__rangebtn{height:26px;padding:0 12px;border:0;background:none;cursor:pointer;
  font:inherit;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-tertiary)}
.aic__rangebtn+.aic__rangebtn{border-left:1px solid var(--border-default)}
.aic__rangebtn.is-on{background:var(--color-ai);color:#fff}

.aic__stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-3)}
.aic__stat{display:flex;flex-direction:column;gap:2px;padding:var(--s-3) var(--s-4);
  border:1px solid var(--border-default);border-radius:var(--r-lg);background:var(--bg-surface)}
.aic__stat b{font-size:24px;line-height:1.1;font-weight:var(--fw-bold);color:var(--text-primary);
  font-variant-numeric:tabular-nums}
.aic__stat-l{font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-secondary)}
.aic__stat-s{font-size:10px;color:var(--text-tertiary)}

.aic__card2{border:1px solid var(--border-default);border-radius:var(--r-lg);
  background:var(--bg-surface);padding:var(--s-4)}
.aic__card2 h4{margin:0 0 var(--s-3);font-size:var(--fs-2xs);letter-spacing:.08em;
  text-transform:uppercase;font-weight:var(--fw-bold);color:var(--text-tertiary)}
.aic__bars{display:flex;align-items:flex-end;gap:2px;height:120px}
.aic__bar{flex:1;min-width:2px;height:100%;display:flex;align-items:flex-end;border-radius:3px}
.aic__bar i{display:block;width:100%;border-radius:3px 3px 0 0;background:var(--color-ai)}
.aic__bar.is-empty i{background:var(--border-default)}
.aic__bar:hover i{background:#6D28D9}

.aic__mtable{display:flex;flex-direction:column;gap:2px;font-variant-numeric:tabular-nums}
.aic__mtr{display:grid;grid-template-columns:minmax(0,2.4fr) repeat(5,minmax(0,1fr));
  gap:var(--s-2);align-items:center;padding:7px 8px;border-radius:var(--r-sm,4px);
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.aic__mtr > span:not(.aic__mname){text-align:right}
.aic__mtr--head{font-size:10px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text-tertiary);font-weight:var(--fw-bold)}
.aic__mname{position:relative;min-width:0;overflow:hidden}
.aic__mname b{position:relative;z-index:1;color:var(--text-primary);font-weight:var(--fw-semibold);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
/* The share bar sits behind the name rather than in its own column: it is a
   proportion, and reading it against the label it belongs to costs no width. */
.aic__mshare{position:absolute;inset:-4px auto -4px 0;z-index:0;display:block;
  border-radius:var(--r-sm,4px);background:var(--color-ai-soft)}

@media (max-width:900px){
  .aic__stats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .aic__mtr{grid-template-columns:minmax(0,2fr) repeat(2,minmax(0,1fr));}
  .aic__mtr > span:nth-child(3),.aic__mtr > span:nth-child(4){display:none}
}
@media (max-width:640px){
  /* The action row is narrow on a phone; the domain has one usable value
     today, so it is the first thing to give way. Model and language stay. */
  .aic__sel--domain{display:none}
  .aic__menu--models{width:min(320px,calc(100vw - 32px))}
  .aic__usagechip-l{display:none}
}

/* A microphone that fails must say so where the operator is looking. */
.aic__notice{margin:0;padding:6px 12px;border-radius:var(--r-md);
  background:var(--sev-medium-bg);color:var(--sev-medium-tx,var(--sev-medium));
  font-size:var(--fs-2xs);font-weight:var(--fw-medium)}
.aic[data-recording="true"] .aic__notice{background:var(--color-ai-soft);color:var(--color-ai)}

/* ===== Tab strip: clipping made legible ==================================
   Eight tabs want ~970px; a laptop card offers ~745. The strip scrolled
   already, but with the scrollbar hidden the only visible result was a label
   sliced in half — which reads as a broken layout, not as "there is more".
   The edge now fades wherever there is something to scroll to, and the
   selected tab is brought into view (see wireTabScroll/revealTab in ui.js).
   A mask is used rather than an overlay gradient so it works on any card
   background, light or dark. ===================================== */
.tabs{scroll-behavior:smooth;overscroll-behavior-x:contain}
.tabs[data-of="end"]  {mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 40px),transparent 100%)}
.tabs[data-of="start"]{mask-image:linear-gradient(90deg,transparent 0,#000 40px,#000 100%)}
.tabs[data-of="both"] {mask-image:linear-gradient(90deg,transparent 0,#000 40px,#000 calc(100% - 40px),transparent 100%)}

/* Tighter tabs buy back ~40px across eight of them without shrinking the
   label or the icon the Founder asked to enlarge. */
.tabs--inset .tab{padding-inline:10px}
.tabs--inset{gap:2px}

/* A pointer device gets a slim scrollbar on hover; a trackpad or touch just
   swipes. Neither is left guessing. */
.tabs{scrollbar-width:none}
.tabs:hover{scrollbar-width:thin;scrollbar-color:var(--border-default) transparent}
.tabs::-webkit-scrollbar{height:0}
.tabs:hover::-webkit-scrollbar{height:4px}
.tabs:hover::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:4px}
@media (prefers-reduced-motion: reduce){ .tabs{scroll-behavior:auto} }

/* ═══ Catalogue (Command Centre) ═══════════════════════════════════════════
   A library, not a settings page: one grid of everything work is filed under,
   narrowed by a rail. Built entirely from tokens.css — the categorical palette
   for kinds, the severity palette for priority levels, and nothing invented, so
   a theme change reaches this screen like every other.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- KPI strip ----------------------------------------------------------
   Six tiles that are also the primary filter. Auto-fit rather than a fixed
   column count: at 1100px they are six, at 700px three, at phone width two —
   and nothing is ever cut in half. */
/* Counted columns rather than auto-fit. auto-fit put five tiles on one row and
   the sixth alone underneath at 1280 — a 7px miss that reads as a mistake
   rather than as a sixth number. Six across only once the tiles are wide enough
   for "Priority levels" not to truncate; three, then two, below that, so every
   row is full at every width. */
.cat__kpis{display:grid;gap:var(--s-3);margin-bottom:var(--s-4);
  grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:600px){ .cat__kpis{grid-template-columns:repeat(3,minmax(0,1fr))} }
/* Six across needs 1600: at 1280 it gave each tile 146px, and "Priority
   levels" and "0 no department" both truncated — six numbers nobody can read
   are worth less than three they can. Three by two is balanced either way. */
@media (min-width:1600px){ .cat__kpis{grid-template-columns:repeat(6,minmax(0,1fr))} }
.cat__kpi{display:flex;align-items:center;gap:var(--s-3);min-width:0;
  padding:var(--s-3) var(--s-4);text-align:left;cursor:pointer;
  background:var(--bg-surface);border:1px solid var(--border-default);
  border-radius:var(--r-lg,12px);box-shadow:var(--shadow-sm);
  transition:border-color .12s,box-shadow .12s,transform .12s}
.cat__kpi:hover{border-color:var(--border-strong);box-shadow:var(--shadow-md)}
.cat__kpi:active{transform:translateY(1px)}
/* The pressed tile is the filter in force. A ring rather than a fill, so the
   number stays the loudest thing on the tile. */
.cat__kpi[aria-pressed="true"]{border-color:var(--accent);
  box-shadow:0 0 0 1px var(--accent),var(--shadow-sm)}
.cat__kpi--flat{cursor:default}
.cat__kpi--flat:hover{border-color:var(--border-default);box-shadow:var(--shadow-sm)}
.cat__kpiicon{flex:0 0 auto;display:grid;place-items:center;width:36px;height:36px;
  border-radius:var(--r-md);color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 12%,transparent)}
.cat__kpitext{display:flex;flex-direction:column;min-width:0;line-height:1.2}
.cat__kpin{font-size:var(--fs-xl);font-weight:var(--fw-semibold);color:var(--text-primary);
  font-variant-numeric:tabular-nums}
.cat__kpilabel{font-size:var(--fs-2xs);color:var(--text-secondary);margin-top:2px}
.cat__kpisub{font-size:11px;color:var(--text-tertiary);min-height:15px}
/* At phone width the tile is 162px and this line truncates to "0 no departm…".
   A secondary figure that cannot be read is worth less than the space, and the
   number it qualifies is still on screen. */
@media (max-width:599px){ .cat__kpisub{display:none} }

/* ---- Toolbar ------------------------------------------------------------ */
.cat__bar{display:flex;flex-wrap:wrap;gap:var(--s-2);align-items:center;
  margin-bottom:var(--s-4)}
.cat__searchwrap{position:relative;flex:1 1 260px;min-width:180px;display:flex;
  align-items:center}
.cat__searchwrap svg{position:absolute;left:11px;color:var(--text-tertiary);
  pointer-events:none}
.cat__search{width:100%;padding-left:34px}
.cat__sel{flex:0 1 190px;min-width:140px}
.cat__views{display:inline-flex;flex:none;border:1px solid var(--border-default);
  border-radius:var(--r-sm,4px);overflow:hidden;background:var(--bg-surface)}
.cat__view{display:grid;place-items:center;width:34px;height:34px;border:0;
  background:transparent;color:var(--text-tertiary);cursor:pointer}
.cat__view+.cat__view{border-left:1px solid var(--border-default)}
.cat__view:hover{background:var(--bg-hover);color:var(--text-primary)}
.cat__view[aria-pressed="true"]{background:var(--accent-quiet);color:var(--sev-info-tx)}

/* ---- Three columns ------------------------------------------------------
   The rails are the frame; the grid is the content. They collapse in order of
   how much each is worth on a small screen: the right rail is a summary of what
   the grid already shows, so it goes first. */
.cat__body{display:grid;gap:var(--s-4);align-items:start;
  grid-template-columns:216px minmax(0,1fr) 284px}
/* Both rails stay beside the grid down to 1200 — a laptop is where this screen
   is actually used, and the rails are how it is navigated. The middle drops to
   two cards a row there, which costs less than putting the summary below the
   fold. Under 1200 the right rail becomes a band, because it summarises what
   the grid already shows and is the cheapest thing to move. */
@media (max-width:1200px){ .cat__body{grid-template-columns:216px minmax(0,1fr)}
  .cat__side{grid-column:1 / -1;display:grid;gap:var(--s-4);
    grid-template-columns:repeat(auto-fit,minmax(260px,1fr))} }
@media (max-width:900px){ .cat__body{grid-template-columns:minmax(0,1fr)}
  .cat__rail{display:grid;gap:4px;grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
  .cat__railhead{grid-column:1 / -1} }

.cat__rail{display:flex;flex-direction:column;gap:2px;position:sticky;top:var(--s-4)}
@media (max-width:900px){ .cat__rail{position:static} }
.cat__railhead{margin:var(--s-4) 0 var(--s-2);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary);font-weight:var(--fw-semibold)}
.cat__railhead:first-child{margin-top:0}
.cat__facet{position:relative;display:flex;align-items:center;gap:var(--s-2);
  width:100%;padding:7px var(--s-2);border:0;border-radius:var(--r-md);
  background:transparent;cursor:pointer;text-align:left;
  font-size:var(--fs-xs);color:var(--text-secondary)}
.cat__facet:hover{background:var(--bg-hover);color:var(--text-primary)}
.cat__facet[aria-pressed="true"]{background:var(--accent-quiet);color:var(--sev-info-tx);
  font-weight:var(--fw-semibold)}
.cat__facetdot{flex:0 0 auto;display:grid;place-items:center;width:18px;height:18px;
  border-radius:5px;color:var(--tone,var(--text-tertiary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 14%,transparent)}
.cat__facetname{flex:1 1 auto;min-width:0}
.cat__facetn{flex:0 0 auto;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.cat__facet[aria-pressed="true"] .cat__facetn{color:var(--sev-info-tx)}
/* The load bar sits under the row rather than beside it: a department name can
   be "Bureau — Tirupati", and a bar competing for that width would squeeze the
   name to three letters. */
.cat__facet--bar{padding-bottom:11px}
.cat__facetbar{position:absolute;left:var(--s-2);right:var(--s-2);bottom:5px;height:2px;
  border-radius:2px;background:var(--border-subtle)}
.cat__facetbar::after{content:"";display:block;height:100%;width:var(--w,0%);
  border-radius:2px;background:var(--tone,var(--accent));opacity:.55}

/* ---- The grid ----------------------------------------------------------- */
/* min-width:0 on the grid child, or a long project name makes the middle track
   wider than its share and squeezes both rails. */
.cat__mainwrap{min-width:0}
.cat__count{margin:0 0 var(--s-3);font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
/* 196px, not 228: at the width this column actually gets between two rails,
   228 lands three cards just over the line and falls back to two — which leaves
   each card half a column of empty space beside a short name. */
.cat__grid[data-view="grid"]{display:grid;gap:var(--s-3);
  grid-template-columns:repeat(auto-fill,minmax(196px,1fr))}
.cat__grid[data-view="list"]{display:flex;flex-direction:column;gap:2px}

.cat__card{position:relative;display:flex;flex-direction:column;min-width:0;
  padding:var(--s-3) var(--s-4) var(--s-3);background:var(--bg-surface);
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  box-shadow:var(--shadow-sm);transition:border-color .12s,box-shadow .12s}
.cat__card:hover{border-color:var(--border-strong);box-shadow:var(--shadow-md)}
/* Retired stays legible: it is still the record of what happened, and a row
   faded to illegibility cannot be read well enough to restore. */
.cat__card[data-off]{background:var(--bg-sunken)}
.cat__card[data-off] .cat__name{color:var(--text-secondary)}
.cat__cardtop{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-2);margin-bottom:var(--s-2)}
.cat__tile{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;
  border-radius:var(--r-md);color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 12%,transparent)}
.cat__tile--sm{width:26px;height:26px;border-radius:7px}
/* .04em, not .06: "PRIORITY LEVELS" is the longest kind and at .06 it ran 5px
   past the card's eyebrow slot, so every priority card read "PRIORITY LEV…". */
.cat__kind{display:block;min-width:0;font-size:10px;letter-spacing:.04em;
  text-transform:uppercase;color:var(--text-tertiary);font-weight:var(--fw-semibold);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cat__more{flex:0 0 auto;display:grid;place-items:center;width:28px;height:28px;
  border:0;border-radius:var(--r-sm,4px);background:transparent;
  color:var(--text-tertiary);cursor:pointer}
.cat__more:hover{background:var(--bg-hover);color:var(--text-primary)}
.cat__name{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary);line-height:1.35}
.cat__meta{margin:3px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);min-height:16px}
.cat__chips{display:flex;flex-wrap:wrap;gap:5px;margin-top:var(--s-3)}
.cat__chip{padding:2px 8px;border-radius:var(--r-pill,999px);font-size:11px;
  font-weight:var(--fw-semibold);background:var(--accent-quiet);color:var(--sev-info-tx);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* --text-tertiary is documented as 4.8:1 *on the card*; on the sunken grey it
   measures 4.34 at 11px, which is under AA. The secondary token keeps the chip
   quieter than the accent one and measures 6.8. */
.cat__chip--quiet{background:var(--bg-sunken);color:var(--text-secondary);
  font-weight:var(--fw-medium)}
/* A chip that carries a consequence, not a label — today, "External". Uses the
   severity tokens so it reads at the same weight as the rest of the console's
   warnings rather than inventing a new one. */
.cat__chip[data-sev="medium"]{background:var(--sev-medium-bg);color:var(--sev-medium-tx);
  border:1px solid var(--sev-medium-bd);font-weight:var(--fw-semibold)}
.cat__deptpick{margin-top:var(--s-3);width:100%}
.cat__cardfoot{display:flex;align-items:center;gap:var(--s-2);
  margin-top:var(--s-3);padding-top:var(--s-3);
  border-top:1px solid var(--border-subtle)}
.cat__who{display:flex;align-items:center;gap:6px;min-width:0;flex:1 1 auto;
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.cat__who--none{color:var(--text-tertiary)}
.cat__stats{flex:0 0 auto;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.cat__dot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;
  background:var(--text-disabled)}
.cat__dot[data-on]{background:var(--sev-ok)}

/* ---- List view ---------------------------------------------------------- */
.cat__row{display:grid;align-items:center;gap:var(--s-3);padding:9px var(--s-3);
  border-radius:var(--r-md);background:var(--bg-surface);
  border:1px solid var(--border-subtle);
  grid-template-columns:auto minmax(120px,1.4fr) 92px minmax(0,1.6fr) auto 28px}
.cat__row:hover{border-color:var(--border-default);background:var(--bg-hover)}
.cat__row[data-off]{background:var(--bg-sunken)}
.cat__rowname{font-size:var(--fs-xs);font-weight:var(--fw-semibold);color:var(--text-primary)}
.cat__rowmeta{font-size:var(--fs-2xs);color:var(--text-tertiary)}
@media (max-width:820px){
  .cat__row{grid-template-columns:auto minmax(0,1fr) auto 28px}
  .cat__row .cat__kind,.cat__row .cat__rowmeta{display:none}
}

/* ---- Right rail --------------------------------------------------------- */
.cat__side{display:flex;flex-direction:column;gap:var(--s-4)}
.cat__panel{background:var(--bg-surface);border:1px solid var(--border-default);
  border-radius:var(--r-lg,12px);box-shadow:var(--shadow-sm);padding:var(--s-4);
  min-width:0}
.cat__panelhead{display:flex;align-items:baseline;gap:var(--s-3);margin-bottom:var(--s-3)}
.cat__panelhead h2{margin:0;flex:1 1 auto;font-size:var(--fs-sm);
  font-weight:var(--fw-semibold);color:var(--text-primary)}
.cat__link{flex:0 0 auto;border:0;background:transparent;padding:0;cursor:pointer;
  font-size:var(--fs-2xs);color:var(--text-link);font-weight:var(--fw-semibold)}
.cat__link:hover{text-decoration:underline}
.cat__people,.cat__prios{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:2px}
.cat__person{display:flex;align-items:center;gap:var(--s-2);width:100%;
  padding:6px;border:0;border-radius:var(--r-md);background:transparent;
  cursor:pointer;text-align:left;color:var(--text-tertiary)}
.cat__person:hover{background:var(--bg-hover)}
.cat__persontext{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
.cat__personname{font-size:var(--fs-xs);font-weight:var(--fw-semibold);color:var(--text-primary)}
.cat__personsub{font-size:11px;color:var(--text-tertiary)}
.cat__personn{flex:0 0 auto;font-size:var(--fs-2xs);color:var(--text-secondary);
  font-variant-numeric:tabular-nums}
/* A distribution, not a list: a level matters in proportion to what sits in it. */
.cat__prio{position:relative;display:flex;align-items:center;gap:var(--s-2);width:100%;
  padding:7px 6px 11px;border:0;border-radius:var(--r-md);background:transparent;
  cursor:pointer;text-align:left}
.cat__prio:hover{background:var(--bg-hover)}
.cat__priodot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;
  background:var(--sev-info)}
.cat__priodot[data-sev="critical"]{background:var(--sev-critical)}
.cat__priodot[data-sev="high"]{background:var(--sev-high)}
.cat__priodot[data-sev="medium"]{background:var(--sev-medium)}
.cat__priodot[data-sev="low"]{background:var(--sev-low)}
.cat__prioname{flex:1 1 auto;min-width:0;font-size:var(--fs-xs);color:var(--text-primary)}
.cat__prion{flex:0 0 auto;font-size:var(--fs-2xs);color:var(--text-secondary);
  font-variant-numeric:tabular-nums}
.cat__priobar{position:absolute;left:6px;right:6px;bottom:5px;height:2px;
  border-radius:2px;background:var(--border-subtle)}
.cat__priobar::after{content:"";display:block;height:100%;width:var(--w,0%);
  border-radius:2px;background:var(--accent);opacity:.5}
.cat__quicks{display:flex;flex-direction:column;gap:2px}
.cat__quick{display:flex;align-items:center;gap:var(--s-2);width:100%;padding:8px 6px;
  border:0;border-radius:var(--r-md);background:transparent;cursor:pointer;
  text-align:left;font-size:var(--fs-xs);color:var(--text-secondary)}
.cat__quick:hover{background:var(--bg-hover);color:var(--text-primary)}
.cat__quick span{flex:1 1 auto;min-width:0}
.cat__quick svg:first-child{flex:0 0 auto;color:var(--text-tertiary)}
.cat__quick svg:last-child{flex:0 0 auto;color:var(--text-disabled)}

/* ---- The one menu ------------------------------------------------------- */
/* Fixed, not absolute: see placeMenu() in views.catalogue.js. Absolute put the
   header's menu in the corner of the window, because it positioned against the
   menu's parent rather than against the button. */
.catmenu{position:fixed;z-index:60;min-width:172px;max-width:min(260px,calc(100vw - 24px));
  padding:var(--s-1);background:var(--bg-overlay);
  border:1px solid var(--border-default);border-radius:var(--r-md);
  box-shadow:var(--shadow-lg);display:flex;flex-direction:column}
.catmenu__item{display:flex;align-items:center;gap:var(--s-2);text-align:left;
  padding:8px var(--s-3);border:0;background:transparent;border-radius:var(--r-sm,4px);
  color:var(--text-primary);font-size:var(--fs-xs);font-weight:var(--fw-medium);
  cursor:pointer;white-space:nowrap}
.catmenu__item:hover{background:var(--bg-hover)}
.catmenu__item.is-danger{color:var(--sev-critical)}
.catmenu__item svg{flex:0 0 auto;opacity:.7}
.cat__card,.cat__row{position:relative}

.cat__pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3)}

/* ---- The categorical palette, bound once ------------------------------- */
[data-tone="1"]{--tone:var(--cat-1)} [data-tone="2"]{--tone:var(--cat-2)}
[data-tone="3"]{--tone:var(--cat-3)} [data-tone="4"]{--tone:var(--cat-4)}
[data-tone="5"]{--tone:var(--cat-5)} [data-tone="6"]{--tone:var(--cat-6)}
[data-tone="7"]{--tone:var(--cat-7)} [data-tone="8"]{--tone:var(--cat-8)}
.cat__tile[data-sev="critical"]{--tone:var(--sev-critical)}
.cat__tile[data-sev="high"]{--tone:var(--sev-high)}
.cat__tile[data-sev="medium"]{--tone:var(--sev-medium)}
.cat__tile[data-sev="low"]{--tone:var(--sev-low)}

/* ═══ Create / Edit Department ═════════════════════════════════════════════
   A long form that has to stay readable at a glance from a desk: labels are
   --text-primary rather than a muted grey, hints are secondary, and only the
   character counters sit at tertiary. Nothing here is lighter than the eye
   needs at two feet.
   ────────────────────────────────────────────────────────────────────────── */
.dep__head{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--s-4);
  margin-bottom:var(--s-4)}
.dep__head h1{margin:0;font-size:var(--fs-2xl);line-height:var(--lh-2xl);
  font-weight:var(--fw-bold);color:var(--text-primary);letter-spacing:-.01em}
.dep__head p{margin:4px 0 0;font-size:var(--fs-sm);color:var(--text-secondary);max-width:64ch}
.dep__headactions{display:flex;gap:var(--s-2);margin-left:auto;flex:0 0 auto}
@media (max-width:640px){ .dep__headactions{margin-left:0;width:100%}
  .dep__headactions .btn{flex:1 1 auto} }

.dep__banner{margin-bottom:var(--s-4);padding:10px var(--s-4);border-radius:var(--r-md);
  background:var(--sev-critical-bg);border:1px solid var(--sev-critical-bd);
  color:var(--sev-critical-tx);font-size:var(--fs-xs);font-weight:var(--fw-semibold)}

.dep__body{display:grid;gap:var(--s-4);align-items:start;
  grid-template-columns:minmax(0,1fr) 340px}
@media (max-width:1180px){ .dep__body{grid-template-columns:minmax(0,1fr)} }
.dep__forms{display:flex;flex-direction:column;gap:var(--s-4);min-width:0}

.dep__sec,.dep__panel{background:var(--bg-surface);border:1px solid var(--border-default);
  border-radius:var(--r-lg,12px);box-shadow:var(--shadow-sm);padding:var(--s-4);min-width:0}
.dep__sechead,.dep__panelhead{display:flex;gap:var(--s-3);align-items:flex-start;
  margin-bottom:var(--s-4)}
.dep__sechead h2,.dep__panelhead h2{margin:0;font-size:var(--fs-lg);
  font-weight:var(--fw-semibold);color:var(--text-primary)}
.dep__sechead p,.dep__panelhead p{margin:2px 0 0;font-size:var(--fs-2xs);
  color:var(--text-secondary)}
.dep__secicon{flex:0 0 auto;display:grid;place-items:center;width:32px;height:32px;
  border-radius:var(--r-md);background:var(--accent-quiet);color:var(--sev-info-tx)}
.dep__subhead{margin:0 0 var(--s-3);font-size:var(--fs-sm);
  font-weight:var(--fw-semibold);color:var(--text-primary)}

.dep__grid{display:grid;gap:var(--s-4)}
.dep__grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.dep__grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.dep__grid+.dep__grid{margin-top:var(--s-4)}
.dep__span2{grid-column:1 / -1}
@media (max-width:760px){
  .dep__grid--2,.dep__grid--3{grid-template-columns:minmax(0,1fr)}
}

/* ---- One field ----------------------------------------------------------
   The label is primary-weight text, not a grey caption: it is the question
   being asked, and it has to read from across a desk. */
.dep__fld{display:block;min-width:0}
.dep__label{display:block;font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary);margin-bottom:2px}
.dep__req{color:var(--sev-critical);margin-left:3px}
.dep__hint{margin:0 0 6px;font-size:var(--fs-2xs);color:var(--text-secondary);line-height:1.5}
.dep__fld .input{width:100%}
.dep__under{display:flex;align-items:baseline;gap:var(--s-2);margin-top:4px;min-height:16px}
.dep__err{margin:0;flex:1 1 auto;font-size:var(--fs-2xs);color:var(--sev-critical-tx);
  font-weight:var(--fw-semibold)}
/* ---- The live note under a box ----------------------------------------
   TWO TONES, AND A MARK BESIDE EACH. Colour alone is not a message: roughly one
   man in twelve cannot separate the red from the green, and a note that only
   differs by hue reads as the same note twice. The ✓ and ! carry the meaning
   and the colour reinforces it.

   Both inks are the -tx variants, which are the darkened ones the token file
   reserves for text; measured on this form's white surface they clear 7:1, and
   the dark theme redefines them for its own ground. */
.dep__cue{margin:0;flex:1 1 auto;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.dep__cue[data-tone]::before{font-weight:var(--fw-semibold);margin-right:5px}
.dep__cue[data-tone="ok"]{color:var(--sev-ok-tx)}
.dep__cue[data-tone="ok"]::before{content:"\2713"}
.dep__cue[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}
.dep__cue[data-tone="bad"]::before{content:"\0021"}
/* The save-time error still wins the row: it is the one that stopped a save. */
.dep__err:not([hidden]) ~ .dep__cue{display:none}
/* In the little new-type form the note sits directly under its box, not in a
   shared row, so it needs its own spacing. */
.dep__leadfld > .dep__cue{margin-top:3px}

.dep__count{margin-left:auto;font-size:11px;color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.dep__count[data-over]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}
/* The invalid field is outlined, not just annotated: on a form this long the
   message alone is easy to scroll past. */
.dep__fld[data-bad] .input{border-color:var(--sev-critical);
  box-shadow:0 0 0 1px var(--sev-critical)}

/* ---- The chooser: core or sub-department -------------------------------- */
/* Two cards, equal weight, because neither is the default. A radio pair would
   have been smaller and would have made one of them look like the answer. */
.depc{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:var(--s-4);margin-top:var(--s-4)}
.depc__card{display:flex;flex-direction:column;align-items:flex-start;gap:8px;
  padding:var(--s-5);text-align:left;cursor:pointer;
  border:1px solid var(--border-default);border-radius:var(--r-lg,14px);
  background:var(--bg-surface);transition:border-color .15s,box-shadow .15s,transform .15s}
.depc__card:hover{border-color:var(--accent);box-shadow:var(--shadow-md,0 6px 20px rgba(15,23,42,.08));
  transform:translateY(-2px)}
.depc__card:focus-visible{outline:2px solid var(--border-focus);outline-offset:3px}
.depc__icon{display:grid;place-items:center;width:44px;height:44px;border-radius:12px;
  color:var(--accent);background:var(--accent-quiet)}
.depc__card[data-kind="sub"] .depc__icon{color:var(--sev-info-tx);background:var(--sev-info-bg)}
.depc__title{font-size:var(--fs-lg);font-weight:var(--fw-semibold);color:var(--text-primary)}
.depc__lead{font-size:var(--fs-sm);color:var(--text-secondary);line-height:var(--lh-relaxed)}
.depc__points{margin:4px 0 0;padding:0;list-style:none;display:grid;gap:5px}
.depc__points li{position:relative;padding-left:16px;font-size:var(--fs-2xs);
  color:var(--text-secondary)}
.depc__points li::before{content:"";position:absolute;left:3px;top:7px;width:5px;height:5px;
  border-radius:50%;background:var(--accent)}
.depc__go{margin-top:auto;padding-top:10px;display:inline-flex;align-items:center;gap:4px;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-link)}
.depc__note{margin-top:var(--s-4);font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* ---- The step rail ------------------------------------------------------ */
/* Sticky, so the sequence is legible from anywhere in a long form, and the tick
   means "has what it needs" rather than "you scrolled past it". */
.dep__steps{position:sticky;top:0;z-index:5;display:flex;gap:6px;flex-wrap:wrap;
  padding:10px 0;margin-bottom:var(--s-3);background:var(--bg-canvas,var(--bg-surface));
  border-bottom:1px solid var(--border-default)}
.dep__step{display:inline-flex;align-items:center;gap:6px;padding:5px 11px 5px 5px;
  border:1px solid var(--border-default);border-radius:var(--r-pill,999px);
  background:var(--bg-surface);cursor:pointer;color:var(--text-secondary);
  font-size:var(--fs-2xs);font-weight:var(--fw-medium)}
.dep__step:hover{border-color:var(--border-strong);color:var(--text-primary)}
.dep__step:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.dep__stepn{display:grid;place-items:center;width:20px;height:20px;border-radius:50%;
  background:var(--bg-sunken);color:var(--text-tertiary);font-size:10px;
  font-weight:var(--fw-semibold);flex:0 0 auto}
.dep__step[data-done]{border-color:var(--sev-low-bd);color:var(--sev-low-tx)}
.dep__step[data-done] .dep__stepn{background:var(--sev-low-bg);color:var(--sev-low-tx)}

/* ---- Kind chip and level ------------------------------------------------ */
.dep__eyebrow{display:flex;align-items:center;gap:6px;margin-bottom:5px}
.dep__kind{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;
  border-radius:var(--r-pill,999px);font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);background:var(--accent-quiet);color:var(--sev-info-tx)}
.dep__kind[data-kind="sub"]{background:var(--sev-info-bg);color:var(--sev-info-tx)}
.dep__level{font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* ---- Placement ---------------------------------------------------------- */
.dep__note{display:flex;gap:10px;padding:var(--s-3) var(--s-4);border-radius:var(--r-md);
  background:var(--bg-sunken);border:1px solid var(--border-default);margin-bottom:var(--s-4)}
.dep__noteicon{display:grid;place-items:center;width:32px;height:32px;border-radius:9px;
  background:var(--accent-quiet);color:var(--sev-info-tx);flex:0 0 auto}
.dep__note strong{display:block;font-size:var(--fs-sm);color:var(--text-primary)}
.dep__note p{margin:2px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}
/* ---- The searchable dropdown -------------------------------------------- */
/* ABSOLUTE, NOT FIXED. The Catalogue's card menu is fixed and clamped to the
   viewport because it hangs off a button in a scrolling grid. This one belongs
   to a form field: absolute inside the field keeps it glued to its control
   while the page scrolls, which is what a dropdown is expected to do. */
.cb{position:relative}
.cb__btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  cursor:pointer}
.cb__val{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.cb__val[data-empty]{color:var(--text-tertiary)}
.cb__chev{display:grid;place-items:center;flex:0 0 auto;color:var(--text-tertiary)}
.cb__btn[aria-expanded="true"]{border-color:var(--accent)}
.cb__pop{position:absolute;z-index:40;top:calc(100% + 4px);left:0;right:0;
  padding:6px;border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-surface);box-shadow:var(--shadow-md,0 10px 30px rgba(15,23,42,.14))}
.cb__search{width:100%;margin-bottom:6px}
.cb__list{list-style:none;margin:0;padding:0;max-height:230px;overflow-y:auto}
.cb__opt{display:flex;align-items:baseline;gap:8px;padding:6px 9px;cursor:pointer;
  border-radius:var(--r-sm,8px);font-size:var(--fs-sm);color:var(--text-primary)}
.cb__opt:hover,.cb__opt[data-active]{background:var(--bg-sunken)}
.cb__opt[aria-selected="true"]{background:var(--accent-quiet);color:var(--sev-info-tx);
  font-weight:var(--fw-semibold)}
.cb__opt[aria-disabled="true"]{opacity:.5;cursor:default}
.cb__optlabel{flex:0 1 auto;min-width:0}
.cb__optsub{flex:1 1 auto;min-width:0;text-align:right;font-size:var(--fs-2xs);
  color:var(--text-tertiary)}
.cb__none{margin:0;padding:10px 9px;font-size:var(--fs-2xs);color:var(--text-tertiary)}
/* Disabled, the way a native select looks disabled — the priority drawer locks
   its mapping for built-in rows. */
.cb[data-disabled] .cb__btn{opacity:.6;cursor:not-allowed;background:var(--bg-sunken)}
/* The toolbar variant sits in a row of filters, so it sizes to its content
   rather than stretching like a form field. */
.cat__sel.cb{min-width:150px}
.cat__sel .cb__btn{height:var(--ctl-h,34px)}
.cat__deptpick.cb .cb__btn{font-size:var(--fs-2xs);padding-block:5px}

/* ---- Placement: a level, a department, and its tree --------------------- */



/* Still used by the Department-context panel in the rail, which draws the
   chosen department's mark at the same size the old placement tree did. */
.dep__tmark{display:grid;place-items:center;width:30px;height:30px;border-radius:9px;
  flex:0 0 auto;color:var(--mark);background:color-mix(in srgb,var(--mark) 14%,transparent)}
/* The new-kind form: three fields, the two short ones side by side on a wide
   form and stacked when there is no room for both. */
.dep__newtype{margin-top:8px;display:flex;flex-direction:column;gap:var(--s-3);
  padding:var(--s-3);border:1px solid var(--border-default);
  border-radius:var(--r-md);background:var(--bg-sunken)}
/* start, not stretch: a hint under one box must not stretch the other's. */
.dep__newtypegrid{display:grid;gap:var(--s-3);grid-template-columns:1fr;
  align-items:start}
@media (min-width:560px){ .dep__newtypegrid{grid-template-columns:1.6fr 1fr} }
.dep__newtypedesc textarea{width:100%;resize:vertical;min-height:52px}
.dep__newtypego{display:flex;gap:var(--s-2);flex-wrap:wrap}

/* ---- Leadership chips --------------------------------------------------- */
/* The primary head is a role, not a position in a list, so it says so. */
/* The third rung sits UNDER the two above it, with a rule to say so — the
   indent is the org chart, not decoration. */
.dep__tier3{margin-top:var(--s-4);padding-top:var(--s-4);
  border-top:1px solid var(--border-default)}
.dep__person[data-primary]{border-color:var(--accent);background:var(--accent-quiet)}
.dep__primary{font-size:10px;font-weight:var(--fw-semibold);letter-spacing:.03em;
  text-transform:uppercase;color:var(--sev-info-tx)}

/* Still used by "New type" beside the department-type select. It was defined
   with the palette disclosure and went out with it; the button did not. */
.dep__more{align-self:flex-start;margin-top:8px;padding:3px 9px;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);color:var(--text-link);background:none;cursor:pointer;
  border:1px solid var(--border-default);border-radius:var(--r-pill,999px)}
.dep__more:hover{border-color:var(--border-strong);background:var(--bg-sunken)}
.dep__more:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* ---- The colour picker --------------------------------------------------
   The reference design's shape — area, hue rail, hex, channels — in this
   console's own CSS. The area's two gradients are the standard construction:
   white to transparent left-to-right over the hue, black to transparent
   bottom-to-top, so x is saturation and y is brightness. */
.dep__picker{margin-top:10px;padding:12px;border:1px solid var(--border-default);
  border-radius:var(--r-md);background:var(--bg-sunken);max-width:340px}
/* The working surface: saturation left-to-right over the chosen hue, brightness
   bottom-to-top. Two gradients over a hue-coloured box — the standard
   construction, and the one every designer's hand already knows. */
.dep__area{position:relative;height:168px;border-radius:var(--r-md);
  cursor:crosshair;touch-action:none;overflow:hidden}
.dep__areabg{position:absolute;inset:0;background-color:var(--hue,#2563A8);
  background-image:linear-gradient(to top,#000,transparent),
                   linear-gradient(to right,#fff,transparent)}
.dep__area:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.dep__areathumb,.dep__hthumb{position:absolute;width:16px;height:16px;
  border-radius:50%;background:var(--dot,#fff);border:2px solid #fff;
  box-shadow:0 0 0 1px rgba(15,23,42,.35);transform:translate(-50%,-50%);
  pointer-events:none}
/* Named hrail, not rail: `.dep__rail` is the form's right-hand column and would
   otherwise style this 12px slider as a sticky flex container. */
.dep__hrail{position:relative;height:12px;margin-top:12px;border-radius:999px;
  cursor:pointer;touch-action:none;
  background:linear-gradient(to right,#f00 0%,#ff0 17%,#0f0 33%,#0ff 50%,
    #00f 67%,#f0f 83%,#f00 100%);
  box-shadow:inset 0 0 0 1px rgba(15,23,42,.12)}
.dep__hrail:focus-visible{outline:2px solid var(--border-focus);outline-offset:3px}
.dep__hthumb{top:50%}
.dep__hexrow{display:flex;align-items:center;gap:8px;margin-top:12px;
  padding:6px 8px;border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-surface)}
.dep__chip2{flex:0 0 auto;width:20px;height:20px;border-radius:6px;
  background:var(--dot,#2563A8);box-shadow:inset 0 0 0 1px rgba(15,23,42,.14)}
.dep__hexin{flex:1 1 auto;min-width:0;border:0;background:none;padding:0;
  text-transform:uppercase}
.dep__hexin:focus{outline:none}
/* Four across would need an alpha channel; there are three, because the column
   this colour is stored in has no alpha to put one in. */
.dep__chans{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-top:10px}
.dep__chan{display:flex;align-items:center;gap:5px;padding:4px 8px;
  border:1px solid var(--border-default);border-radius:var(--r-sm,8px);
  background:var(--bg-surface)}
.dep__chan>span{font-size:10px;font-weight:var(--fw-semibold);
  text-transform:uppercase;color:var(--text-tertiary)}
.dep__chanin{flex:1 1 auto;min-width:0;border:0;background:none;padding:0;
  text-align:right;font-variant-numeric:tabular-nums}
.dep__chanin:focus{outline:none}

/* ---- Places ------------------------------------------------------------- */
.dep__places{display:grid;gap:var(--s-3)}
.dep__place{padding:var(--s-3) var(--s-4);border:1px solid var(--border-default);
  border-radius:var(--r-md);background:var(--bg-surface)}
.dep__placehead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin-bottom:10px}
.dep__placen{font-size:10px;font-weight:var(--fw-semibold);letter-spacing:.04em;
  text-transform:uppercase;color:var(--text-tertiary);
  padding:2px 8px;border-radius:var(--r-pill,999px);background:var(--bg-sunken)}
/* Two buttons, one answer — a segmented control rather than a dropdown,
   because there are exactly two and both should be readable at a glance. */
.dep__kindpick{display:inline-flex;border:1px solid var(--border-default);
  border-radius:var(--r-pill,999px);overflow:hidden}
.dep__kindbtn{display:inline-flex;align-items:center;gap:5px;padding:5px 11px;
  border:0;background:var(--bg-surface);color:var(--text-secondary);cursor:pointer;
  font-size:var(--fs-2xs);font-weight:var(--fw-medium)}
.dep__kindbtn+.dep__kindbtn{border-left:1px solid var(--border-default)}
.dep__kindbtn[aria-checked="true"]{background:var(--accent-quiet);
  color:var(--sev-info-tx);font-weight:var(--fw-semibold)}
.dep__kindbtn:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px}
.dep__placehead .dep__x{margin-left:auto}
/* The organisation's own address, shown where "head office" is chosen so the
   choice is not a blank. */
.dep__orgaddr{margin:0;padding:8px 10px;border-radius:var(--r-sm,8px);
  background:var(--bg-sunken);border:1px solid var(--border-default);
  font-size:var(--fs-2xs);color:var(--text-secondary);white-space:pre-wrap}
.dep__orgset{display:grid;gap:6px;justify-items:start}
.dep__orgset textarea{width:100%}
.dep__addplace{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.dep__addplace .dep__hint{margin:0;flex:1 1 220px}

/* ---- Colour and icon ---------------------------------------------------- */
/* Says where a priority the creator did not pick came from. */
.nt__prionote{margin:-6px 0 10px;font-size:var(--fs-2xs);color:var(--text-secondary)}
.dep__swatches,.dep__icons{display:flex;flex-wrap:wrap;gap:6px}
.dep__swatch{width:28px;height:28px;border-radius:50%;border:2px solid transparent;
  background:var(--sw);cursor:pointer;padding:0;
  box-shadow:inset 0 0 0 1px rgba(15,23,42,.12)}
/* Selected, or open — the same ring either way, because both mean "this is the
   one you are working in". */
.dep__swatch[aria-checked="true"],
.dep__swatch[aria-expanded="true"]{border-color:var(--text-primary)}
/* A hairline, not a gap: the two triggers belong to the row but are not
   themselves colours, and a plain gap reads as a missing swatch. */
.dep__swatchsep{flex:0 0 1px;align-self:stretch;margin:2px 3px;
  background:var(--border-default)}
/* THE WHOLE GAMUT AS ONE ICON — the colour wheel everybody recognises: hue
   around the rim, washing out to white at the centre. The same two gradients a
   full-size wheel is made of, at 28px. It reads as "colour" instantly, which is
   the one job an icon has, and it cannot be mistaken for a swatch you are
   picking because no single colour looks like that.

   THE WHITE STOPS AT 58%, not at the rim. A fade that runs the whole radius is
   right on a 196px wheel and wrong on a 28px one: at this size it washes the
   entire icon pale and the colours stop being legible. Ending it partway leaves
   a solid band of full-strength hue around the edge, so the circle reads as
   complete colour rather than as a white dot with a tint. */
.dep__swatch--picker{background-image:
  radial-gradient(circle closest-side, #fff 0%, rgba(255,255,255,.85) 22%,
                  rgba(255,255,255,0) 58%),
  conic-gradient(from 90deg, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
  /* ORIGIN border-box, REPEAT no-repeat — this is the whole fix for the square
     edge that was showing inside the circle. .dep__swatch carries a 2px
     transparent border, so a gradient defaults to being sized against the
     PADDING box (24px) while painting out to the border box (28px); the 4px
     shortfall was filled by the gradient TILING, and the seam where the tile
     repeated drew a square inside the round button. Sized against the border
     box and told not to repeat, the disc is one circle that fills the button. */
  background-origin:border-box;background-repeat:no-repeat;
  background-size:100% 100%;background-position:center;
  box-shadow:inset 0 0 0 1px rgba(15,23,42,.18)}
.dep__swatch:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}
.dep__custom{margin-top:10px}
.dep__hexnote{margin:5px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary);min-height:1em}
.dep__hexnote[data-tone="bad"]{color:var(--sev-high-tx)}
.dep__hexnote[data-tone="good"]{color:var(--sev-low-tx)}
/* A colour from neither list is still the department's colour — shown, so the
   current choice is never a swatch the operator cannot find. */
.dep__chosen{display:inline-flex;align-items:center;gap:6px;margin-top:8px;
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.dep__iconbtn{display:grid;place-items:center;width:32px;height:32px;
  border-radius:var(--r-md);border:1px solid var(--border-default);
  background:var(--bg-surface);color:var(--text-secondary);cursor:pointer}
.dep__iconbtn:hover{border-color:var(--border-strong);color:var(--text-primary)}
.dep__iconbtn[aria-checked="true"]{border-color:var(--accent);
  background:var(--accent-quiet);color:var(--sev-info-tx)}

/* ---- Secondary heads ---------------------------------------------------- */
.dep__chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.dep__chips:empty{display:none}
.dep__person{display:inline-flex;align-items:center;gap:6px;max-width:100%;
  padding:3px 4px 3px 3px;border-radius:var(--r-pill,999px);
  background:var(--bg-sunken);border:1px solid var(--border-default);
  font-size:var(--fs-2xs);color:var(--text-primary)}
.dep__x{display:grid;place-items:center;width:20px;height:20px;border:0;
  border-radius:50%;background:transparent;color:var(--text-secondary);cursor:pointer;
  font-size:11px}
.dep__x:hover{background:var(--bg-hover);color:var(--sev-critical-tx)}

/* ---- Switches ----------------------------------------------------------- */
.dep__switches{display:flex;flex-direction:column;gap:var(--s-4)}
.dep__switchrow{display:flex;gap:var(--s-3);align-items:flex-start}
.dep__switchrow strong{display:block;font-size:var(--fs-xs);
  font-weight:var(--fw-semibold);color:var(--text-primary)}
.dep__switchrow p{margin:2px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary);
  line-height:1.5}
.dep__toggle{flex:0 0 auto;width:40px;height:22px;border-radius:999px;border:0;
  background:var(--border-strong);cursor:pointer;padding:0;position:relative;
  transition:background .14s}
.dep__toggle[aria-checked="true"]{background:var(--accent)}
.dep__knob{position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;
  background:#fff;box-shadow:var(--shadow-sm);transition:transform .14s}
.dep__toggle[aria-checked="true"] .dep__knob{transform:translateX(18px)}
.dep__toggle:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}

/* ---- Right rail --------------------------------------------------------- */
.dep__rail{display:flex;flex-direction:column;gap:var(--s-4);min-width:0}
@media (min-width:1181px){ .dep__rail{position:sticky;top:var(--s-4)} }
/* The preview is the Catalogue card, drawn from the same tokens, so what you
   see here is what the grid will draw rather than an impression of it. */
.dep__preview{display:flex;gap:var(--s-3);padding:var(--s-4);border-radius:var(--r-md);
  background:var(--bg-sunken);border:1px solid var(--border-default);min-width:0}
.dep__mark{flex:0 0 auto;display:grid;place-items:center;width:52px;height:52px;
  border-radius:14px;color:var(--mark);
  background:color-mix(in srgb,var(--mark) 14%,transparent)}
.dep__previewtext{min-width:0;flex:1 1 auto}
.dep__previewname{display:block;font-size:var(--fs-md);font-weight:var(--fw-semibold);
  color:var(--text-primary);overflow-wrap:anywhere;text-wrap:balance}
/* NO LINE CLAMP. The preview is the one surface whose whole job is to show you
   what you wrote; clipping it at two lines made the description the only field
   on the page you could not read back. The Catalogue CARD clamps — that is a
   grid cell with neighbours to line up with. This is not. */
.dep__previewsub{margin:3px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary);
  line-height:var(--lh-relaxed);white-space:pre-wrap;overflow-wrap:anywhere}
.dep__previewpath{margin:7px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  display:flex;align-items:center;gap:4px;overflow-wrap:anywhere}
.dep__previewstats{display:flex;flex-wrap:wrap;gap:var(--s-3);margin-top:8px}
.dep__previewstats span{display:inline-flex;align-items:center;gap:4px;
  font-size:var(--fs-2xs);color:var(--text-secondary);font-variant-numeric:tabular-nums}
.dep__previewstats svg{color:var(--text-tertiary)}
.dep__previewchips{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}
.dep__chip{padding:2px 8px;border-radius:var(--r-pill,999px);font-size:11px;
  font-weight:var(--fw-semibold);background:var(--accent-quiet);color:var(--sev-info-tx)}

/* ---- The rail: where it will sit, and what it is going into -------------
   ONE VERTICAL SPINE, not a tree drawn sideways. The rail is ~300px wide, so
   indenting five levels the way the form's tree does would leave the deepest
   name four characters wide. The depth is carried by a left rule plus a small
   step instead, which stays readable at every level. */
/* ═══ Placement, the reference layout ═════════════════════════════════════
   Two numbered panels — choose the department, then walk down inside it. They
   sit INSIDE the form's section body, so they carry a quieter surface than a
   top-level card would; nesting a card in a card and giving both the same
   elevation is what makes an enterprise form read as a pile of boxes. */
.dep__pick{display:flex;flex-direction:column;gap:var(--s-4)}
.dep__pcard{border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface);padding:var(--s-4);
  container-type:inline-size;container-name:pcard}
.dep__phead{display:flex;align-items:flex-start;gap:10px;margin-bottom:var(--s-3)}
.dep__pnum{flex:0 0 auto;display:grid;place-items:center;width:24px;height:24px;
  border-radius:50%;background:var(--accent);color:var(--text-inverse);
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);font-variant-numeric:tabular-nums}
.dep__phead h3{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.dep__phead p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}

.dep__prow{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;
  margin-bottom:var(--s-3)}
.dep__psearchwrap{position:relative;flex:1 1 240px;min-width:0;display:block}
.dep__psearchicon{position:absolute;left:10px;top:50%;transform:translateY(-50%);
  display:grid;place-items:center;color:var(--text-tertiary);pointer-events:none}
.dep__psearch{width:100%;padding-left:30px}
.dep__pviews{display:flex;align-items:center;gap:6px;flex:0 0 auto}
.dep__pviewlabel{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.dep__pvbtn{display:inline-flex;align-items:center;gap:5px;padding:5px 10px;
  border:1px solid var(--border-default);border-radius:var(--r-sm,6px);
  background:var(--bg-surface);color:var(--text-secondary);
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);cursor:pointer}
.dep__pvbtn[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);
  color:var(--text-inverse)}

/* AUTO-FIT, NOT A FIXED FOUR. The rail takes a third of the width on a laptop
   and none on a tablet, so a hard column count would strand a card alone on a
   row at exactly the widths people actually use. */
.dep__pgrid{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(190px,1fr))}
.dep__plist{display:flex;flex-direction:column;gap:6px}
.dep__pcardbtn{display:flex;align-items:flex-start;gap:10px;padding:11px 12px;width:100%;
  text-align:left;border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-surface);cursor:pointer;min-width:0}
.dep__pcardbtn:hover{border-color:var(--border-strong);background:var(--bg-hover)}
.dep__pcardbtn[data-on]{border-color:var(--accent);background:var(--accent-quiet);
  box-shadow:inset 0 0 0 1px var(--accent)}
.dep__pmark{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;
  border-radius:9px;color:var(--mark);
  background:color-mix(in srgb,var(--mark) 14%,transparent)}
.dep__pbody{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:2px}
.dep__pname{font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--text-primary);
  overflow-wrap:anywhere}
/* Two lines, then stop: this is a grid cell with neighbours to line up with.
   The preview panel shows the whole description. */
.dep__pdesc{font-size:var(--fs-2xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
.dep__pmeta{display:flex;gap:5px;flex-wrap:wrap;font-size:var(--fs-2xs);
  color:var(--text-tertiary);font-variant-numeric:tabular-nums}
.dep__pstate{flex:0 0 auto;display:grid;place-items:center;color:var(--accent)}
.dep__pdot{width:7px;height:7px;border-radius:50%;background:var(--border-strong)}
.dep__pdot[data-live]{background:var(--sev-ok-tx)}

.dep__crumbs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  padding-bottom:var(--s-3);margin-bottom:var(--s-3);
  border-bottom:1px solid var(--border-default)}
.dep__crumb{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.dep__crumb--root,.dep__crumb[data-picked]{color:var(--accent);font-weight:var(--fw-semibold)}
.dep__crumbsep{display:grid;place-items:center;color:var(--text-tertiary)}
.dep__crumbopt{color:var(--text-tertiary);font-weight:var(--fw-regular,400)}

/* THE COLUMNS ARE THE INPUT. Equal tracks with a rule between them, the way the
   reference draws them; they stack on a narrow screen because three 200px
   columns in a 360px viewport is not a cascade, it is a scrollbar. */
.dep__cols{display:grid;gap:0;grid-template-columns:repeat(3,minmax(0,1fr))}
.dep__col{min-width:0;padding:0 var(--s-3);border-left:1px solid var(--border-default)}
.dep__col:first-child{border-left:0;padding-left:0}
.dep__col:last-child{padding-right:0}
.dep__colhead h4{margin:0;font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.dep__colhead h4 span{font-weight:var(--fw-regular,400);color:var(--text-tertiary)}
.dep__colhead p{margin:1px 0 8px;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.dep__colrows{display:flex;flex-direction:column;gap:5px}
.dep__colrow{display:flex;align-items:center;gap:8px;padding:8px 9px;width:100%;
  text-align:left;border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-surface);cursor:pointer;min-width:0}
.dep__colrow:hover{border-color:var(--border-strong);background:var(--bg-hover)}
.dep__colrow[data-on]{border-color:var(--accent);background:var(--accent-quiet);
  box-shadow:inset 0 0 0 1px var(--accent)}
.dep__colmark{flex:0 0 auto;display:grid;place-items:center;width:28px;height:28px;
  border-radius:8px;color:var(--mark);
  background:color-mix(in srgb,var(--mark) 14%,transparent)}
.dep__colbody{min-width:0;flex:1 1 auto;display:flex;flex-direction:column}
.dep__colbody strong{font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.dep__colbody span{font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.dep__colchev{flex:0 0 auto;display:grid;place-items:center;color:var(--text-tertiary)}
.dep__colrow[data-on] .dep__colchev{color:var(--accent)}
/* Below three comfortable tracks the rules stop helping: a wrapped row would
   carry a leading border into the middle of the panel. So the separator turns
   horizontal at the same moment the columns stack. */
@container pcard (max-width:640px){
  .dep__cols{grid-template-columns:repeat(2,minmax(0,1fr));gap:0 var(--s-3)}
  .dep__col{border-left:0;padding:0}
  .dep__col:nth-child(n+3){border-top:1px solid var(--border-default);
    padding-top:var(--s-3);margin-top:var(--s-3)}
}
@container pcard (max-width:380px){
  .dep__cols{grid-template-columns:1fr}
  .dep__col:nth-child(n+2){border-top:1px solid var(--border-default);
    padding-top:var(--s-3);margin-top:var(--s-3)}
}

/* The level is derived from the walk now, so it is stated rather than typed. */
.dep__derived{margin:var(--s-3) 0 0;padding:9px 12px;border-radius:var(--r-md);
  background:var(--accent-quiet);font-size:var(--fs-2xs);color:var(--text-secondary)}
.dep__derived strong{color:var(--text-primary);font-weight:var(--fw-semibold)}
.dep__derivedlv,.dep__derivedin{color:var(--text-tertiary);font-variant-numeric:tabular-nums}

/* ---- The rail: leadership, path and who runs each rung above ---------- */
















/* ---- Department leadership, in the rail ---------------------------------
   THE REFERENCE'S ROW: rung on the left, person in the middle, one action on
   the right, separated by hairlines rather than boxed as cards — a list of
   four people does not need four borders to read as four people. */
.dep__lead{display:flex;flex-direction:column;margin-top:var(--s-3)}
.dep__lrow{border-top:1px solid var(--border-default);padding:10px 0}
.dep__lrow:first-child{border-top:0;padding-top:0}
.dep__lmain{display:flex;align-items:center;gap:var(--s-3);min-width:0}
/* Person, then role, then the actions. The role picker takes a fixed share so
   every row's controls line up down the list instead of shifting with the
   length of each name. */
.dep__lrole{flex:0 0 190px;max-width:44%;min-width:0}
@media (max-width:760px){
  .dep__lmain{flex-wrap:wrap}
  .dep__lrole{flex:1 1 100%;max-width:none;order:3}
  .dep__lacts{order:2;margin-left:auto}
}
/* ONE QUESTION PER LINE, AT EVERY WIDTH. These used to be a flex row that
   wrapped differently as the form resized, so the two pickers and the button
   landed somewhere new each time. Stacked, they read in the order they are
   answered — person, then role, then add — and never move. Deliberately NOT
   side-by-side on a wide screen: a layout that rearranges itself at a
   breakpoint is the thing that made this feel unplaced in the first place. */
.dep__leadadd{display:flex;flex-direction:column;gap:var(--s-3);
  padding:var(--s-3);border:1px solid var(--border-default);
  border-radius:var(--r-md);background:var(--bg-sunken)}
.dep__leadfld{display:flex;flex-direction:column;gap:4px;min-width:0}
.dep__leadgo{align-items:flex-start}
.dep__leadgo .btn{align-self:flex-start;min-width:110px;justify-content:center}
/* The rung is named once per group, so a second head sits under the first
   without the label being repeated as though it were a different rung. It keeps
   its width when empty so every person still lines up in one column. */

.dep__lwho{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 200px}
.dep__ltext{min-width:0;display:flex;flex-direction:column}
.dep__ltext strong{display:flex;align-items:center;gap:5px;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);color:var(--text-primary);
  overflow-wrap:anywhere}
.dep__ltext span{font-size:10px;color:var(--text-tertiary);
  overflow-wrap:anywhere}
/* An empty seat is a decision nobody has made, not a row that failed to load. */

.dep__lacts{flex:0 0 auto;display:flex;align-items:center;gap:2px}
/* Promote and remove stay quiet until the row is under the pointer: the action
   the reference shows is the link, and two glyphs competing with it on every
   row would make the panel read as a toolbar. */
.dep__lacts .dep__x{opacity:0;transition:opacity .12s ease}
.dep__lrow:hover .dep__lacts .dep__x,
.dep__lacts .dep__x:focus-visible{opacity:1}
.dep__llink{padding:2px 4px;border:0;background:none;cursor:pointer;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--accent);
  border-radius:var(--r-sm,4px);white-space:nowrap}
.dep__llink:hover{text-decoration:underline}



/* What the catalogue already holds about somebody — no second request, and
   nothing the screen was not already trusted with. */
.dep__lprof{display:grid;grid-template-columns:auto 1fr;gap:3px var(--s-3);
  margin:8px 0 0;padding:9px 11px;border-radius:var(--r-md);
  background:var(--bg-sunken);border:1px solid var(--border-default)}
.dep__lprof > div{display:contents}
.dep__lprof dt{font-size:10px;color:var(--text-tertiary)}
.dep__lprof dd{margin:0;font-size:10px;color:var(--text-primary);
  overflow-wrap:anywhere}

/* ---- The kinds of department ------------------------------------------
   A SEARCHABLE DROPDOWN, with the chosen kind spelled out under it. The list of
   rows this replaced read well at four kinds and would not at forty — and the
   catalogue is meant to grow, because "New type" writes to it. */
.dep__typesbox{display:flex;flex-direction:column;gap:8px;margin-top:6px}
.dep__typechosen{display:flex;align-items:center;gap:var(--s-3);min-width:0;
  padding:10px 12px;border-radius:var(--r-md);
  border:1px solid var(--accent);background:var(--accent-quiet)}
.dep__typetick{flex:0 0 auto;display:grid;place-items:center;color:var(--accent)}
.dep__typebody{min-width:0;flex:1 1 auto}
.dep__typetop{display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}
.dep__typetop strong{font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary);overflow-wrap:anywhere}
.dep__typechosen p{margin:2px 0 0;font-size:var(--fs-2xs);
  color:var(--text-secondary);line-height:var(--lh-relaxed)}
.dep__typecount{display:block;margin-top:4px;font-size:10px;
  color:var(--text-tertiary);font-variant-numeric:tabular-nums}
/* "None of these" is not an option in the dropdown, so it is a control here. */
.dep__typeclear{flex:0 0 auto;padding:4px 10px;cursor:pointer;
  border:1px solid var(--border-strong);border-radius:var(--r-sm,6px);
  background:var(--bg-surface);color:var(--text-primary);
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);white-space:nowrap}
.dep__typeclear:hover{border-color:var(--accent);color:var(--accent)}
.dep__typeslead{margin:0 0 var(--s-3)}

/* ---- The address book --------------------------------------------------
   A searchable dropdown of the addresses the organisation already holds, read
   back from the same payload that drew the form. Picking one COPIES it into a
   new entry — places belong to the department that holds them, so a shared row
   would mean editing one address silently rewrote another department's. */
.dep__bookpick{display:flex;align-items:flex-start;gap:var(--s-2);margin-top:var(--s-3)}
.dep__bookwrap{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:4px}
.dep__bookwrap > .cb{min-width:0}
/* Empty, it is still there and still says what it is — just not pressable. */
.dep__bookwrap .cb__btn:disabled{opacity:.6;cursor:not-allowed}
.dep__bookicon{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:8px;color:var(--text-tertiary);background:var(--bg-sunken)}

/* ---- The preview, past the card ---------------------------------------
   The card above answers "what will this look like in the Catalogue". These
   blocks answer "what am I about to save" — every section of the form that the
   card does not show. Quieter than the card on purpose: it is the card that is
   being previewed, and this is the receipt under it. */
.dep__pvmore{display:flex;flex-direction:column;gap:var(--s-4);margin-top:var(--s-3)}
.dep__pvblock{display:flex;flex-direction:column;gap:5px;min-width:0}
.dep__pvblock h3{margin:0 0 2px;font-size:10px;font-weight:var(--fw-semibold);
  letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}
.dep__pvrow{display:flex;align-items:baseline;gap:7px;min-width:0;
  font-size:var(--fs-2xs)}
.dep__pvicon{flex:0 0 auto;align-self:center;display:grid;place-items:center;
  color:var(--text-tertiary)}
/* The label column holds its width so the values line up down the block; the
   value takes what is left and wraps rather than pushing the row sideways. */
.dep__pvlabel{flex:0 0 40%;max-width:40%;color:var(--text-tertiary);
  overflow-wrap:anywhere}
.dep__pvvalue{flex:1 1 auto;min-width:0;color:var(--text-primary);
  font-weight:var(--fw-semibold);overflow-wrap:anywhere}
.dep__pvplace{display:flex;flex-direction:column;gap:2px;
  padding-bottom:6px;border-bottom:1px solid var(--border-default)}
.dep__pvplace:last-child{padding-bottom:0;border-bottom:0}
/* Postal addresses keep their own line breaks: reflowed into a paragraph they
   stop being checkable against an envelope. */
.dep__pvaddr{margin:2px 0 0 19px;font-size:var(--fs-2xs);color:var(--text-secondary);
  white-space:pre-wrap;overflow-wrap:anywhere;line-height:var(--lh-relaxed)}
.dep__pvcontact{margin:1px 0 0 19px;font-size:10px;color:var(--text-tertiary);
  overflow-wrap:anywhere}
.dep__pvnotes{margin:0;font-size:var(--fs-2xs);color:var(--text-secondary);
  white-space:pre-wrap;overflow-wrap:anywhere;line-height:var(--lh-relaxed)}

/* ---- What the quota is measured against --------------------------------
   Three facts under the box: this department's usage against its ceiling, a bar
   for the proportion, and the newsroom total the ceiling is carving up. */
/* ---- Draft with AI -----------------------------------------------------
   Sits above the step rail because it is the thing you do BEFORE filling the
   form, not a section of it. Deliberately one row: a panel with options would
   suggest the draft is a configuration rather than a starting point. */
.dep__ai{margin:var(--s-4) 0 0;padding:var(--s-4);
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-sunken)}
.dep__aihead{display:flex;align-items:flex-start;gap:10px;margin-bottom:var(--s-3)}
.dep__aiicon{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:9px;color:var(--accent);background:var(--accent-quiet)}
.dep__aihead h2{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.dep__aihead p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}
.dep__airow{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap}
.dep__airow .input{flex:1 1 320px;min-width:0}
.dep__ainote{margin:8px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.dep__ainote[data-tone="ok"]{color:var(--sev-ok-tx)}
.dep__ainote[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

/* ---- External collaborators -------------------------------------------
   Sits under the switch it belongs to, indented and on a sunken ground so it
   reads as a consequence of that setting rather than a section of its own. */
/* Full width under the grid now, so three text fields per person have room. */
.dep__externals{margin:var(--s-4) 0 0;padding:var(--s-3);
  border:1px solid var(--border-default);border-radius:var(--r-md);
  background:var(--bg-sunken)}
/* Spelled out rather than left to the UA [hidden] rule: the moment anything
   above gives .dep__externals a display, the panel comes back while the switch
   says it is off. The same trap the auth captcha fell into. */
.dep__externals[hidden]{display:none}
.dep__exthead{display:flex;align-items:flex-start;gap:8px;margin-bottom:var(--s-3)}
.dep__exticon{flex:0 0 auto;display:grid;place-items:center;width:26px;height:26px;
  border-radius:8px;color:var(--text-tertiary);background:var(--bg-surface);
  border:1px solid var(--border-default)}
.dep__exthead strong{display:block;font-size:var(--fs-xs);
  font-weight:var(--fw-semibold);color:var(--text-primary)}
.dep__exthead p{margin:1px 0 0;font-size:10px;color:var(--text-tertiary);
  line-height:var(--lh-relaxed)}
/* ---- Adding somebody, and the vocabulary for what they do (0140) --------
   The add row sits between the panel head and the list because that is the
   order the work happens in: bring somebody in, then say who they are. It is
   separated by a rule rather than a card so it does not read as a second
   panel inside this one. */
.dep__extadd{display:flex;flex-direction:column;gap:5px;
  padding:0 0 var(--s-3);border-bottom:1px solid var(--border-default)}
.dep__extaddrow{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.dep__extaddrow > .cb{flex:1 1 220px;min-width:0}
.dep__extaddrow > .btn{flex:0 0 auto}
.dep__exthint{margin:0;font-size:10px;color:var(--text-tertiary);
  line-height:var(--lh-relaxed);overflow-wrap:anywhere}
/* The role catalogue is a deeper permission than the rest of the panel, so it
   sits at the BOTTOM, behind a disclosure — present for whoever may use it,
   out of the way of the job most people came here to do. */
.dep__extroles{display:flex;flex-direction:column;align-items:flex-start;gap:6px;
  margin-top:var(--s-3);padding-top:var(--s-3);
  border-top:1px solid var(--border-default)}
.dep__extnew{width:100%;display:flex;flex-direction:column;gap:var(--s-2);
  margin-top:2px}
.dep__extnew[hidden]{display:none}
.dep__extrow{display:flex;flex-direction:column;gap:var(--s-2);
  padding:var(--s-3) 0;border-top:1px solid var(--border-default)}
/* Only when nothing sits above it — with the add row present, the rule
   between them is the add row's own bottom border. */
.dep__extadd + .dep__extrow{border-top:0}
.dep__extrow:first-of-type{border-top:0;padding-top:0}
.dep__extwho{display:flex;align-items:center;gap:8px;min-width:0}
.dep__exttext{min-width:0;display:flex;flex-direction:column}
.dep__exttext strong{font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary);overflow-wrap:anywhere}
.dep__exttext span{font-size:10px;color:var(--text-tertiary);
  line-height:var(--lh-relaxed);overflow-wrap:anywhere}
.dep__extfields{display:grid;gap:var(--s-2);grid-template-columns:1fr}
@media (min-width:620px){ .dep__extfields{grid-template-columns:1fr 1fr} }
.dep__extfld{display:flex;flex-direction:column;gap:3px;min-width:0}
.dep__extfld > span{font-size:9px;font-weight:var(--fw-semibold);letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary)}
/* THE ARRANGEMENT KEEPS THE WHOLE ROW, at every width. It used to drop into a
   third column on wide screens, ~250px, where its own placeholder wrapped to
   three lines inside a two-row box and the third line was cut off. A sentence
   field sits under the two short ones instead of beside them. */
.dep__extfld--wide{grid-column:1 / -1}
/* min-height comes from textarea.input (72px) — two full lines plus padding —
   and is not repeated here, where the weaker selector would never win. */
.dep__extnote{width:100%}
/* The summary above the fields carries the SAVED values in full, so a value too
   long for its input is still readable without clicking into it — see the
   .dep__exttext rules above. */
.dep__extgo{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
/* Pushes Remove to the far edge, away from Save. Two buttons that do opposite
   things should not sit a thumb's width apart. */
.dep__extspacer{flex:1 1 auto;min-width:var(--s-3)}
/* The confirmation takes the place of the button rather than appearing beside
   it, so there is never a moment where both "Remove" and "Remove" are on
   screen meaning different things. */
.dep__extask{display:flex;flex-direction:column;gap:6px;margin-top:6px;
  padding:var(--s-2) var(--s-3);border-radius:var(--r-md);
  background:var(--sev-critical-bg);border:1px solid var(--sev-critical-bd)}
.dep__extask[hidden]{display:none}
.dep__extask p{margin:0;font-size:10px;color:var(--sev-critical-tx);
  line-height:var(--lh-relaxed);overflow-wrap:anywhere}
.dep__extaskgo{display:flex;gap:var(--s-2);flex-wrap:wrap}
/* The drawer's form is the page's field stack at drawer width. */
.dep__drawerform{display:flex;flex-direction:column;gap:var(--s-3)}

/* ---- An address, in its own boxes (0141) --------------------------------
   Two columns, with the two street lines spanning both, in the order an
   envelope is written: building and street, area, city, district, state, PIN,
   country. One column at phone width, where two would give each box about
   eight characters. */
.dep__addr{display:grid;gap:var(--s-2);grid-template-columns:1fr}
@media (min-width:560px){ .dep__addr{grid-template-columns:1fr 1fr} }
.dep__addrfld{display:flex;flex-direction:column;gap:3px;min-width:0}
.dep__addrfld--wide{grid-column:1 / -1}
.dep__addrfld > span{font-size:9px;font-weight:var(--fw-semibold);letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary)}
/* The PIN cue sits under its own box rather than under the block, so it is
   next to the thing it is talking about. */
.dep__addrfld > .dep__cue{margin:0}

/* ---- Every way to reach a place (0142) ----------------------------------
   One row per contact: what kind it is, its label, the value, and a way to
   drop it. The label picker is fixed-width so a column of them lines up and
   the values start at the same place down the list. */
.dep__contacts{display:flex;flex-direction:column;gap:6px}
.dep__ctrow{display:grid;gap:6px;align-items:center;
  grid-template-columns:auto 130px minmax(0,1fr) auto auto}
.dep__cticon{display:grid;place-items:center;width:24px;height:24px;
  border-radius:7px;color:var(--text-tertiary);background:var(--bg-sunken);
  border:1px solid var(--border-default)}
/* SIZED TO ITS ROW. .cb__btn carries the `input` class, which is 40px — a head
   taller than the 26px boxes it sits beside, so the row read as two different
   controls stacked badly rather than one line. */
.dep__ctlabel{min-width:0}
.dep__ctlabel .cb__btn{height:26px;padding-inline:var(--s-2)}
/* SIZED TO THIS ROW, not to the full-width combos elsewhere. The shared popup
   is built for a 40px control: a 40px search box, 33px rows at 14px. Hung under
   a 26px picker it came out 225px tall for five one-word labels — taller than
   the block it belongs to. Everything here is the compact equivalent, which
   brings it to about 150px.

   Wider than the control, but only just: the control is narrow to keep the row
   on one line, and "డ్యూటీ డెస్క్" needs a little more than that. */
.dep__ctlabel .cb__pop{right:auto;min-width:150px;padding:4px}
.dep__ctlabel .cb__search{height:26px;font-size:var(--fs-xs)}
.dep__ctlabel .cb__opt{padding:4px var(--s-2);font-size:var(--fs-xs)}
.dep__ctlabel .cb__optlabel{font-size:var(--fs-xs)}
.dep__ctlabel .cb__none{padding:4px var(--s-2);font-size:var(--fs-xs)}
.dep__ctrow > .dep__cue{grid-column:3 / -1;margin:0}
/* WHAT THE CONTACT DOES, beside the contact. A link rather than a button: it
   navigates, so middle-click, copy-link and the keyboard all work the way
   they do everywhere else. */
.dep__ctact{display:flex;align-items:center;min-width:0}
.dep__ctgo-link{display:inline-flex;align-items:center;gap:4px;
  height:26px;padding:0 var(--s-2);border-radius:var(--r-sm);
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  color:var(--accent);text-decoration:none;white-space:nowrap;
  border:1px solid transparent}
.dep__ctgo-link:hover{background:var(--bg-hover);border-color:var(--border-default)}
.dep__ctgo-link:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* Not yet saved, so not yet reachable — said quietly, not offered as a link.
   A CHIP, not a sentence: this column also holds a button, and the sentence
   ran straight across it into the remove control. The explanation is the
   tooltip, where a sentence can be as long as it needs to be. */
.dep__ctwait{display:inline-flex;align-items:center;height:26px;
  padding:0 var(--s-2);border-radius:var(--r-sm);white-space:nowrap;
  font-size:9px;font-weight:var(--fw-semibold);letter-spacing:.04em;
  text-transform:uppercase;color:var(--text-tertiary);
  background:var(--bg-sunken);border:1px solid var(--border-default)}
/* Says which of several numbers is the one a card shows — the same rule
   app.contact_primary applies, spelled out where it can be seen. */
.dep__ctprimary[hidden]{display:none}
.dep__ctprimary{grid-column:3 / -1;justify-self:start;font-size:9px;
  font-weight:var(--fw-semibold);letter-spacing:.05em;text-transform:uppercase;
  color:var(--accent)}
.dep__ctgo{display:flex;gap:var(--s-2);flex-wrap:wrap;margin-top:2px}
/* In the preview the links sit inline in a sentence of contacts. */
.dep__pvcontact{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.dep__pvlink{height:auto;padding:0;font-size:inherit;font-weight:var(--fw-semibold)}
.dep__pvlink:hover{background:none;border-color:transparent;text-decoration:underline}
.dep__pvsep{color:var(--text-tertiary)}

/* ---- Finishing an email address -----------------------------------------
   The list hangs UNDER the box, the width of the box, and above everything
   below it. Nothing but the address on each row: a suggestion that needs a
   caption is not one. */
.dep__mailwrap{position:relative;min-width:0}
.dep__mailsug{position:absolute;top:calc(100% + 3px);left:0;right:0;z-index:40;
  margin:0;padding:var(--s-1);list-style:none;
  max-height:220px;overflow-y:auto;
  background:var(--bg-overlay);border:1px solid var(--border-default);
  border-radius:var(--r-md);box-shadow:var(--shadow-lg)}
.dep__mailsug[hidden]{display:none}
.dep__mailopt{padding:5px var(--s-2);border-radius:var(--r-sm);cursor:pointer;
  font-size:var(--fs-xs);color:var(--text-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dep__mailopt:hover,.dep__mailopt[data-active]{background:var(--bg-hover)}
/* .iconbtn exists; the small variant did not. Sized to sit on the same
   line as a 26px input rather than towering over it. */
.iconbtn--sm{width:26px;height:26px;padding:0}
/* A value that is absent rather than unknown — said quietly. */
.dep__quiet{color:var(--text-tertiary);font-style:italic}

/* ---- The AI bar's own controls -----------------------------------------
   The microphone reads as ON while it is listening — a recording control that
   looks the same either way is the one nobody notices is still running. */
.dep__aimic{flex:0 0 auto}
.dep__aimic.is-live{background:var(--sev-critical-bg);border-color:var(--sev-critical-bd);
  color:var(--sev-critical-tx)}
.dep__aimic.is-live svg{animation:v9-mic-pulse 1.1s ease-in-out infinite}
@keyframes v9-mic-pulse{0%,100%{opacity:1}50%{opacity:.45}}
@media (prefers-reduced-motion:reduce){ .dep__aimic.is-live svg{animation:none} }
.dep__aimodel{flex:0 1 220px;min-width:0}
/* THE ROWS ARE THE ASSISTANT'S — .aic__mrow-top, .aic__tier and .aic__mrow-meta
   are global selectors in this sheet, so this reuses that look rather than
   restating it. All the option itself has to do is stop being a one-line flex
   row and give them the width they were designed for. */
.dep__aimodel .cb__pop{right:auto;min-width:330px}
.dep__aimodel .cb__opt{flex-direction:column;align-items:stretch;gap:3px;
  padding:7px 9px}
/* The closed control: the vendor's mark and the model name, nothing else. */
.dep__aimodel .cb__val{display:flex;align-items:center;gap:6px}
.dep__aimname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Where the numbers come from, said under the bar rather than in every row. */
.dep__aimlink{display:block;margin-top:6px;font-size:var(--fs-2xs)}
.dep__aimlink a{color:var(--text-tertiary);text-decoration:none}
.dep__aimlink a:hover{color:var(--accent);text-decoration:underline}
.dep__aimlink a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;
  border-radius:var(--r-sm)}
.dep__aifresh{color:var(--text-tertiary)}
/* A refresh that has stopped is a problem to see, not a detail to skim. */
.dep__aistale{color:var(--sev-medium-tx,var(--sev-medium));font-weight:var(--fw-semibold)}
@media (max-width:560px){
  /* Below this the four columns give the value about ten characters. */
  .dep__ctrow{grid-template-columns:auto minmax(0,1fr) auto}
  .dep__ctlabel{grid-column:2 / -1}
  .dep__ctrow > .dep__cue,.dep__ctprimary{grid-column:1 / -1}
}
.dep__extsaid{font-size:10px;color:var(--text-tertiary)}
.dep__extsaid[data-tone="ok"]{color:var(--sev-ok-tx);font-weight:var(--fw-semibold)}
.dep__extsaid[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

.dep__storage{display:flex;flex-direction:column;gap:5px;margin-top:6px}
.dep__storerow{display:flex;align-items:baseline;gap:7px;flex-wrap:wrap;
  font-size:var(--fs-2xs);color:var(--text-primary)}
.dep__storerow--quiet{color:var(--text-tertiary)}
.dep__storelabel{color:var(--text-tertiary)}
.dep__storerow strong{font-weight:var(--fw-semibold);font-variant-numeric:tabular-nums}
/* The caveat beside the database figure — quieter than the number it qualifies. */
.dep__storenote{color:var(--text-tertiary);font-size:10px}
.dep__storebar{height:5px;border-radius:999px;background:var(--bg-sunken);
  border:1px solid var(--border-default);overflow:hidden}
.dep__storebar > span{display:block;height:100%;background:var(--accent);
  border-radius:999px;transition:width .15s ease}
/* Full and red once the ceiling is passed — the number beside it still says by
   how much, because a bar that stops at 100% cannot. */
.dep__storebar > span[data-over]{background:var(--sev-critical)}

.dep__path{display:flex;flex-direction:column;gap:8px}
.dep__pathlead{margin:0;display:flex;align-items:center;gap:5px;
  font-size:var(--fs-2xs);color:var(--text-tertiary)}
.dep__pathsteps{display:flex;flex-direction:column;gap:6px}
.dep__pathstep{display:flex;align-items:center;gap:8px;min-width:0}
.dep__pathnum{flex:0 0 auto;display:grid;place-items:center;width:20px;height:20px;
  border-radius:50%;font-size:10px;font-weight:var(--fw-semibold);
  font-variant-numeric:tabular-nums;
  background:var(--accent-quiet);color:var(--sev-info-tx)}
.dep__pathtext{min-width:0;font-size:var(--fs-2xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
.dep__pathlvl{color:var(--text-tertiary)}
.dep__pathtext strong{color:var(--text-primary);font-weight:var(--fw-semibold)}

/* ---- Quick Info --------------------------------------------------------
   WHO RUNS EACH RUNG ABOVE THIS ONE. Rebuilt as a stack of cards rather than a
   label/value table: the old version put the rung on the left and the person on
   the right, which reads as a form field and made the NAME — the thing you are
   actually here to see — the least prominent item in its own row.

   Each card leads with the rung as a small uppercase eyebrow, then the face and
   the name at the size of a heading. A vacancy is drawn as an outlined seat
   rather than a greyed name, because an empty rung is a decision nobody has
   taken, not a value that failed to load. */
.dep__quick{display:flex;flex-direction:column;gap:6px}
/* One block, not two. The rail needs position/overflow on the same element, and
   splitting it across a second .dep__qrow rule was how the last duplicate in
   this file started. */
.dep__qrow{position:relative;overflow:hidden;
  display:flex;align-items:center;gap:10px;min-width:0;
  padding:9px 11px;border-radius:var(--r-md);
  border:1px solid var(--border-default);background:var(--bg-surface)}
.dep__qrow::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--accent);opacity:.55}
.dep__qrow[data-vacant]::before{background:var(--border-strong);opacity:1}
.dep__qwho{display:flex;align-items:center;gap:9px;min-width:0;flex:1 1 auto}
.dep__qtext{min-width:0;display:flex;flex-direction:column;gap:1px}
.dep__qlabel{font-size:9px;font-weight:var(--fw-semibold);letter-spacing:.07em;
  text-transform:uppercase;color:var(--text-tertiary)}
.dep__qtext strong{font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary);overflow-wrap:anywhere}
.dep__qtext span{font-size:10px;color:var(--text-tertiary);
  overflow-wrap:anywhere}
/* The empty seat: an outline where a face would be, at the same size, so the
   row keeps its rhythm instead of collapsing. */
.dep__qseat{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:50%;color:var(--text-tertiary);
  border:1px dashed var(--border-strong);background:var(--bg-sunken)}
.dep__qrow[data-vacant]{background:var(--bg-sunken);border-style:dashed}
.dep__qrow[data-vacant] .dep__qtext strong{color:var(--text-tertiary);
  font-weight:var(--fw-regular,400)}









/* Not placed yet: the shape of the answer without claiming an answer. */






/* The duplicate warning, now under the placement columns rather than in the
   rail — same rule, different home. */
.dep__hsibs{margin:7px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  line-height:var(--lh-relaxed);overflow-wrap:anywhere}
/* A sibling whose name is what is being typed. This is the duplicate warning:
   it fires while the operator is still looking at the box, not on save. */
.dep__hsib--same{color:var(--sev-high-tx);font-weight:var(--fw-semibold)}

.dep__ctx{display:flex;flex-direction:column;gap:var(--s-3)}
.dep__ctxhead{display:flex;align-items:center;gap:9px;min-width:0}
.dep__ctxtext{min-width:0;flex:1 1 auto}
.dep__ctxtext strong{display:block;font-size:var(--fs-sm);color:var(--text-primary);
  overflow-wrap:anywhere}
.dep__ctxtext p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.dep__ctxstats{display:flex;flex-wrap:wrap;gap:6px}
.dep__ctxstats span{display:inline-flex;align-items:center;gap:4px;
  padding:3px 9px;border-radius:var(--r-pill,999px);background:var(--bg-sunken);
  border:1px solid var(--border-default);
  font-size:var(--fs-2xs);color:var(--text-secondary);font-variant-numeric:tabular-nums}
.dep__ctxstats strong{color:var(--text-primary);font-weight:var(--fw-semibold)}
.dep__ctxstats--quiet span{background:transparent;border-color:transparent;padding:0 2px}
.dep__ctxstats--quiet svg{color:var(--text-tertiary)}

.dep__steprow{display:flex;gap:var(--s-2);align-items:center;margin-bottom:var(--s-3)}
.dep__steprow .input{flex:1 1 auto;min-width:0}
.dep__focus{display:flex;flex-direction:column;gap:1px;max-height:320px;overflow:auto}
.dep__focusrow{display:flex;align-items:center;gap:var(--s-2);padding:6px;
  border-radius:var(--r-sm,4px);font-size:var(--fs-xs);color:var(--text-primary);
  cursor:pointer}
.dep__focusrow:hover{background:var(--bg-hover)}
.dep__focusrow input{flex:0 0 auto;width:15px;height:15px;accent-color:var(--accent);
  cursor:pointer}

/* ---- The actions again, at the end -------------------------------------- */
.dep__foot{display:flex;justify-content:flex-end;gap:var(--s-2);
  margin-top:var(--s-4);padding-top:var(--s-4);border-top:1px solid var(--border-default)}
@media (max-width:640px){ .dep__foot .btn{flex:1 1 auto} }

/* ---- Department logo ----------------------------------------------------
   Never an empty dashed box: a department without a logo shows the icon and
   colour it does have, so the slot demonstrates the fallback rather than a
   hole, and "Remove" returns to something rather than to nothing. */
.dep__grid--logo{grid-template-columns:minmax(0,1fr) 220px}
@media (max-width:860px){ .dep__grid--logo{grid-template-columns:minmax(0,1fr)} }
.dep__logo{display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-3);
  padding:var(--s-4);border-radius:var(--r-md);background:var(--bg-sunken);
  border:1px dashed var(--border-strong)}
.dep__logoimg{display:grid;place-items:center;width:52px;height:52px;border-radius:14px;
  overflow:hidden;background:var(--bg-surface);border:1px solid var(--border-default)}
.dep__logoimg img{width:100%;height:100%;object-fit:cover;display:block}
.dep__logoactions{display:flex;flex-wrap:wrap;gap:6px}
.dep__mark--logo{overflow:hidden;background:var(--bg-surface);
  border:1px solid var(--border-default)}
.dep__mark--logo img{width:100%;height:100%;object-fit:cover;display:block}
/* The Catalogue card shows the same mark, so the grid and the form agree. */
.cat__tile--logo{overflow:hidden;background:var(--bg-surface);
  border:1px solid var(--border-default)}
.cat__tile--logo img{width:100%;height:100%;object-fit:cover;display:block}

/* ---- Quick create -------------------------------------------------------
   Sits with the fields it saves, and only while creating: once the row exists
   the button at the foot saves everything, and two controls that both mean
   "save" is one too many. */
/* NOTE: a second .dep__quick block sat here — the remains of a callout whose
   element was removed. Being later in the sheet it was quietly overriding the
   live Quick Info panel above, which is why that panel had a tinted background
   it never asked for. Deleted rather than reordered. */

/* What the form says once the department exists: not an alert, an offer. */
.dep__made{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);
  margin-bottom:var(--s-4);padding:var(--s-3) var(--s-4);border-radius:var(--r-md);
  background:var(--sev-ok-bg);border:1px solid var(--sev-ok-bd)}
.dep__madeicon{flex:0 0 auto;display:grid;place-items:center;color:var(--sev-ok-tx)}
.dep__madetext{flex:1 1 300px;min-width:0}
.dep__madetext strong{display:block;font-size:var(--fs-xs);
  font-weight:var(--fw-semibold);color:var(--sev-ok-tx)}
.dep__madetext p{margin:2px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary);
  line-height:1.5}
.dep__madeactions{display:flex;flex-wrap:wrap;gap:6px;flex:0 0 auto}
@media (max-width:640px){
  .dep__quick .btn,.dep__madeactions .btn{flex:1 1 auto}
  .dep__madeactions{width:100%}
}

/* ── The Catalogue's assistant ──────────────────────────────────────────────
   It sits between the title and the numbers, which is where a thing that acts
   on everything below it belongs. It reuses the department bar's row, mic and
   model-picker rules by mirroring their shapes rather than by sharing class
   names: the two bars sit on different screens with different widths, and one
   selector serving both is how a fix for one silently breaks the other.

   THE PLAN IS STYLED AS A LIST OF PROPOSALS, not of results. Cards are quiet
   until approved; only then does one take the "done" treatment. A plan that
   looked finished before anybody pressed anything would be the interface
   telling a lie the code does not tell. */
.cai{margin:var(--s-4) 0 var(--s-4);padding:var(--s-4);
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-sunken)}
.cai__head{display:flex;align-items:flex-start;gap:10px;margin-bottom:var(--s-3)}
.cai__icon{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:9px;color:var(--accent);background:var(--accent-quiet)}
.cai__head h2{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.cai__head p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}
.cai__row{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap}
/* The brief grows with what is dictated into it; `resize:none` because a drag
   handle on a one-line box invites somebody to make it a text editor.

   WRITTEN AS `textarea.cai__in`, NOT `.cai__in`, AND THAT MATTERS. There are
   two `textarea.input` blocks in this file (around 652 and 3022), and the
   second sets `min-height:72px`. Element+class beats a lone class, so a plain
   `.cai__in{min-height:0}` loses and the one-line brief box renders three
   lines tall — which is exactly what it did before this selector was
   qualified. Same trap the department form's `textarea.input.grows` rules
   exist to escape. */
textarea.cai__in{flex:1 1 320px;min-width:0;min-height:0;resize:none;
  overflow:hidden;line-height:1.45}
.cai__mic{flex:0 0 auto}
.cai__mic.is-live{background:var(--sev-critical-bg);border-color:var(--sev-critical-bd);
  color:var(--sev-critical-tx)}
.cai__mic.is-live svg{animation:v9-mic-pulse 1.1s ease-in-out infinite}
.cai__model{flex:0 1 220px;min-width:0}
.cai__model .cb__pop{right:auto;min-width:330px}
.cai__model .cb__opt{flex-direction:column;align-items:stretch;gap:3px;
  padding-block:6px}
.cai__model .cb__val{display:flex;align-items:center;gap:6px}
.cai__mname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cai__mlink{display:block;margin-top:6px;font-size:var(--fs-2xs)}
.cai__mlink a{color:var(--text-tertiary);text-decoration:none}
.cai__mlink a:hover{color:var(--accent);text-decoration:underline}
.cai__mlink a:focus-visible{outline:2px solid var(--accent);outline-offset:2px;
  border-radius:3px}
.cai__note{margin:8px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.cai__note[data-tone="ok"]{color:var(--sev-ok-tx)}
.cai__note[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

.cai__plan{margin-top:var(--s-3);display:flex;flex-direction:column;gap:6px}
.cai__planhead{display:flex;align-items:baseline;gap:8px;
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.cai__count{color:var(--text-tertiary)}
.cai__card{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;
  border:1px solid var(--border-default);border-left:3px solid var(--tone,var(--border-default));
  border-radius:var(--r-md,8px);background:var(--bg-surface)}
.cai__card[data-done]{opacity:.7;border-left-color:var(--sev-ok-tx)}
.cai__cicon{flex:0 0 auto;display:grid;place-items:center;width:26px;height:26px;
  border-radius:7px;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 14%,transparent)}
.cai__cbody{flex:1 1 auto;min-width:0}
.cai__ckind{display:block;font-size:var(--fs-3xs,10px);letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary)}
/* The sentence may be long and must never be clipped — it is the thing being
   approved, so every word of it has to be readable. */
.cai__csum{margin:2px 0 0;font-size:var(--fs-xs);color:var(--text-primary);
  overflow-wrap:anywhere}
.cai__cgo{flex:0 0 auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  justify-content:flex-end}
.cai__said{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.cai__said[data-tone="ok"]{color:var(--sev-ok-tx)}
.cai__said[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}
.cai__all{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:2px}
.cai__allnote{font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* At phone width the card stacks: a button beside a two-line sentence in 360px
   leaves the sentence about eight characters wide. */
@media (max-width:640px){
  .cai__card{flex-wrap:wrap}
  .cai__cgo{width:100%;justify-content:flex-start}
}

/* ── The Beta mark ──────────────────────────────────────────────────────────
   ONE TREATMENT FOR ALL THREE ASSISTANTS. The Tasks composer used a severity
   tag that happened to read "Beta", which put it in the same visual family as
   CRITICAL and LOW — a badge meaning "not finished" should not be styled as a
   badge meaning "how bad is it".

   Quiet on purpose. It has to be legible beside a heading without competing
   with it: somebody should notice it once, understand it, and then stop
   seeing it. Uppercase and tracked so it reads as a stamp rather than a word
   in the sentence. */
.betamark{flex:0 0 auto;align-self:center;
  padding:1px 6px;border-radius:999px;
  border:1px solid var(--accent-bd,var(--border-default));
  background:var(--accent-quiet);color:var(--accent);
  font-size:var(--fs-3xs,10px);font-weight:var(--fw-semibold);
  letter-spacing:.07em;text-transform:uppercase;line-height:1.6;
  white-space:nowrap;cursor:help}
/* The heading and its mark on one baseline, wrapping together rather than the
   pill dropping alone onto a line of its own at narrow widths. */
.cai__h2row{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.cai__h2row h2{margin:0}

/* ── The AI Approval Center ─────────────────────────────────────────────────
   A TRIAGE SCREEN, in the Founder's layout: how much is waiting and how bad,
   then a table to pick from, then everything about the one thing picked.

   The table and the panel sit side by side on a wide screen and stack on a
   narrow one — reviewing a machine's work means reading the row and the detail
   together, and a panel that covered the list would make every comparison a
   navigation. */
.aia__title{display:inline-flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.aia__loading{margin:var(--s-4) 0;font-size:var(--fs-xs);color:var(--text-tertiary)}
.aia__hint{margin:0 0 var(--s-3);font-size:var(--fs-2xs);color:var(--text-secondary)}

/* ---- The four counts --------------------------------------------------
   Auto-fit rather than four fixed columns: at tablet width four cards of this
   density become unreadable slivers, and wrapping to two rows of two keeps the
   number and its label on the same line. */
.aia__kpis{display:grid;gap:var(--s-3);margin:var(--s-4) 0;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.aia__kpi{display:flex;align-items:center;gap:12px;padding:14px 16px;
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface)}
.aia__kpiicon{flex:0 0 auto;display:grid;place-items:center;width:38px;height:38px;
  border-radius:50%;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 14%,transparent)}
.aia__kpi[data-sev="critical"]{--tone:var(--sev-critical)}
.aia__kpi[data-sev="high"]{--tone:var(--sev-high)}
.aia__kpi[data-sev="medium"]{--tone:var(--sev-medium)}
.aia__kpi[data-sev="low"]{--tone:var(--sev-ok,var(--sev-low))}
.aia__kpi strong{display:block;font-size:var(--fs-xl,22px);line-height:1.15;
  font-variant-numeric:tabular-nums;color:var(--text-primary)}
.aia__kpilabel{display:block;font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.aia__kpihint{display:block;font-size:var(--fs-2xs);color:var(--text-tertiary)}

/* ---- Table beside panel ---------------------------------------------- */
.aia__body{display:grid;gap:var(--s-4);align-items:start;
  grid-template-columns:minmax(0,1fr)}
@media (min-width:1180px){
  .aia__body[data-x]{grid-template-columns:minmax(0,1fr)}
  .aia__body:has(.aia__panel:not([hidden])){grid-template-columns:minmax(0,1fr) 380px}
}
.aia__main{min-width:0;border:1px solid var(--border-default);
  border-radius:var(--r-lg,12px);background:var(--bg-surface);overflow:hidden}
.aia__toolbar{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;
  padding:10px 12px;border-bottom:1px solid var(--border-default)}
.aia__tabs{flex:1 1 auto;min-width:0;flex-wrap:wrap}
.aia__sort{flex:0 0 auto;margin-inline-start:auto}

/* A dense table must be allowed to scroll sideways rather than crush its own
   columns; everything else on the page stays put. */
.aia__tablewrap{overflow-x:auto}
.aia__table{width:100%;border-collapse:collapse;font-size:var(--fs-xs)}
.aia__table th{padding:9px 12px;text-align:start;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);letter-spacing:.04em;text-transform:uppercase;
  color:var(--text-tertiary);border-bottom:1px solid var(--border-default);
  white-space:nowrap}
.aia__table td{padding:10px 12px;border-bottom:1px solid var(--border-subtle,var(--border-default));
  vertical-align:middle}
.aia__cbcol{width:36px}
.aia__tr{cursor:pointer;transition:background var(--dur-micro) var(--ease)}
.aia__tr:hover{background:var(--bg-sunken)}
.aia__tr:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aia__tr[data-on]{background:var(--accent-quiet)}
.aia__table th:nth-child(2),.aia__table td:nth-child(2){min-width:230px;width:30%}
.aia__req{display:flex;align-items:flex-start;gap:9px;min-width:0}
.aia__reqicon{flex:0 0 auto;display:grid;place-items:center;width:28px;height:28px;
  border-radius:8px;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.aia__reqicon[data-sev="critical"]{--tone:var(--sev-critical)}
.aia__reqicon[data-sev="high"]{--tone:var(--sev-high)}
.aia__reqicon[data-sev="medium"]{--tone:var(--sev-medium)}
.aia__reqicon[data-sev="low"]{--tone:var(--sev-ok,var(--sev-low))}
.aia__reqtext{min-width:0;display:flex;flex-direction:column;gap:1px}
/* The sentence somebody approves. It may be long and is never clipped — two
   lines then ellipsis, with the whole of it in the panel beside. */
.aia__reqtitle{color:var(--text-primary);overflow-wrap:break-word;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.aia__reqsub{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.aia__src{display:flex;flex-direction:column;gap:1px;min-width:0}
.aia__srcname{color:var(--text-primary);overflow-wrap:break-word}
.aia__srcmodel{font-size:var(--fs-2xs);color:var(--text-tertiary);overflow-wrap:break-word}
.aia__why{max-width:260px;color:var(--text-secondary);overflow-wrap:break-word;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.aia__none{color:var(--text-tertiary)}
.aia__when{white-space:pre-line;font-size:var(--fs-2xs);color:var(--text-secondary);
  font-variant-numeric:tabular-nums}

/* ---- Bulk bar and pagination ----------------------------------------- */
.aia__bulk{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:10px 12px;border-top:1px solid var(--border-default);
  background:var(--accent-quiet)}
.aia__bulkn{font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-primary)}
.aia__foot{display:flex;align-items:center;justify-content:space-between;gap:10px;
  flex-wrap:wrap;padding:10px 12px;border-top:1px solid var(--border-default)}
.aia__count{font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.aia__pages{display:flex;gap:4px;flex-wrap:wrap}
.aia__page{min-width:30px;height:30px;padding:0 8px;border-radius:var(--r-sm,6px);
  border:1px solid var(--border-default);background:var(--bg-surface);
  color:var(--text-secondary);font-size:var(--fs-2xs);cursor:pointer;
  font-variant-numeric:tabular-nums}
.aia__page:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}
.aia__page[data-on]{background:var(--accent);border-color:var(--accent);color:#fff}
.aia__page:disabled{opacity:.45;cursor:default}
.aia__page:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- The panel --------------------------------------------------------- */
.aia__panel{min-width:0;position:sticky;top:var(--s-4);
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface);padding:var(--s-4);
  max-height:calc(100vh - var(--s-6,48px));overflow-y:auto}
.aia__phead{display:flex;align-items:center;justify-content:space-between;gap:10px}
.aia__x{width:26px;height:26px;border:0;border-radius:var(--r-sm,6px);
  background:transparent;color:var(--text-tertiary);cursor:pointer;font-size:14px}
.aia__x:hover{background:var(--bg-sunken);color:var(--text-primary)}
.aia__x:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aia__ptitle{margin:8px 0 var(--s-3);font-size:var(--fs-md,16px);
  font-weight:var(--fw-semibold);color:var(--text-primary);overflow-wrap:anywhere}
.aia__pane{margin-top:var(--s-3);display:flex;flex-direction:column;gap:var(--s-3)}
/* The model's own sentence, marked as such — the one place its prose is shown. */
.aia__rec{display:flex;gap:10px;padding:12px;border-radius:var(--r-md,8px);
  background:var(--accent-quiet)}
.aia__recicon{flex:0 0 auto;color:var(--accent)}
.aia__rec strong{display:block;font-size:var(--fs-2xs);color:var(--text-primary)}
.aia__rec p{margin:3px 0 0;font-size:var(--fs-xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
/* The brief is QUOTED, not restyled into body copy: it is somebody's own words
   and the panel's job is to let them be compared against what the machine did. */
.aia__brief{padding:12px;border-radius:var(--r-md,8px);background:var(--bg-sunken);
  border-inline-start:3px solid var(--accent)}
.aia__brief h3{margin:0 0 5px;font-size:var(--fs-2xs);font-weight:var(--fw-semibold);
  letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}
.aia__brief blockquote{margin:0;font-size:var(--fs-xs);color:var(--text-primary);
  overflow-wrap:anywhere}
.aia__touches{display:flex;flex-wrap:wrap;gap:6px}
.aia__touch{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;
  border-radius:999px;background:var(--bg-sunken);color:var(--text-secondary);
  font-size:var(--fs-2xs)}
.aia__json{margin:0;padding:8px 10px;border-radius:var(--r-sm,6px);
  background:var(--bg-sunken);font-size:var(--fs-2xs);
  white-space:pre;overflow-x:auto;max-width:100%}
.aia__sibs{display:flex;flex-direction:column;gap:6px}
.aia__sib{display:flex;align-items:center;gap:9px;flex-wrap:wrap;width:100%;
  text-align:start;padding:8px 10px;border:0;border-radius:var(--r-sm,6px);
  background:var(--bg-sunken);color:inherit;font:inherit;cursor:pointer}
.aia__sib[data-on]{background:var(--accent-quiet);cursor:default}
.aia__sibsum{flex:1 1 140px;min-width:0;font-size:var(--fs-xs);overflow-wrap:anywhere}
.aia__sibop{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.aia__decide{margin-top:var(--s-4);padding-top:var(--s-3);
  border-top:1px solid var(--border-default)}
.aia__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:var(--s-3)}
.aia__actions .btn{flex:1 1 auto;justify-content:center}
.aia__said{margin:8px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.aia__said[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

/* A rejected proposal is struck through rather than removed: the point of the
   ledger is that saying no leaves a trace. */
.cai__card[data-rejected]{opacity:.6}
.cai__card[data-rejected] .cai__csum{text-decoration:line-through}
.cai__no{flex:0 0 auto}

/* ── Both Approval Centers ──────────────────────────────────────────────────
   ONE SET OF RULES FOR TWO SCREENS. The AI centre and the Human centre ask
   different questions but are the same shape — counts, a filtered table, a
   detail panel beside it. Two stylesheets would be two places for a padding
   fix to land, and the pair would drift within a month. */
.apc__title{display:inline-flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.apc__loading{margin:var(--s-4);font-size:var(--fs-xs);color:var(--text-tertiary)}
.apc__hint{margin:0;font-size:var(--fs-2xs);color:var(--text-secondary)}

/* ---- The four counts --------------------------------------------------- */
.apc__kpis{display:grid;gap:var(--s-3);margin:var(--s-4) 0;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.apc__kpi{display:flex;align-items:center;gap:13px;padding:16px;
  border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface)}
.apc__kpiicon{flex:0 0 auto;display:grid;place-items:center;width:44px;height:44px;
  border-radius:50%;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.apc__kpi[data-sev="critical"]{--tone:var(--sev-critical)}
.apc__kpi[data-sev="high"]{--tone:var(--sev-high)}
.apc__kpi[data-sev="medium"]{--tone:var(--sev-medium)}
.apc__kpi[data-sev="low"]{--tone:var(--sev-ok,var(--sev-low))}
.apc__kpibody{min-width:0}
.apc__kpi strong{display:block;font-size:26px;line-height:1.1;
  font-variant-numeric:tabular-nums;color:var(--text-primary)}
.apc__kpilabel{display:block;font-size:var(--fs-xs);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.apc__kpihint{display:block;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.apc__delta{font-weight:var(--fw-semibold)}
.apc__delta[data-dir="up"]{color:var(--sev-ok-tx,var(--sev-ok))}
.apc__delta[data-dir="down"]{color:var(--sev-critical-tx)}

/* ---- Table beside panel ------------------------------------------------ */
.apc__body{display:grid;gap:var(--s-4);align-items:start;
  grid-template-columns:minmax(0,1fr)}
@media (min-width:1240px){
  .apc__body:has(.apc__panelhost:not([hidden])){grid-template-columns:minmax(0,1fr) 400px}
}
.apc__main{min-width:0;border:1px solid var(--border-default);
  border-radius:var(--r-lg,12px);background:var(--bg-surface);overflow:hidden}
.apc__tabs{padding:4px 12px 0;flex-wrap:wrap;border-bottom:1px solid var(--border-default)}
.apc__filters{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:10px 12px;border-bottom:1px solid var(--border-default)}
.apc__searchwrap{position:relative;display:flex;align-items:center;
  flex:1 1 200px;min-width:0}
.apc__searchwrap svg{position:absolute;inset-inline-start:10px;color:var(--text-tertiary);
  pointer-events:none}
.apc__search{padding-inline-start:32px}
.apc__filter{flex:0 1 auto;width:auto;min-width:130px;max-width:190px}

.apc__tablewrap{overflow-x:auto}
.apc__table{width:100%;border-collapse:collapse;font-size:var(--fs-xs)}
.apc__table th{padding:10px 12px;text-align:start;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);letter-spacing:.03em;color:var(--text-tertiary);
  border-bottom:1px solid var(--border-default);white-space:nowrap}
.apc__table td{padding:11px 12px;vertical-align:middle;
  border-bottom:1px solid var(--border-subtle,var(--border-default))}
.apc__cbcol{width:36px}
.apc__morecol{width:34px;text-align:end}
.apc__more{width:26px;height:26px;border:0;border-radius:var(--r-sm,6px);
  background:transparent;color:var(--text-tertiary);cursor:pointer;font-size:15px}
.apc__more:hover{background:var(--bg-sunken);color:var(--text-primary)}
.apc__tr{cursor:pointer;transition:background var(--dur-micro) var(--ease)}
.apc__tr:hover{background:var(--bg-sunken)}
.apc__tr:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.apc__tr[data-on]{background:var(--accent-quiet)}

/* ── overflow-wrap IN A TABLE CELL ──────────────────────────────────────────
   `anywhere` AND `break-word` WRAP THE SAME; THEY SIZE DIFFERENTLY. `anywhere`
   counts toward intrinsic sizing, so a cell using it reports a min-content
   width of ONE CHARACTER. An auto-layout table believes that, hands the column
   almost nothing, and renders the title vertically — one letter per line, the
   row a thousand pixels tall. `break-word` wraps long words exactly the same
   way at render time but leaves min-content alone, which is what a table needs.

   Every cell below therefore uses `break-word`. `anywhere` is still right
   OUTSIDE a table — in the panel, where the container width is fixed and a long
   unbroken string must never push it wider — and is left alone there. */
/* THE SENTENCE IS THE POINT OF THE ROW, so its column gets the widest share
   and a floor under it. Without the floor a table full of long department
   names and job titles squeezes the one column somebody actually reads. */
.apc__table th:nth-child(2),.apc__table td:nth-child(2){min-width:230px;width:32%}
.apc__req{display:flex;align-items:flex-start;gap:10px;min-width:0}
.apc__reqicon{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:8px;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.apc__reqtext{min-width:0;display:flex;flex-direction:column;gap:1px}
.apc__reqtitle{color:var(--text-primary);font-weight:var(--fw-medium,500);
  overflow-wrap:break-word}
.apc__reqsub{font-size:var(--fs-2xs);color:var(--text-tertiary);overflow-wrap:break-word}
.apc__typechip{display:inline-block;padding:3px 10px;border-radius:999px;
  font-size:var(--fs-2xs);font-weight:var(--fw-semibold);white-space:nowrap;
  color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.apc__dept{color:var(--text-secondary);overflow-wrap:break-word}
.apc__person{display:flex;align-items:center;gap:8px;min-width:0}
.apc__face{flex:0 0 auto;display:grid;place-items:center;width:28px;height:28px;
  border-radius:50%;overflow:hidden;background:var(--bg-sunken);
  color:var(--text-secondary);font-size:var(--fs-3xs,10px);
  font-weight:var(--fw-semibold)}
.apc__face img{width:100%;height:100%;object-fit:cover}
.apc__persontext{min-width:0;display:flex;flex-direction:column;gap:0}
.apc__personname{color:var(--text-primary);overflow-wrap:break-word}
.apc__personrole{font-size:var(--fs-2xs);color:var(--text-tertiary);overflow-wrap:break-word}
.apc__when{display:flex;flex-direction:column;font-size:var(--fs-2xs);
  color:var(--text-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}
.apc__time{color:var(--text-tertiary)}

.apc__bulk{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:10px 12px;border-top:1px solid var(--border-default);
  background:var(--accent-quiet)}
.apc__bulkn{font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--text-primary)}
.apc__foot{display:flex;align-items:center;justify-content:space-between;gap:10px;
  flex-wrap:wrap;padding:10px 12px;border-top:1px solid var(--border-default)}
.apc__count{font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.apc__pages{display:flex;gap:4px;flex-wrap:wrap}
.apc__page{min-width:30px;height:30px;padding:0 8px;border-radius:var(--r-sm,6px);
  border:1px solid var(--border-default);background:var(--bg-surface);
  color:var(--text-secondary);font-size:var(--fs-2xs);cursor:pointer;
  font-variant-numeric:tabular-nums}
.apc__page:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}
.apc__page[data-on]{background:var(--accent);border-color:var(--accent);color:#fff}
.apc__page:disabled{opacity:.45;cursor:default}
.apc__page:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.apc__rpp{display:flex;align-items:center;gap:8px;font-size:var(--fs-2xs);
  color:var(--text-tertiary)}
.apc__rppsel{width:auto;min-width:64px}

/* ---- The panel --------------------------------------------------------- */
.apc__panelhost{min-width:0;position:sticky;top:var(--s-4);
  max-height:calc(100vh - var(--s-6,48px));overflow-y:auto;
  display:flex;flex-direction:column;gap:var(--s-3)}
.apc__panel{border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface);padding:var(--s-4)}
.apc__phead{display:flex;align-items:center;justify-content:space-between;gap:10px}
.apc__pheadleft{display:flex;align-items:center;gap:8px;min-width:0}
.apc__pheadicon{display:grid;place-items:center;width:28px;height:28px;
  border-radius:8px;color:var(--accent);background:var(--accent-quiet)}
.apc__pchip{font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--text-primary)}
.apc__x{width:26px;height:26px;border:0;border-radius:var(--r-sm,6px);
  background:transparent;color:var(--text-tertiary);cursor:pointer;font-size:13px}
.apc__x:hover{background:var(--bg-sunken);color:var(--text-primary)}
.apc__x:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.apc__ptitle{margin:var(--s-3) 0 2px;font-size:var(--fs-md,16px);
  font-weight:var(--fw-semibold);color:var(--text-primary);overflow-wrap:anywhere}
.apc__psub{margin:0 0 var(--s-3);font-size:var(--fs-2xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
/* FOUR TABS IN A 400px PANEL. They do not fit on one line and must not be
   clipped — "Hist" is not a word, and a tab somebody cannot read is a tab they
   cannot find. Wrapping to a second line is the honest answer; a horizontal
   scroller inside a panel that already scrolls vertically is two scrollbars
   fighting. */
.apc__panel .tabs{flex-wrap:wrap;row-gap:2px}
.apc__panel .tab{padding-inline:10px}
.apc__pane{margin-top:var(--s-3);display:flex;flex-direction:column;gap:var(--s-3)}
.apc__details{display:flex;flex-direction:column;gap:9px}
.apc__drow{display:grid;grid-template-columns:minmax(0,132px) minmax(0,1fr);
  gap:10px;align-items:start;font-size:var(--fs-xs)}
.apc__dlabel{display:flex;align-items:center;gap:6px;color:var(--text-tertiary);
  overflow-wrap:anywhere}
.apc__dicon{flex:0 0 auto;display:inline-grid;place-items:center;color:var(--text-tertiary)}
.apc__dvalue{min-width:0;color:var(--text-primary);overflow-wrap:anywhere}
/* At panel width a two-column row leaves the value about eight characters
   wide, so below that it stacks. */
@media (max-width:1400px){
  .apc__drow{grid-template-columns:minmax(0,1fr)}
  .apc__dvalue{padding-inline-start:19px}
}
.apc__quote{margin:var(--s-2) 0 0;display:flex;gap:10px;padding:12px;
  border-radius:var(--r-md,8px);background:var(--bg-sunken)}
.apc__quotemark{flex:0 0 auto;font-size:22px;line-height:1;color:var(--text-tertiary)}
.apc__quote strong{display:block;font-size:var(--fs-2xs);color:var(--text-primary)}
.apc__quote p{margin:4px 0 0;font-size:var(--fs-xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
.apc__files{display:flex;flex-direction:column;gap:6px}
.apc__file{display:flex;align-items:center;gap:9px;padding:9px 11px;
  border-radius:var(--r-sm,6px);background:var(--bg-sunken);font-size:var(--fs-xs)}
.apc__fileicon{flex:0 0 auto;color:var(--text-tertiary)}
.apc__filename{flex:1 1 auto;min-width:0;overflow-wrap:anywhere;color:var(--text-primary)}
.apc__filesize{flex:0 0 auto;font-size:var(--fs-2xs);color:var(--text-tertiary);
  font-variant-numeric:tabular-nums}
.apc__comments{display:flex;flex-direction:column;gap:10px}
.apc__comment{padding:10px 12px;border-radius:var(--r-md,8px);background:var(--bg-sunken)}
.apc__commenthead{display:flex;align-items:baseline;justify-content:space-between;
  gap:8px;flex-wrap:wrap;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.apc__commenthead strong{color:var(--text-primary)}
.apc__comment p{margin:4px 0 0;font-size:var(--fs-xs);color:var(--text-secondary);
  overflow-wrap:anywhere}
.apc__commentbox{display:flex;gap:8px;align-items:flex-end;margin-top:var(--s-2)}
.apc__commentbox textarea{flex:1 1 auto;min-width:0}
/* The audit reads forwards, down a rule, because a trail is a sequence. */
.apc__history{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.apc__history li{display:flex;gap:10px;font-size:var(--fs-xs);position:relative}
.apc__dot{flex:0 0 auto;width:9px;height:9px;margin-top:5px;border-radius:50%;
  background:var(--accent)}
.apc__history li:not(:last-child)::before{content:'';position:absolute;
  inset-block-start:16px;inset-inline-start:4px;width:1px;height:calc(100% + 4px);
  background:var(--border-default)}
.apc__hwho{color:var(--text-secondary)}
.apc__hwhen{display:block;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.apc__hnote{margin:3px 0 0;color:var(--text-secondary);overflow-wrap:anywhere}

.apc__decide{border:1px solid var(--border-default);border-radius:var(--r-lg,12px);
  background:var(--bg-surface);padding:var(--s-4)}
.apc__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:var(--s-3)}
.apc__actions .btn{flex:1 1 auto;justify-content:center}
.apc__said{margin:8px 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:anywhere}
.apc__said[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

/* The reference's confidence bar and impact row. Both are drawn only when the
   row carries the data — see views.aiapprovals.js for why that matters. */
.aia__conf{padding:12px;border-radius:var(--r-md,8px);background:var(--bg-sunken)}
.aia__confhead{display:flex;align-items:baseline;justify-content:space-between;
  gap:8px;font-size:var(--fs-2xs);color:var(--text-secondary)}
.aia__confhead strong{font-size:var(--fs-sm);color:var(--text-primary);
  font-variant-numeric:tabular-nums}
.aia__confbar{margin-top:6px;height:6px;border-radius:999px;overflow:hidden;
  background:color-mix(in srgb,var(--text-tertiary) 20%,transparent)}
.aia__confbar span{display:block;height:100%;border-radius:999px;
  background:var(--sev-ok,#16A34A)}
.aia__impact{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(90px,1fr))}
.aia__imp{display:flex;flex-direction:column;align-items:center;gap:6px;
  text-align:center;font-size:var(--fs-2xs);color:var(--text-secondary)}
.aia__impicon{display:grid;place-items:center;width:38px;height:38px;border-radius:50%;
  color:var(--accent);background:var(--accent-quiet)}

/* ── Alert Settings and the alert inbox ─────────────────────────────────────
   Two columns on a wide screen — the things you set on the left, the rules that
   govern them on the right — collapsing to one below 1180px, because the right
   rail's cards are unreadable much narrower than 320px. */
.alt__body{display:grid;gap:var(--s-4);align-items:start;
  grid-template-columns:minmax(0,1fr)}
@media (min-width:1180px){ .alt__body{grid-template-columns:minmax(0,1fr) 360px} }
.alt__rail{display:flex;flex-direction:column;gap:var(--s-4);min-width:0}
.alt__card{margin-bottom:var(--s-4);padding:var(--s-4)}
.alt__rail .alt__card{margin-bottom:0}
.alt__loading{margin:var(--s-4) 0;font-size:var(--fs-xs);color:var(--text-tertiary)}
.alt__sechead{display:flex;align-items:flex-start;gap:11px;margin-bottom:var(--s-3);
  flex-wrap:wrap}
.alt__secicon{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;
  border-radius:9px;color:var(--accent);background:var(--accent-quiet)}
.alt__sechead h2{margin:0;font-size:var(--fs-sm);font-weight:var(--fw-semibold);
  color:var(--text-primary)}
.alt__sechead p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}
.alt__secactions{margin-inline-start:auto;display:flex;align-items:center;gap:8px;
  flex-wrap:wrap}
.alt__searchwrap{position:relative;display:flex;align-items:center}
.alt__searchwrap svg{position:absolute;inset-inline-start:9px;color:var(--text-tertiary);
  pointer-events:none}
.alt__search{padding-inline-start:30px;width:190px}

/* ---- The switch -------------------------------------------------------- */
.alt__sw{flex:0 0 auto;width:38px;height:22px;padding:0;border:0;cursor:pointer;
  border-radius:999px;background:var(--border-default);position:relative;
  transition:background var(--dur-micro) var(--ease)}
.alt__sw[aria-checked="true"]{background:var(--accent)}
.alt__swdot{position:absolute;inset-block-start:3px;inset-inline-start:3px;
  width:16px;height:16px;border-radius:50%;background:#fff;
  transition:transform var(--dur-micro) var(--ease)}
.alt__sw[aria-checked="true"] .alt__swdot{transform:translateX(16px)}
.alt__sw:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- Channel cards ----------------------------------------------------- */
.alt__chans{display:grid;gap:var(--s-3);
  grid-template-columns:repeat(auto-fit,minmax(178px,1fr))}
.alt__chan{padding:13px;border:1px solid var(--border-default);
  border-radius:var(--r-md,10px);background:var(--bg-surface)}
.alt__chanhead{display:flex;align-items:center;justify-content:space-between;
  gap:8px;margin-bottom:9px}
.alt__chanicon{display:grid;place-items:center;width:34px;height:34px;
  border-radius:9px;color:var(--accent);background:var(--accent-quiet)}
.alt__chan strong{display:block;font-size:var(--fs-xs);color:var(--text-primary)}
.alt__chan p{margin:2px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary)}
/* The honest line. Toned so "not connected" is visibly not the same as
   "working now" — a channel that does nothing must not look reassuring. */
.alt__chanstate{display:inline-block;margin-top:8px;padding:2px 8px;
  border-radius:999px;font-size:var(--fs-3xs,10px);font-weight:var(--fw-semibold)}
.alt__chan[data-state="live"] .alt__chanstate{
  color:var(--sev-ok-tx,var(--sev-ok));background:var(--sev-ok-bg,transparent)}
.alt__chan[data-state="queued"] .alt__chanstate{
  color:var(--sev-medium-tx,var(--sev-medium));background:var(--sev-medium-bg,transparent)}
/* The test-send control, only on the email card. It sits under the state pill
   and wraps rather than widening the card — the grid is auto-fit and one wide
   card would re-flow the other four. */
/* Three counts on one line, wrapping to one per line on a phone. */
.as__delivrow{display:flex;flex-wrap:wrap;gap:14px;align-items:center;
  margin-bottom:8px;font-size:var(--fs-2xs);color:var(--text-secondary)}
.as__delivrow > span{display:inline-flex;align-items:center;gap:6px}
.alt__testrow{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:9px}
.alt__test{font-size:var(--fs-3xs,10px);color:var(--text-secondary);
  overflow-wrap:break-word;min-width:0}
.alt__test[data-tone="ok"]{color:var(--sev-ok-tx,var(--sev-ok))}
.alt__test[data-tone="bad"]{color:var(--sev-critical-tx,var(--sev-critical))}
.alt__chan[data-state="off"] .alt__chanstate{
  color:var(--text-tertiary);background:var(--bg-sunken)}

/* ---- The categories table ---------------------------------------------- */
.alt__tablewrap{overflow-x:auto}
.alt__table{width:100%;border-collapse:collapse;font-size:var(--fs-xs)}
.alt__table th{padding:9px 10px;text-align:start;font-size:var(--fs-2xs);
  font-weight:var(--fw-semibold);color:var(--text-tertiary);
  border-bottom:1px solid var(--border-default);white-space:nowrap}
.alt__table td{padding:10px;border-bottom:1px solid var(--border-subtle,var(--border-default));
  vertical-align:middle}
.alt__table th.alt__ch,.alt__table td.alt__ch{text-align:center;width:70px}
/* `break-word`, not `anywhere` — see the note above the Approval Centre rules:
   `anywhere` reports a one-character min-content and collapses the column. */
.alt__cat{display:flex;align-items:center;gap:9px;min-width:0}
.alt__caticon{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:8px;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.alt__cat strong{color:var(--text-primary);overflow-wrap:break-word}
.alt__catdesc{color:var(--text-secondary);overflow-wrap:break-word;min-width:200px}

/* ---- Priority rows, digest, quiet hours -------------------------------- */
.alt__prios{display:flex;flex-direction:column;gap:9px}
.alt__prio{display:flex;align-items:center;gap:10px;padding:9px;
  border-radius:var(--r-md,8px);background:var(--bg-sunken)}
.alt__prioicon{flex:0 0 auto;display:grid;place-items:center;width:32px;height:32px;
  border-radius:50%;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 15%,transparent)}
.alt__prio[data-sev="critical"]{--tone:var(--sev-critical)}
.alt__prio[data-sev="high"]{--tone:var(--sev-high)}
.alt__prio[data-sev="medium"]{--tone:var(--sev-medium)}
.alt__prio[data-sev="low"]{--tone:var(--sev-ok,var(--sev-low))}
.alt__prio > div{flex:1 1 auto;min-width:0}
.alt__prio strong{display:block;font-size:var(--fs-xs);color:var(--tone,var(--text-primary))}
.alt__prio p{margin:1px 0 0;font-size:var(--fs-2xs);color:var(--text-secondary);
  overflow-wrap:break-word}
.alt__digest{display:flex;flex-direction:column;gap:4px}
.alt__dgrow{display:flex;align-items:flex-start;gap:9px;padding:7px 8px;
  border-radius:var(--r-sm,6px);cursor:pointer}
.alt__dgrow:hover{background:var(--bg-sunken)}
.alt__dgrow strong{display:block;font-size:var(--fs-xs);color:var(--text-primary)}
.alt__dgrow span span{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.alt__note{margin:var(--s-3) 0 0;font-size:var(--fs-2xs);color:var(--text-tertiary);
  overflow-wrap:break-word}
.alt__grid2{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(130px,1fr))}
.alt__grid2 label,.alt__tz{display:flex;flex-direction:column;gap:5px;
  font-size:var(--fs-2xs);color:var(--text-secondary)}
.alt__tz{margin-top:10px}
.alt__critrow{display:flex;align-items:flex-start;gap:9px;margin-top:12px;
  cursor:pointer}
.alt__critrow strong{display:block;font-size:var(--fs-xs);color:var(--text-primary)}
.alt__critrow span span{font-size:var(--fs-2xs);color:var(--text-tertiary)}
.alt__actions{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.alt__actions .btn{flex:1 1 auto;justify-content:center}
.alt__said{width:100%;margin:0;font-size:var(--fs-2xs);color:var(--text-tertiary)}
.alt__said[data-tone="bad"]{color:var(--sev-critical-tx);font-weight:var(--fw-semibold)}

/* ---- The inbox --------------------------------------------------------- */
.alt__alert{display:flex;align-items:flex-start;gap:11px;width:100%;text-align:start;
  padding:12px;margin-top:6px;border:1px solid var(--border-default);
  border-radius:var(--r-md,8px);background:var(--bg-surface);color:inherit;
  cursor:pointer;transition:border-color var(--dur-micro) var(--ease)}
.alt__alert:hover{border-color:var(--accent)}
.alt__alert:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Unread is a WEIGHT and a dot, not only a colour — colour alone is not a
   signal everybody receives. */
.alt__alert[data-unread]{background:var(--accent-quiet)}
.alt__alert[data-unread] strong{font-weight:var(--fw-semibold)}
.alt__alicon{flex:0 0 auto;display:grid;place-items:center;width:32px;height:32px;
  border-radius:9px;color:var(--tone,var(--text-secondary));
  background:color-mix(in srgb,var(--tone,var(--text-tertiary)) 13%,transparent)}
.alt__albody{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:3px}
.alt__altop{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.alt__altop strong{flex:1 1 180px;min-width:0;font-size:var(--fs-xs);
  color:var(--text-primary);overflow-wrap:break-word}
.alt__albodytext{font-size:var(--fs-2xs);color:var(--text-secondary);
  overflow-wrap:break-word}
.alt__almeta{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  font-size:var(--fs-2xs);color:var(--text-tertiary)}
.alt__dot{flex:0 0 auto;align-self:center;width:8px;height:8px;border-radius:50%;
  background:var(--accent)}

/* The routing rules on Approval Settings: a statement of what happens, not a
   set of controls — so they read as prose with a tick, not as a form. */
.as__rules{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.as__rules li{display:flex;align-items:flex-start;gap:8px;font-size:var(--fs-xs);
  color:var(--text-secondary);overflow-wrap:break-word}
.as__rules li svg{flex:0 0 auto;margin-top:2px;color:var(--sev-ok,#16A34A)}
.as__link{margin-top:var(--s-3)}
.as__link a{color:var(--accent)}

/* ═══════════════════════════════════════════════════════════════════════════
   ALERTS CONSOLE (views.alerts.js — ALERT_VIEWS.alerts)
   Built to the Founder's reference: five counters, a tab row carrying counts,
   a filter bar, the table, and a sidebar that repeats the counts as filters.

   Every colour, radius, space and size below is a TOKEN. The reference is a
   picture; matching it with literals would put a second palette in the file
   and the screen would stop following the theme the moment either changed.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The five counters ──────────────────────────────────────────────────── */
.acs__stats {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
/* Five across is the reference at desktop. Below that they wrap rather than
   shrink: a counter squeezed under its own number reads as broken. */
@media (max-width: 1280px) { .acs__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .acs__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px)  { .acs__stats { grid-template-columns: minmax(0, 1fr); } }

.acs__stat {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: border-color .12s ease, background .12s ease;
}
.acs__stat:hover { background: var(--bg-hover); border-color: var(--border-default); }
.acs__stat[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-quiet); }
.acs__stat:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }

/* The tinted disc. One rule, coloured by data-tone, so a new counter needs a
   token pair rather than another block of CSS. */
.acs__statIco {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--sev-info-bg);
  color: var(--sev-info-tx);
}
.acs__stat[data-tone="critical"] .acs__statIco { background: var(--sev-critical-bg); color: var(--sev-critical-tx); }
.acs__stat[data-tone="high"]     .acs__statIco { background: var(--sev-high-bg);     color: var(--sev-high-tx); }
.acs__stat[data-tone="info"]     .acs__statIco { background: var(--sev-info-bg);     color: var(--sev-info-tx); }
.acs__stat[data-tone="ok"]       .acs__statIco { background: var(--sev-ok-bg);       color: var(--sev-ok-tx); }
.acs__stat[data-tone="medium"]   .acs__statIco { background: var(--sev-medium-bg);   color: var(--sev-medium-tx); }

.acs__statBody { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.acs__statNum {
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}
.acs__statLabel { font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); }
.acs__statSub   { font-size: var(--fs-2xs); color: var(--text-tertiary); }

/* ── Filter bar ─────────────────────────────────────────────────────────── */
.acs__filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-3);
}
.acs__filters .input, .acs__filters select { height: 36px; }
.acs__search { flex: 1 1 260px; min-width: 200px; }
.acs__filters > .btn { margin-left: auto; }

/* ── Main + sidebar ─────────────────────────────────────────────────────── */
.acs__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 272px;
  gap: var(--s-3);
  align-items: start;
}
/* The sidebar repeats what the tabs and counters already say, so at narrow
   widths it is dropped BELOW rather than squeezed beside — and the table,
   which is the part you cannot get elsewhere, keeps the width. */
@media (max-width: 1180px) { .acs__layout { grid-template-columns: minmax(0, 1fr); } }

.acs__side { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.acs__card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.acs__cardHead {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-3) var(--s-2);
}
.acs__cardTitle { font-size: var(--fs-sm); font-weight: 700; color: var(--text-primary); }
.acs__cardAction {
  margin-left: auto; font-size: var(--fs-2xs); font-weight: 600;
  color: var(--text-link); background: none; border: 0; cursor: pointer; padding: 2px 4px;
  border-radius: var(--r-xs);
}
.acs__cardAction:hover { background: var(--bg-hover); }

/* ── Quick filters ──────────────────────────────────────────────────────── */
.acs__qf {
  display: flex; align-items: center; gap: var(--s-2);
  width: 100%;
  padding: 7px var(--s-3);
  background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--text-primary); text-align: left;
}
.acs__qf:hover { background: var(--bg-hover); }
.acs__qf[aria-pressed="true"] { background: var(--accent-quiet); font-weight: 600; }
.acs__qfIco { flex: 0 0 auto; width: 18px; display: grid; place-items: center; color: var(--text-tertiary); }
.acs__qf[data-tone="critical"] .acs__qfIco { color: var(--sev-critical); }
.acs__qf[data-tone="high"]     .acs__qfIco { color: var(--sev-high); }
.acs__qf[data-tone="info"]     .acs__qfIco { color: var(--sev-info); }
.acs__qfLabel { flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acs__qfCount { flex: 0 0 auto; font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
                color: var(--text-secondary); }

/* ── Recent activity ────────────────────────────────────────────────────── */
.acs__act { display: flex; align-items: baseline; gap: var(--s-2); padding: 5px var(--s-3); }
.acs__actDot { flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: var(--sev-info); }
.acs__act[data-tone="critical"] .acs__actDot { background: var(--sev-critical); }
.acs__act[data-tone="high"]     .acs__actDot { background: var(--sev-high); }
.acs__act[data-tone="medium"]   .acs__actDot { background: var(--sev-medium); }
.acs__act[data-tone="low"]      .acs__actDot { background: var(--sev-ok); }
.acs__actText { flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm); color: var(--text-primary);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acs__actWhen { flex: 0 0 auto; font-size: var(--fs-2xs); color: var(--text-tertiary); }

/* ── Health / preferences rows ──────────────────────────────────────────── */
.acs__row {
  display: flex; align-items: center; gap: var(--s-3);
  width: 100%; padding: var(--s-3);
  background: none; border: 0; cursor: pointer; font: inherit; color: inherit; text-align: left;
}
.acs__row:hover { background: var(--bg-hover); }
.acs__rowIco { flex: 0 0 auto; width: 32px; height: 32px; display: grid; place-items: center;
               border-radius: 50%; background: var(--sev-ok-bg); color: var(--sev-ok-tx); }
.acs__row[data-tone="info"] .acs__rowIco { background: var(--sev-info-bg); color: var(--sev-info-tx); }
.acs__rowBody { flex: 1 1 auto; min-width: 0; }
.acs__rowTitle { font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); }
.acs__rowSub { font-size: var(--fs-2xs); color: var(--text-tertiary); }
.acs__rowChev { flex: 0 0 auto; color: var(--text-tertiary); }

/* ── Table cells ────────────────────────────────────────────────────────── */
/* The Alert cell: tinted square, title, one line of detail. */
.acs__alert { display: flex; align-items: flex-start; gap: var(--s-2); min-width: 0; }
.acs__alertIco {
  flex: 0 0 auto; width: 30px; height: 30px; display: grid; place-items: center;
  border-radius: 8px; background: var(--sev-info-bg); color: var(--sev-info-tx);
}
.acs__alert[data-sev="CRITICAL"] .acs__alertIco { background: var(--sev-critical-bg); color: var(--sev-critical-tx); }
.acs__alert[data-sev="HIGH"]     .acs__alertIco { background: var(--sev-high-bg);     color: var(--sev-high-tx); }
.acs__alert[data-sev="MEDIUM"]   .acs__alertIco { background: var(--sev-medium-bg);   color: var(--sev-medium-tx); }
.acs__alert[data-sev="LOW"]      .acs__alertIco { background: var(--sev-ok-bg);       color: var(--sev-ok-tx); }
.acs__alertBody { min-width: 0; }
.acs__alertTitle { font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); }
.acs__alertSub {
  font-size: var(--fs-2xs); color: var(--text-tertiary);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
/* The actor cell: avatar, name, role underneath. */
.acs__who { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
.acs__whoBody { min-width: 0; }
.acs__whoName { font-size: var(--fs-sm); color: var(--text-primary);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acs__whoSub  { font-size: var(--fs-2xs); color: var(--text-tertiary);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Time: relative above, absolute beneath, as the reference shows it. */
.acs__when { line-height: 1.25; }
.acs__whenRel { font-size: var(--fs-sm); color: var(--text-primary); }
.acs__whenAbs { font-size: var(--fs-2xs); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }

/* A scope note, shown only when the console is NOT organisation-wide, so an
   Editor is told why their list is short instead of guessing. */
.acs__scope {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); margin-bottom: var(--s-3);
  background: var(--sev-info-bg); color: var(--sev-info-tx);
  border-radius: var(--r-md); font-size: var(--fs-sm);
}

/* The console's tab row carries every cross-cutting view PLUS one tab per live
   catalogue category — fourteen of them here, and more whenever the newsroom
   defines a category. The reference draws a single clean row, so this scrolls
   sideways rather than wrapping into a ragged block that pushes the table down.
   Scoped to .acs: every other saved-view row in the console has a handful of
   entries and wraps perfectly well. */
.acs .savedviews {
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  /* Room for the scrollbar so it never sits on top of the last tab. */
  padding-bottom: 2px;
}
.acs .savedviews .savedview { flex: 0 0 auto; white-space: nowrap; }

/* The Alert column carries the only text you cannot get from another column,
   so it gets the room. Two lines for the title, one for the detail — enough to
   identify the alert, bounded so one long title cannot set the row height for
   the whole table. */
.acs__alertTitle {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.acs .tablewrap table { table-layout: auto; }

/* ── Arrangement: hierarchy inside the table card ───────────────────────────
   The toolbar stacked the tab row, the search box and three dropdowns on one
   line, which read as a single undifferentiated strip of controls. The
   reference separates them: tabs are NAVIGATION (which slice of the data), the
   row beneath is REFINEMENT (within that slice). Giving the tabs their own
   full-width line with a rule under it makes that difference visible instead of
   leaving the reader to infer it. */
.acs .toolbar { flex-wrap: wrap; row-gap: var(--s-2); padding-bottom: var(--s-2); }
.acs .savedviews {
  flex: 1 0 100%;
  border-bottom: 1px solid var(--border-subtle);
  padding-bottom: var(--s-2);
  margin-bottom: 2px;
}

/* Counters keep a common height so the row reads as one object rather than
   five cards of different sizes — the sub-label wraps to two lines on some and
   one on others, and without this the whole row goes ragged. */
.acs__stat { min-height: 76px; align-items: center; }
.acs__statSub { line-height: 1.3; }

/* The quick-filter list grows with the catalogue — fourteen entries today, more
   whenever a category is added — and would otherwise push Recent Activity,
   System Health and Preferences off the bottom of the page entirely. Capped and
   scrolled, so everything below it stays reachable. */
.acs__qfScroll { max-height: 320px; overflow-y: auto; scrollbar-width: thin; }

/* Premium detail, not decoration: the table and the side cards sit on the same
   surface with the same border and radius as every other card in the console,
   so the screen reads as one composition rather than a table with widgets
   beside it. */
.acs .tablewrap {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.acs .tablewrap .toolbar { padding-left: var(--s-3); padding-right: var(--s-3); }
.acs .tablewrap .tablefoot { padding-left: var(--s-3); padding-right: var(--s-3); }
/* Row height with room to breathe: two lines of title plus a line of detail is
   the tallest cell, and a row that clips it is worse than a taller table. */
.acs .tablewrap td { padding-top: 10px; padding-bottom: 10px; vertical-align: middle; }

/* ── Fixes found by looking at the rendered screen ──────────────────────────
   1. The actor cell printed "SystemAutomated" as one word. Name and role are
      SPANS, and spans are inline, so the two lines the design shows ran
      together. They stack because they are told to, not by accident. */
.acs__whoName, .acs__whoSub { display: block; }

/* 2. The Alert column was being crushed to ~95px and truncating mid-word while
      six fixed columns held their size. With `table-layout: auto` a declared
      width is only a SUGGESTION — a cell whose content is wider wins — so the
      columns with long values quietly took the space the message needed.
      `fixed` makes the declared widths binding, and the Alert column, which is
      the only one carrying text you cannot read anywhere else, gets the rest. */
.acs .tablewrap table { table-layout: fixed; width: 100%; }
.acs .tablewrap th, .acs .tablewrap td { overflow: hidden; }

/* 3. The timestamp broke across three lines. The reference shows two: relative
      above, absolute below, each on its own line. */
.acs__whenAbs { white-space: nowrap; }
.acs__whenRel { white-space: nowrap; }

/* 4. Side-by-side ONLY when there is genuinely room for it. The reference was
      drawn on a wide screen; below that the sidebar was stealing the width the
      table needed and the result was seven columns fighting over 880px. The
      sidebar repeats what the counters and tabs already say, so it goes BELOW
      and the table — the part you cannot get elsewhere — keeps the full width. */
@media (max-width: 1620px) {
  .acs__layout { grid-template-columns: minmax(0, 1fr); }
  /* Below the table it is a row of cards, not a column down one side. */
  .acs__side { flex-direction: row; flex-wrap: wrap; }
  .acs__side > * { flex: 1 1 260px; }
  .acs__qfScroll { max-height: 260px; }
}

/* The same inline-span trap as the actor cell, in the sidebar rows: title and
   sub-line are spans, so "System Health" and "Last 7 days" rendered as one
   run-on string. Fixed once here rather than per card. */
.acs__rowTitle, .acs__rowSub { display: block; }
.acs__rowSub { margin-top: 1px; }

/* Cells clip rather than wrap now that the table layout is fixed, so anything
   that can outgrow its column has to degrade gracefully instead of being cut
   mid-word. A tag keeps its shape and loses characters from the end; the plain
   text cells do the same. */
.acs .tablewrap td .tag {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.acs .tablewrap td { text-overflow: ellipsis; }
