@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&family=Space+Mono:wght@400;700&display=swap');
:root{
  --navy:#14273F; --navy-700:#12325a; --navy-600:#1b477d;
  --brand:#E63B34; --brand-600:#B62A24; --brand-tint:#fbe9ea;
  /* --faint darkened 2026-08-26 (#7a8aa0 -> #5f7085): the old value was 3.3:1 on --bg,
     failing WCAG AA for the small (.72-.78rem) de-emphasized text it is used for. It now
     measures ~4.7:1 on --bg / ~5.1:1 on --card while staying clearly lighter than --muted
     (~6.0:1), so the visual hierarchy is unchanged. */
  --bg:#f4f7fa; --card:#ffffff; --ink:#0a1420; --muted:#4a607b; --faint:#5f7085;
  --border:#dbe1e9; --border-2:#eaeef3;
  --ok:#1c7c54; --ok-tint:#e6f5ee; --warn:#8a5a00; --warn-tint:#fbf1dd;
  --ok-rgb:28,124,84; --warn-rgb:138,90,0;
  --info:#1b477d; --info-tint:#e9f0f8;
  /* EU-872 Phase 5: radii bumped from AdminKit's tight .25/.2/.1 to a softer system;
     shadows deepened from .05 alpha to .08/.07/.08 so cards lift off --bg on both themes;
     --ease is the single transition curve for interactive feedback. */
  --radius:.5rem; --radius-sm:.375rem; --radius-xs:.25rem; --radius-pill:999px;
  --shadow-sm:0 .125rem .375rem rgba(11,36,64,.08);
  --shadow:0 .125rem 1rem rgba(11,36,64,.07);
  --shadow-lg:0 .5rem 2.5rem rgba(11,36,64,.08);
  --ring:0 0 0 3px rgba(230,59,52,.2);
  --ease:.2s cubic-bezier(.4,0,.2,1);
  /* RGB triplets of the brand hexes so tint rgba() stays token-driven (flipped in dark). */
  --brand-rgb:230,59,52; --navy-rgb:20,39,63; --ink-rgb:10,20,32;
  /* The brand GROUND. Ironclad Navy in both themes: the sidebar is a dark surface by
     design (identity sheet 03, reversed / on-color), so it does not need lightening for
     dark mode - and lightening it turned the rail mid-blue, off-brand. --navy keeps its
     second job as a FOREGROUND colour, which dark mode legitimately does lighten. */
  --rail:#14273F;
  /* Card-title color: the muted gray-blue at reduced emphasis (AdminKit's lightened gray-600).
     Darkened 2026-08-26 (#677d94 -> #5e7387): card titles render at .925rem/600, which is
     NOT WCAG "large text" (that needs 18.66px bold), and the old value was 4.25:1 on --card.
     Now ~4.9:1 on --card and ~4.6:1 on --bg - still a shade lighter than --faint, so the
     title/body emphasis order is preserved. */
  --card-title:#5e7387;
  --eu-side-w:264px;
  --eu-side-w-mini:72px;
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:var(--bg);color:var(--ink);
  -webkit-font-smoothing:antialiased;line-height:1.5;}
h1,h2,h3,.font-display{font-family:'Archivo Black','Space Grotesk','Inter',sans-serif;letter-spacing:-.01em;font-weight:400;}
/* Identity sheet 07: Space Mono is the SUPPORT face - labels, specs, captions and
   technical detail. Deliberately NOT the UI body face; the sheet defines a display
   and a support role, not a body role, and setting a data-dense app in monospace
   would cost far more legibility than it buys. Inter keeps the body. The support
   face is applied by the single .eu-label rule in the forms section below. */
a{color:inherit;text-decoration:none;}
.eu-shell{max-width:1140px;margin:0 auto;padding:1.5rem 1.25rem 3rem;}
/* AdminKit content rhythm at desktop ($content-padding-desktop: 3rem 3rem 1.5rem). */
@media (min-width:992px){.eu-shell{padding:3rem 3rem 1.5rem;}}
/* Reading-width pages (a single feed or list column) keep a narrower measure. Use this
   instead of a page-local .wrap with its own max-width (EU-423;
   tests/test_no_local_wrap.py ratchets the old pattern). */
.eu-shell.eu-shell--narrow{max-width:860px;}
/* AdminKit navbar: soft ambient shadow ($navbar-box-shadow 0 0 2rem 0 rgba($dark,.1)) carries
   the separation instead of a hard bottom border. */
.eu-topbar{position:sticky;top:0;z-index:40;background:rgba(255,255,255,.86);backdrop-filter:saturate(150%) blur(8px);box-shadow:0 0 2rem 0 rgba(11,36,64,.1);}
.eu-topbar-in{max-width:1140px;margin:0 auto;padding:.875rem 1.375rem;display:flex;align-items:center;gap:1rem;}
.eu-nav{display:flex;gap:.15rem;margin-left:auto;align-items:center;flex-wrap:wrap;}
/* Harden the topbar account-switcher <select> against pages (e.g. Settings) whose own forms
   set a broad `select{width:100%}`. Without this, that rule stretches the switcher to the full
   nav width and the flex-wrap above breaks the whole nav onto multiple rows. Scope to .eu-nav
   (topbar-only) so page form selects are unaffected. */
.eu-nav select{width:auto;}
.eu-nav a{padding:.45rem .8rem;border-radius:var(--radius);font-size:.85rem;font-weight:500;color:var(--muted);transition:var(--ease);}
.eu-nav a:hover{background:var(--border-2);color:var(--ink);}
/* ---------- Topbar global search (plain GET form to /auth/search - no JS) ---------- */
.eu-search{display:inline-flex;align-items:center;position:relative;}
.eu-search-ico{position:absolute;left:.55rem;top:50%;transform:translateY(-50%);
  width:14px;height:14px;color:var(--faint);pointer-events:none;}
.eu-search-in{font-family:inherit;font-size:.78rem;color:var(--ink);background:var(--card);
  border:1px solid var(--border);border-radius:var(--radius);padding:.32rem .6rem .32rem 1.75rem;
  width:150px;line-height:1;transition:border-color var(--ease),box-shadow var(--ease),width var(--ease);}
.eu-search-in:hover{border-color:var(--navy);}
.eu-search-in:focus{outline:none;box-shadow:var(--ring);width:210px;}
@media (max-width:700px){.eu-search-in{width:104px;}.eu-search-in:focus{width:136px;}}
/* Topbar user avatar - shared so it renders identically on every authenticated page
   (previously each page redefined it, and Activity/History defined it nowhere, so the
   shared topbar's avatar was unstyled there). */
.avatar{width:34px;height:34px;border-radius:50%;background:var(--navy);display:flex;align-items:center;justify-content:center;font-size:.8rem;font-weight:700;color:#fff;flex-shrink:0;}
/* ---------- Topbar notifications bell (AdminKit .indicator + dropdown-menu-lg pattern,
   re-implemented on the token system; data comes from /auth/notifications/unread). ---------- */
.eu-bell-wrap{position:relative;display:inline-flex;}
.eu-bell{background:none;border:0;cursor:pointer;color:var(--muted);padding:.4rem;
  border-radius:var(--radius);position:relative;display:inline-flex;}
.eu-bell:hover{background:var(--border-2);color:var(--ink);}
.eu-bell svg{width:20px;height:20px;display:block;}
.eu-bell-badge{position:absolute;top:0;right:-2px;min-width:16px;height:16px;padding:0 3px;
  border-radius:var(--radius-pill);background:var(--brand);color:#fff;font-size:.62rem;
  font-weight:700;line-height:16px;text-align:center;transition:top .1s ease-out;}
.eu-bell-wrap:hover .eu-bell-badge{top:-3px;}
.eu-bell-menu{position:absolute;right:0;top:calc(100% + .45rem);width:320px;max-width:88vw;
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow-lg);z-index:90;overflow:hidden;
  animation:euBellIn .18s ease forwards;}
@keyframes euBellIn{from{opacity:0;transform:translateY(-8px);}to{opacity:1;transform:none;}}
.eu-bell-head{padding:.6rem .8rem;text-align:center;font-weight:700;font-size:.8rem;
  color:var(--ink);border-bottom:1px solid var(--border);}
.eu-bell-list{max-height:340px;overflow-y:auto;}
.eu-bell-item{display:flex;gap:.6rem;align-items:flex-start;width:100%;text-align:left;
  background:none;border:0;border-bottom:1px solid var(--border-2);cursor:pointer;
  padding:.6rem .8rem;font:inherit;color:var(--ink);}
.eu-bell-item:hover{background:var(--border-2);}
.eu-bell-dot{width:9px;height:9px;border-radius:50%;background:var(--info);flex-shrink:0;
  margin-top:.35rem;}
.eu-bell-dot.sev-danger{background:var(--brand);}
.eu-bell-dot.sev-success{background:var(--ok);}
.eu-bell-title{font-size:.82rem;font-weight:600;}
.eu-bell-body{font-size:.76rem;color:var(--muted);margin-top:.1rem;}
.eu-bell-time{font-size:.72rem;color:var(--faint);margin-top:.15rem;}
.eu-bell-empty{padding:1rem .8rem;text-align:center;font-size:.8rem;color:var(--faint);}
.eu-bell-foot{padding:.5rem;text-align:center;border-top:1px solid var(--border);}
.eu-bell-foot a{font-size:.78rem;color:var(--muted);}
.eu-bell-foot a:hover{color:var(--ink);}
/* ---------- Topbar task panel (cross-cutting slide-over, EU-871) ---------- */
.eu-task-wrap{position:relative;display:inline-flex;}
.eu-task-btn{background:none;border:0;cursor:pointer;color:var(--muted);padding:.4rem;
  border-radius:8px;position:relative;display:inline-flex;}
.eu-task-btn:hover{background:var(--border-2);color:var(--ink);}
.eu-task-btn svg{width:20px;height:20px;display:block;}
.eu-task-badge{position:absolute;top:0;right:-2px;min-width:16px;height:16px;padding:0 3px;
  border-radius:var(--radius-pill);background:var(--brand);color:#fff;font-size:.62rem;
  font-weight:700;line-height:16px;text-align:center;transition:top .1s ease-out;}
.eu-task-wrap:hover .eu-task-badge{top:-3px;}
.eu-task-menu{position:absolute;right:0;top:calc(100% + .45rem);width:360px;max-width:92vw;
  background:var(--card);border:1px solid var(--border);border-radius:8px;
  box-shadow:var(--shadow-lg);z-index:90;overflow:hidden;
  animation:euBellIn .18s ease forwards;}
.eu-task-head{padding:.6rem .8rem;display:flex;align-items:center;justify-content:space-between;
  border-bottom:1px solid var(--border);}
.eu-task-head-title{font-weight:700;font-size:.8rem;color:var(--ink);}
.eu-task-head-add{background:none;border:0;cursor:pointer;color:var(--muted);font-size:.78rem;
  font-weight:600;padding:.2rem .5rem;border-radius:6px;display:inline-flex;align-items:center;gap:.3rem;}
.eu-task-head-add:hover{background:var(--border-2);color:var(--ink);}
.eu-task-list{max-height:380px;overflow-y:auto;padding:.25rem 0;}
.eu-task-item{display:flex;gap:.5rem;align-items:flex-start;width:100%;text-align:left;
  background:none;border:0;border-bottom:1px solid var(--border-2);cursor:pointer;
  padding:.5rem .8rem;font:inherit;color:var(--ink);}
.eu-task-item:last-child{border-bottom:0;}
.eu-task-item:hover{background:var(--border-2);}
.eu-task-check{width:16px;height:16px;border:2px solid var(--border);border-radius:4px;
  flex-shrink:0;margin-top:.15rem;cursor:pointer;display:flex;align-items:center;justify-content:center;
  background:var(--card);transition:border-color .15s,background .15s;}
.eu-task-check:hover{border-color:var(--ok);}
.eu-task-check.done{background:var(--ok);border-color:var(--ok);}
.eu-task-check.done::after{content:'';width:6px;height:3px;border-left:2px solid #fff;
  border-bottom:2px solid #fff;transform:rotate(-45deg);display:block;}
.eu-task-info{flex:1;min-width:0;}
.eu-task-name{font-size:.82rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.eu-task-meta{font-size:.72rem;color:var(--faint);margin-top:.1rem;}
.eu-task-meta .overdue{color:var(--brand-600);font-weight:600;}
.eu-task-empty{padding:1rem .8rem;text-align:center;font-size:.8rem;color:var(--faint);}
.eu-task-foot{padding:.5rem;text-align:center;border-top:1px solid var(--border);}
.eu-task-foot a{font-size:.78rem;color:var(--muted);}
.eu-task-foot a:hover{color:var(--ink);}
.eu-task-new{display:flex;gap:.4rem;padding:.5rem .8rem;border-top:1px solid var(--border);}
.eu-task-new-in{flex:1;font-family:inherit;font-size:.8rem;color:var(--ink);background:var(--bg);
  border:1px solid var(--border);border-radius:6px;padding:.35rem .6rem;}
.eu-task-new-in:focus{outline:none;border-color:var(--navy-600);box-shadow:var(--ring);}
.eu-task-new-btn{background:var(--navy);color:#fff;border:0;border-radius:6px;padding:.35rem .6rem;
  font-size:.78rem;font-weight:600;cursor:pointer;white-space:nowrap;}
.eu-task-new-btn:hover{background:var(--navy-700);}
/* ---------- Topbar account (avatar) menu ----------
   Same interaction contract as the bell above (button + role="menu" + hidden
   toggle + outside-click/Escape close) so the topbar has ONE dropdown pattern,
   not two. The avatar itself became a real control: it used to be a decorative
   <div aria-hidden="true">, which is exactly what you must not do to something
   a user is meant to operate. The plain "Sign out" link beside it is KEPT so
   signing out never depends on JavaScript. */
.eu-acct-wrap{position:relative;display:inline-flex;}
.eu-acct-btn{background:none;border:0;padding:0;cursor:pointer;display:inline-flex;
  border-radius:50%;line-height:0;}
.eu-acct-btn:hover .avatar{background:var(--navy-600,var(--navy));}
.eu-acct-menu{position:absolute;right:0;top:calc(100% + .45rem);width:230px;max-width:88vw;
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow-lg);z-index:90;overflow:hidden;
  animation:euBellIn .18s ease forwards;}
.eu-acct-head{padding:.6rem .8rem;border-bottom:1px solid var(--border);
  font-size:.72rem;color:var(--faint);}
.eu-acct-head strong{display:block;font-size:.82rem;color:var(--ink);font-weight:600;
  word-break:break-all;margin-top:.1rem;}
.eu-acct-item{display:block;padding:.55rem .8rem;font-size:.82rem;color:var(--ink);
  text-decoration:none;border-bottom:1px solid var(--border-2);}
.eu-acct-item:last-child{border-bottom:0;}
.eu-acct-item:hover{background:var(--border-2);}
/* ---------- AdminKit-pattern left sidebar (EU-192) ----------
   Fixed 260px rail, dark in BOTH themes (its palette is self-contained navy, so
   no token flip needed); content shifts right via body margin at >900px and the
   rail goes off-canvas with a scrim + hamburger below. Collapse is native
   <details>; the chevron is a pure-CSS rotate on [open]. */
body{margin-left:var(--eu-side-w);}
.eu-sidebar{position:fixed;top:0;bottom:0;left:0;width:var(--eu-side-w);z-index:60;
  background:var(--rail);color:#e9f0f8;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;}
.eu-side-brand{display:flex;align-items:center;gap:.55rem;padding:1.15rem 1.5rem;padding-right:3rem;
  font-family:'Space Grotesk';font-size:1.15rem;font-weight:600;color:#fff;flex-shrink:0;}
.eu-side-brand img{height:26px;width:auto;display:block;}
/* Identity sheet 08: clearspace = the mark's cap height on all sides. The mark is 26px
   here, so 26px is the floor for padding around the lockup. */
.eu-side-brand .eu-mark{flex-shrink:0;color:#fff;}
.eu-side-word{font-family:'Archivo Black','Space Grotesk',sans-serif;font-size:.95rem;
  line-height:1.05;color:#fff;letter-spacing:-.01em;text-transform:uppercase;}
:root.eu-side-mini .eu-side-word{display:none;}
.eu-side-brand small{color:rgba(233,240,248,.55);font-weight:600;font-size:.7rem;
  font-family:'Inter';border-left:1px solid rgba(233,240,248,.25);padding-left:.55rem;}
.eu-side-nav{padding:.35rem 0 2rem;}
/* AdminKit 3.5 section header: plain case (no uppercase/letter-spacing), .75rem, low-emphasis
   rail text ($sidebar-header-padding 1.5rem 1.5rem .375rem). */
.eu-side-header{padding:1.5rem 1.5rem .375rem;font-size:.75rem;font-weight:400;
  color:rgba(233,240,248,.38);}
/* Link metrics: rounded-rect active highlight (EU-872 Phase 5) replaces the 3px left
   border with a soft pill that sits inside the rail padding. */
.eu-side-link{display:flex;align-items:center;gap:.75rem;padding:.625rem 1.625rem;
  margin:0 .625rem;border-radius:var(--radius);
  color:rgba(233,240,248,.72);font-size:.875rem;font-weight:400;
  cursor:pointer;transition:background var(--ease),color var(--ease);}
.eu-side-link:hover{color:#fff;background:rgba(255,255,255,.06);}
.eu-side-link.active{color:#fff;
  background:linear-gradient(90deg,rgba(var(--brand-rgb),.14) 0%,rgba(var(--brand-rgb),.06) 100%);
  box-shadow:inset 3px 0 0 var(--brand);}
.eu-side-ico{width:18px;height:18px;flex-shrink:0;opacity:.85;}
summary.eu-side-link{list-style:none;user-select:none;position:relative;padding-right:2.6rem;}
summary.eu-side-link::-webkit-details-marker{display:none;}
/* AdminKit chevron: .075rem borders, absolute right 1.5rem, 45deg -> -135deg with a top
   nudge, .2s ease-out. */
summary.eu-side-link::after{content:'';border:solid currentColor;
  border-width:0 .075rem .075rem 0;display:inline-block;padding:2px;
  position:absolute;right:1.5rem;top:1rem;transform:rotate(45deg);
  transition:all .2s ease-out;opacity:.55;}
.eu-side-group[open]>summary.eu-side-link::after{transform:rotate(-135deg);top:1.2rem;}
.eu-side-children .eu-side-link{padding:.625rem 1rem .625rem 2.5rem;font-size:90%;}
/* AdminKit child marker: an arrow pulled left of the label that nudges right on hover. */
.eu-side-children .eu-side-link::before{content:'\2192';display:inline-block;
  position:relative;left:-14px;margin-right:-.75rem;transition:transform .1s ease;}
.eu-side-children .eu-side-link:hover::before{transform:translateX(4px);}
.eu-side-scrim{display:none;position:fixed;inset:0;z-index:55;background:rgba(11,36,64,.5);}
.eu-side-toggle{display:none;background:none;border:0;color:#e9f0f8;cursor:pointer;
  padding:.3rem;border-radius:var(--radius);position:absolute;top:1.15rem;right:.75rem;z-index:2;}
.eu-side-toggle:hover{background:rgba(255,255,255,.1);color:#fff;}
.eu-side-toggle svg{width:22px;height:22px;display:block;}
@media (max-width:900px){
  body{margin-left:0;}
  .eu-sidebar{transform:translateX(-100%);transition:transform .25s ease;z-index:70;
    box-shadow:var(--shadow-lg);}
  body.eu-side-open .eu-sidebar{transform:none;}
  body.eu-side-open .eu-side-scrim{display:block;}
  .eu-side-toggle{display:inline-flex;position:fixed;top:.6rem;left:.6rem;z-index:71;
    color:var(--muted);background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);}
  .eu-side-toggle:hover{background:var(--border-2);color:var(--ink);}
  body.eu-side-open .eu-side-toggle{position:absolute;top:1.15rem;right:.75rem;left:auto;
    color:#e9f0f8;background:none;box-shadow:none;}
  body.eu-side-open .eu-side-toggle:hover{background:rgba(255,255,255,.1);color:#fff;}
}
/* ---------- Desktop mini rail (icon-only collapse; AdminKit "compact" pattern) ----------
   Keyed on the ROOT element (html.eu-side-mini), not body, so the pre-paint script in
   theme_head can stamp it before first paint - no width flash on load. The mobile
   off-canvas above is untouched (body.eu-side-open, <=900px only). Flyouts are CSS-only
   with a 16px invisible hover bridge (the AdminKit trick) so the hover state survives
   cursor travel across the gap; the groups are forced [open] by euSbToggle in mini mode
   because a closed <details> does not render its children at all. */
@media (min-width:901px){
  .eu-side-toggle{display:inline-flex;}
  :root.eu-side-mini body{margin-left:var(--eu-side-w-mini);}
  :root.eu-side-mini .eu-sidebar{width:var(--eu-side-w-mini);overflow:visible;}
  :root.eu-side-mini .eu-side-brand{justify-content:center;padding:1.15rem .4rem .6rem;}
  :root.eu-side-mini .eu-side-toggle{position:static;display:flex;justify-content:center;
    padding:.35rem 0;margin:0 auto .25rem;}
  :root.eu-side-mini .eu-side-brand small{display:none;}
  :root.eu-side-mini .eu-side-header{display:none;}
  :root.eu-side-mini .eu-side-nav>.eu-side-link>span,
  :root.eu-side-mini summary.eu-side-link>span{display:none;}
  :root.eu-side-mini .eu-side-nav>.eu-side-link,
  :root.eu-side-mini summary.eu-side-link{justify-content:center;padding:.75rem 0;}
  :root.eu-side-mini summary.eu-side-link{padding-right:0;}
  :root.eu-side-mini summary.eu-side-link::after{display:none;}
  :root.eu-side-mini .eu-side-group{position:relative;}
  :root.eu-side-mini .eu-side-children{display:none;position:absolute;left:100%;top:0;
    width:220px;background:var(--rail);border-radius:0 var(--radius) var(--radius) var(--radius);
    box-shadow:var(--shadow-lg);z-index:80;padding:.35rem 0;}
  :root.eu-side-mini .eu-side-children::before{content:'';position:absolute;left:-16px;top:0;
    width:16px;height:100%;}
  :root.eu-side-mini .eu-side-group:hover>.eu-side-children{display:block;}
  :root.eu-side-mini .eu-side-children .eu-side-link{justify-content:flex-start;
    padding:.55rem 1rem .55rem 1.6rem;}
}
/* ---------- Workspace switcher (EU-353 Phase 5) ----------
   Between brand area and nav list in the sidebar. A <details> dropdown listing
   the user's workspaces; the active one shows as label + kind badge. In mini-rail
   mode it collapses to a single-character tile with a flyout. */
.eu-ws-switch{padding:.35rem 1rem .5rem;border-bottom:1px solid rgba(233,240,248,.1);flex-shrink:0;}
.eu-ws-switch>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.55rem;
  padding:.45rem .55rem;border-radius:var(--radius);color:rgba(233,240,248,.82);
  font-size:.8rem;font-weight:500;user-select:none;transition:background var(--ease);}
.eu-ws-switch>summary::-webkit-details-marker{display:none;}
.eu-ws-switch>summary:hover{background:rgba(255,255,255,.06);}
.eu-ws-tile{width:28px;height:28px;border-radius:6px;display:flex;align-items:center;
  justify-content:center;font-weight:700;font-size:.75rem;flex-shrink:0;color:#fff;
  background:rgba(255,255,255,.12);}
.eu-ws-tile[data-kind="personal"]{background:rgba(92,160,234,.25);}
.eu-ws-tile[data-kind="business"]{background:rgba(230,59,52,.2);}
.eu-ws-name{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.eu-ws-kind{font-size:.6rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  padding:.1rem .4rem;border-radius:var(--radius-pill);line-height:1.4;}
.eu-ws-kind[data-kind="personal"]{background:rgba(92,160,234,.18);color:#7cb8e8;}
.eu-ws-kind[data-kind="business"]{background:rgba(230,59,52,.15);color:#f08b87;}
.eu-ws-switch>summary::after{content:'';border:solid currentColor;border-width:0 1.5px 1.5px 0;
  display:inline-block;padding:2px;transform:rotate(45deg);opacity:.45;margin-left:auto;
  transition:transform .2s ease-out;flex-shrink:0;}
.eu-ws-switch[open]>summary::after{transform:rotate(-135deg);}
.eu-ws-list{padding:.3rem 0;margin-top:.25rem;}
.eu-ws-item{display:flex;align-items:center;gap:.5rem;padding:.4rem .55rem;border-radius:var(--radius);
  width:100%;background:none;border:0;color:rgba(233,240,248,.72);font-size:.78rem;cursor:pointer;
  transition:background .1s,color .1s;font-family:inherit;text-align:left;}
.eu-ws-item:hover{background:rgba(255,255,255,.06);color:#fff;}
.eu-ws-item.active{color:#fff;font-weight:600;}
.eu-ws-item .eu-ws-tile{width:24px;height:24px;font-size:.65rem;}
.eu-ws-add{color:rgba(233,240,248,.45);font-size:.72rem;padding:.35rem .55rem;display:flex;
  align-items:center;gap:.4rem;border:0;background:none;width:100%;cursor:pointer;
  font-family:inherit;border-radius:var(--radius);}
.eu-ws-add:hover{color:rgba(233,240,248,.72);background:rgba(255,255,255,.04);}
.eu-ws-add svg{width:14px;height:14px;}
/* Mini-rail: collapse to tile only, flyout on hover. */
@media (min-width:901px){
  :root.eu-side-mini .eu-ws-switch{padding:.35rem .55rem .35rem;border-bottom:none;position:relative;}
  :root.eu-side-mini .eu-ws-switch>summary{justify-content:center;padding:.3rem;}
  :root.eu-side-mini .eu-ws-name,:root.eu-side-mini .eu-ws-kind,
  :root.eu-side-mini .eu-ws-switch>summary::after{display:none;}
  :root.eu-side-mini .eu-ws-list{display:none;position:absolute;left:100%;top:0;
    width:220px;background:var(--rail);border-radius:0 var(--radius) var(--radius) var(--radius);
    box-shadow:var(--shadow-lg);z-index:80;padding:.45rem 0;}
  :root.eu-side-mini .eu-ws-list::before{content:'';position:absolute;left:-16px;top:0;
    width:16px;height:100%;}
  :root.eu-side-mini .eu-ws-switch:hover .eu-ws-list{display:block;}
}
/* Section sub-nav (pill row) - the SiteRekon performance_nav pattern, for pages
   with their own subsections. */
.eu-subnav{display:flex;gap:.4rem;flex-wrap:wrap;margin-bottom:1.1rem;}
.eu-subnav a{padding:.4rem .9rem;border-radius:var(--radius-pill);border:1px solid var(--border);
  font-size:.82rem;font-weight:600;color:var(--muted);background:var(--card);transition:var(--ease);}
.eu-subnav a:hover{color:var(--ink);border-color:var(--faint);}
.eu-subnav a.active{background:var(--navy);border-color:var(--navy);color:#fff;}
/* Leading "back to the parent list" link. Borderless so it reads as an escape hatch
   rather than as one more sibling section. */
.eu-subnav a.eu-subnav-back{border-color:transparent;background:none;padding-left:0;
  padding-right:.6rem;color:var(--faint);}
.eu-subnav a.eu-subnav-back:hover{color:var(--ink);border-color:transparent;}
.eu-subnav .eu-subnav-sep{width:1px;align-self:stretch;background:var(--border);margin:.15rem .35rem;}
/* Grouped-links menu that rides at the end of a sub-nav (downloads, exports). Native
   <details> so it needs no JS and closes on Esc; `margin-left:auto` pushes it right on a
   wide row and it simply wraps under on a phone. */
.eu-subnav-menu{position:relative;margin-left:auto;}
.eu-subnav-menu>summary{list-style:none;cursor:pointer;padding:.4rem .9rem;
  border-radius:var(--radius-pill);border:1px solid var(--border);font-size:.82rem;
  font-weight:600;color:var(--muted);background:var(--card);}
.eu-subnav-menu>summary::-webkit-details-marker{display:none;}
.eu-subnav-menu>summary:hover{color:var(--ink);border-color:var(--faint);}
.eu-subnav-menu[open]>summary{color:var(--ink);border-color:var(--faint);}
.eu-subnav-menu-pop{position:absolute;right:0;top:calc(100% + .35rem);z-index:30;
  min-width:210px;background:var(--card);border:1px solid var(--border);border-radius:10px;
  box-shadow:var(--shadow-lg);padding:.35rem 0;}
.eu-subnav-menu-grp{font-size:.66rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.4px;color:var(--faint);padding:.45rem .85rem .2rem;}
.eu-subnav-menu-pop a{display:block;padding:.35rem .85rem;font-size:.8rem;font-weight:500;
  color:var(--ink);border:0;border-radius:0;background:none;}
.eu-subnav-menu-pop a:hover{background:var(--border-2);color:var(--ink);}
/* ---------- Component kit (EU-201 Phase 3) - shared vocabulary ported natively from
   AdminKit/SiteRekon so product pages stop re-inventing these. ---------- */
/* Stat tile: THE one stat system (EU-423). Card with a label row, a tinted 40px icon
   circle, a big number, and an optional soft delta chip ("+12% since last week").
   Markup: .eu-stat-row > .eu-stat > (.eu-stat-top > .eu-stat-label + .eu-stat-ico) +
   .eu-stat-num. The values below are what every tile actually rendered before EU-423:
   an EU-198 block later in this sheet (.eu-stats / .eu-stat-value + a second .eu-stat
   and .eu-stat-label) won the cascade over this one, so the borderless shadow card and
   the uppercase faint label ARE the canonical look. That block is gone; its 4 consumers
   moved to this markup. Do not add a second stat rule set anywhere. */
.eu-stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;margin-bottom:1.2rem;}
.eu-stat{background:var(--card);border:0;border-radius:var(--radius);padding:1rem 1.15rem;box-shadow:var(--shadow);}
.eu-stat-top{display:flex;align-items:flex-start;justify-content:space-between;gap:.6rem;}
.eu-stat-label{font-size:.74rem;font-weight:600;color:var(--faint);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem;}
.eu-stat-ico{width:40px;height:40px;border-radius:50%;background:rgba(var(--brand-rgb),.12);color:var(--brand);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.eu-stat-ico svg{width:18px;height:18px;stroke-width:1.5;}
.eu-stat-ico.tint-navy{background:rgba(var(--navy-rgb),.12);color:var(--navy);}
.eu-stat-ico.tint-ok{background:var(--ok-tint);color:var(--ok);}
.eu-stat-num{font-size:1.6rem;font-weight:700;color:var(--ink);margin:.35rem 0 .5rem;font-family:'Space Grotesk';}
/* Soft badges (AdminKit .badge-{color}-light): rgba tint of the semantic color. */
.eu-badge-soft{display:inline-block;padding:.16rem .55rem;border-radius:var(--radius-pill);font-size:.72rem;font-weight:600;background:var(--border-2);color:var(--muted);}
.eu-badge-soft.ok{background:var(--ok-tint);color:var(--ok);}
.eu-badge-soft.warn{background:var(--warn-tint);color:var(--warn);}
/* Text on a tint uses --brand-600, not --brand: Break Red is a FILL colour and cannot
   reach WCAG AA at body size on a light ground (4.17:1 even on pure white). The mark
   and solid fills keep pure Break Red. */
.eu-badge-soft.danger{background:var(--brand-tint);color:var(--brand-600);}
.eu-badge-soft.info{background:var(--info-tint);color:var(--info);}
.eu-badge-soft.navy{background:rgba(var(--navy-rgb),.12);color:var(--navy);}
/* Small avatar-initials circle for member rows (the 34px topbar .avatar, sized down). */
.avatar-sm{width:28px;height:28px;border-radius:50%;background:var(--navy);display:inline-flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:700;color:#fff;flex-shrink:0;}
/* Native form validation (AdminKit needs-validation pattern, framework-free):
   add .eu-validate to a form + novalidate; invalid fields get the brand ring after the
   first submit attempt, and a sibling .eu-invalid-feedback shows. */
form.eu-validate .eu-invalid-feedback{display:none;font-size:.76rem;color:var(--brand);margin-top:.25rem;}
form.eu-validate.was-validated :invalid{border-color:var(--brand);box-shadow:var(--ring);}
form.eu-validate.was-validated :invalid~.eu-invalid-feedback{display:block;}
/* ---------- Persistent system status bar (under the topbar, every page) ---------- */
.eu-statusbar{background:var(--bg);border-bottom:1px solid var(--border);}
.eu-statusbar-in{max-width:1140px;margin:0 auto;padding:.3rem 1.25rem;display:flex;align-items:center;gap:.6rem;font-size:.76rem;color:var(--muted);white-space:nowrap;}
.eu-sb-seg{display:inline-flex;align-items:center;gap:.3rem;}
.eu-sb-seg b{color:var(--ink);font-weight:700;}
a.eu-sb-seg{text-decoration:none;color:inherit;}
a.eu-sb-seg:hover{color:var(--ink);text-decoration:underline;}
.eu-sb-sync{color:var(--faint);}
.eu-sb-sync.eu-sb-reconnect{color:var(--warn);font-weight:600;}
.eu-sb-spacer{margin-left:auto;}
.eu-sb-chip{display:inline-flex;align-items:center;gap:.3rem;font-size:.71rem;font-weight:700;padding:.12rem .55rem;border-radius:var(--radius-pill);letter-spacing:.02em;}
.eu-sb-chip[hidden]{display:none;}
.eu-sb-auto.on{background:var(--ok-tint);color:var(--ok);}
.eu-sb-auto.off{background:var(--border-2);color:var(--muted);}
.eu-sb-auto.locked{background:var(--warn-tint);color:var(--warn);}
.eu-sb-importing{background:var(--info-tint);color:var(--navy);}
.eu-sb-importing b{font-weight:800;}
/* Billing-attention chip: amber by default, brand-red for a high-severity (blocked /
   canceled) state. Hidden until the poll (or server render) says needs_attention. */
a.eu-sb-billing{background:var(--warn-tint);color:var(--warn);text-decoration:none;}
a.eu-sb-billing:hover{text-decoration:underline;}
a.eu-sb-billing.sev-high{background:var(--brand-tint);color:var(--brand-600);}
.eu-sb-spin{display:inline-block;animation:eu-sb-rot 1.1s linear infinite;}
@keyframes eu-sb-rot{to{transform:rotate(360deg);}}
/* ---------- Live batch-run banner (EU-196, directly under the status bar) ----------
   Hidden empty by default; the bundled script shows it only while /auth/runs/active
   reports queued/running runs. Tokens only (--info-tint/--navy/--border), so the
   dark theme's token flip restyles it with no extra rules. */
.eu-runbar{background:var(--info-tint);border-bottom:1px solid var(--border);}
.eu-runbar[hidden]{display:none;}
.eu-runbar-in{max-width:1140px;margin:0 auto;padding:.32rem 1.25rem;display:flex;align-items:center;gap:1.1rem;font-size:.76rem;color:var(--navy);flex-wrap:wrap;}
.eu-run{display:inline-flex;align-items:center;gap:.45rem;}
.eu-run-label{font-weight:600;white-space:nowrap;}
.eu-run-track{display:inline-block;width:120px;height:5px;border-radius:999px;background:var(--border);overflow:hidden;flex:0 0 auto;}
.eu-run-fill{display:block;height:100%;border-radius:999px;background:var(--navy);transition:width .4s;}
.eu-run-spin{display:inline-block;animation:eu-sb-rot 1.1s linear infinite;}
@media (max-width:640px){
  .eu-statusbar-in{padding:.3rem .9rem;gap:.45rem;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
  .eu-statusbar-in::-webkit-scrollbar{display:none;}
  .eu-sb-sync{display:none;}
  /* A reconnect warning is a problem, not a benign sub-state - keep it visible on mobile. */
  .eu-sb-sync.eu-sb-reconnect{display:inline;}
}
/* Button metrics: EU-872 Phase 5 adds subtle top-highlight gradients on primary/accent
   to lift them off the surface, and promotes --radius-sm + --ease as the shared tokens. */
.eu-btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;cursor:pointer;font-family:inherit;font-size:.875rem;font-weight:400;padding:.3rem .85rem;border-radius:var(--radius-sm);border:1px solid transparent;transition:color var(--ease),background-color var(--ease),border-color var(--ease),box-shadow var(--ease);text-decoration:none;white-space:nowrap;}
.eu-btn:focus-visible{outline:none;box-shadow:var(--ring);}
.eu-btn--primary{background:linear-gradient(180deg,rgba(255,255,255,.08) 0%,transparent 100%),var(--navy);color:#fff;font-weight:600;}
.eu-btn--primary:hover{background:linear-gradient(180deg,rgba(255,255,255,.12) 0%,transparent 100%),var(--navy-700);}
.eu-btn--accent{background:linear-gradient(180deg,rgba(255,255,255,.05) 0%,transparent 100%),var(--brand);color:#fff;font-weight:600;}
.eu-btn--accent:hover{background:linear-gradient(180deg,rgba(255,255,255,.08) 0%,transparent 100%),var(--brand-600);}
.eu-btn--ghost{background:var(--card);color:var(--ink);border-color:var(--border);}
.eu-btn--ghost:hover{border-color:var(--navy);color:var(--navy);}
.eu-btn--subtle{background:var(--info-tint);color:var(--navy);}
.eu-btn--subtle:hover{background:rgba(var(--navy-rgb),.18);}
.eu-btn--danger{background:var(--card);color:var(--brand-600);border-color:rgba(var(--brand-rgb),.35);}
.eu-btn--danger:hover{background:var(--brand-tint);}
.eu-btn--sm{padding:.2rem .5rem;font-size:.75rem;}
.eu-btn--block{width:100%;}
.eu-btn:disabled{opacity:.55;cursor:not-allowed;}
/* AdminKit card: no visible border - the soft ambient $card-shadow carries the elevation;
   $card-border-radius .25rem, $card-spacer 1.25rem. */
.eu-card{background:var(--card);border:0;border-radius:var(--radius);box-shadow:var(--shadow);}
.eu-card-pad{padding:1.25rem;}
/* Cards stacked directly in the content shell take AdminKit's 24px gutter. */
.eu-shell>.eu-card{margin-bottom:24px;}
/* AdminKit card title: .925rem / 600 / the muted gray-blue title token - plain case. */
.eu-h2{font-size:.925rem;font-weight:600;color:var(--card-title);margin-bottom:.7rem;}
/* (The EU-198 stat block that used to sit here was folded into the kit's stat tile
   above in EU-423 - one stat system.) */
/* AdminKit badge metrics (.3em/.45em, 80%, 600, $border-radius .2rem) on the existing
   tint-pair pattern (== AdminKit's badge-{color}-light). */
.eu-badge{display:inline-flex;align-items:center;gap:.3rem;font-size:80%;font-weight:600;padding:.3em .45em;border-radius:var(--radius-sm);background:var(--border-2);color:var(--muted);}
.eu-badge--navy{background:var(--info-tint);color:var(--navy);}
.eu-badge--accent{background:var(--brand-tint);color:var(--brand-600);}
.eu-badge--ok{background:var(--ok-tint);color:var(--ok);}
.eu-badge--warn{background:var(--warn-tint);color:var(--warn);}
/* AdminKit form metrics: label .5rem/500; input .3rem/.85rem, .875rem, radius .2rem;
   focus = navy-600-derived border + the brand focus ring.
   .eu-label is ONE rule: form labels render in Space Mono ON PURPOSE (owner decision
   2026-09-23, EU-415 - form labels are the identity sheet's "labels"). It used to be two
   rules (font here, layout far above) that read like an accident. */
.eu-label{display:block;font-size:.78rem;color:var(--muted);margin-bottom:.5rem;font-weight:500;
  font-family:'Space Mono','SFMono-Regular',ui-monospace,monospace;letter-spacing:.02em;}
.eu-input{width:100%;padding:.3rem .85rem;background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--ink);font-size:.875rem;font-family:inherit;transition:border-color var(--ease),box-shadow var(--ease);}
.eu-input:focus{outline:none;border-color:var(--navy-600);box-shadow:var(--ring);}
.eu-reclassify-select{padding:.28rem .5rem;background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--ink);font-size:.82rem;font-family:inherit;cursor:pointer;}
.eu-reclassify-select:focus{outline:none;border-color:var(--navy-600);box-shadow:var(--ring);}
/* AdminKit table: quiet plain-case header (weight 600, same size as cells), .75rem cell
   padding, ~4% ink row hover, first/last cells align to the 1.25rem card padding with no
   outer borders inside a card. */
.eu-table{width:100%;border-collapse:collapse;}
.eu-table th{font-size:.83rem;color:var(--muted);text-align:left;padding:.75rem;border-bottom:1px solid var(--border);font-weight:600;white-space:nowrap;}
.eu-table td{padding:.75rem;border-bottom:1px solid var(--border-2);font-size:.83rem;vertical-align:top;}
.eu-table tr:last-child td{border-bottom:none;}
.eu-table tbody tr:hover{background:rgba(var(--ink-rgb),.04);}
.eu-card .eu-table th:first-child,.eu-card .eu-table td:first-child{padding-left:1.25rem;border-left:0;}
.eu-card .eu-table th:last-child,.eu-card .eu-table td:last-child{padding-right:1.25rem;border-right:0;}
/* Empty state (EU-423): icon slot, title, one-line hint, optional action. Rendered by
   the empty_state() context helper (`_empty_state_html`); JS-built lists can emit the
   same markup. A bare .eu-empty with plain text still renders as a quiet centred line.
   The hint says what will land here and who fills it, not just that it is empty
   (docs/kb/2026-08-26-onboarding-empty-states.md). */
.eu-empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.35rem;
  padding:2rem 1.25rem;color:var(--faint);font-size:.85rem;}
.eu-empty-ico{width:44px;height:44px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;background:rgba(var(--navy-rgb),.08);color:var(--muted);margin-bottom:.35rem;}
.eu-empty-ico svg{width:20px;height:20px;}
.eu-empty-title{font-size:.92rem;font-weight:600;color:var(--ink);margin:0;}
.eu-empty-hint{margin:0;max-width:34rem;line-height:1.5;color:var(--muted);}
.eu-empty-action{margin-top:.55rem;}
td>.eu-empty{padding:1.6rem 1rem;}
.eu-empty[hidden]{display:none;}
.eu-alert{border-radius:var(--radius-sm);font-size:.83rem;padding:.7rem .9rem;}
.eu-alert--error{background:var(--brand-tint);border:1px solid rgba(var(--brand-rgb),.3);color:var(--brand-600);}
.eu-alert--ok{background:var(--ok-tint);border:1px solid rgba(var(--ok-rgb),.3);color:var(--ok);}
.eu-footer{border-top:2px solid var(--brand);background:var(--card);margin-top:2.5rem;}
.eu-footer-in{max-width:1140px;margin:0 auto;padding:1.4rem 1.25rem;display:flex;flex-wrap:wrap;gap:1rem 1.5rem;align-items:center;justify-content:space-between;}
.eu-footer-brand{display:flex;align-items:center;gap:.7rem;}
.eu-footer-brand img{height:28px;width:auto;}
.eu-footer-brand .fb-name{font-family:'Space Grotesk';font-weight:700;color:var(--navy);font-size:.92rem;}
.eu-footer-brand .fb-tag{font-size:.75rem;color:var(--faint);}
.eu-footer-meta{display:flex;flex-wrap:wrap;gap:.25rem 1rem;font-size:.8rem;color:var(--muted);align-items:center;justify-content:flex-end;}
.eu-footer-meta a{color:var(--muted);}
.eu-footer-meta a:hover{color:var(--brand);}
.eu-footer-legal{max-width:1140px;margin:0 auto;padding:.85rem 1.25rem 1.6rem;font-size:.74rem;color:var(--faint);border-top:1px solid var(--border-2);}
.eu-footer-legal a{color:var(--muted);}
.eu-footer-legal a:hover{color:var(--brand);}

/* ---------- Page header (EU-872 Phase 5) ----------
   Consistent heading region for every module landing page. Keeps the title, optional
   subtitle, and trailing actions in a baseline-aligned flex row. */
.eu-page-head{display:flex;align-items:baseline;flex-wrap:wrap;gap:.5rem 1rem;margin-bottom:1.25rem;}
.eu-page-head h1{font-size:1.35rem;font-weight:700;color:var(--ink);margin:0;line-height:1.3;}
.eu-page-head p{font-size:.85rem;color:var(--muted);margin:0;flex:1 1 100%;order:1;}
.eu-page-head .eu-page-actions{margin-left:auto;display:flex;gap:.5rem;flex-shrink:0;}
/* ---------- Tab strip card (EU-872 Phase 5) ----------
   Horizontal tab row that sits inside a card or directly below a page header. The active
   tab gets a 2px brand underline; the container carries a subtle card shadow so it reads
   as a surface. Tabs scroll horizontally on narrow viewports. */
.eu-tab-strip{display:flex;gap:0;background:var(--card);border-radius:var(--radius) var(--radius) 0 0;
  box-shadow:var(--shadow-sm);border-bottom:1px solid var(--border);overflow-x:auto;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;}
.eu-tab-strip::-webkit-scrollbar{display:none;}
.eu-tab-strip a,.eu-tab-strip button{display:inline-flex;align-items:center;gap:.4rem;
  padding:.7rem 1rem;font-size:.83rem;font-weight:600;color:var(--muted);background:none;
  border:0;border-bottom:2px solid transparent;cursor:pointer;white-space:nowrap;
  font-family:inherit;transition:color var(--ease),border-color var(--ease);}
.eu-tab-strip a:hover,.eu-tab-strip button:hover{color:var(--ink);}
.eu-tab-strip a.active,.eu-tab-strip button.active{color:var(--ink);border-bottom-color:var(--brand);}
.eu-tab-strip a:focus-visible,.eu-tab-strip button:focus-visible{
  outline:2px solid var(--brand);outline-offset:-2px;box-shadow:none;}

/* ---------- Sticky footer, uniform across every authenticated page ----------
   Each authenticated page carries class="eu-app" on its <body>, which becomes a
   min-height:100vh flex column. The main content region (.eu-shell, or the older
   per-page .wrap) grows to fill, so the footer pins to the bottom of the viewport
   on short pages and sits below the content - never overlapping it - on long ones,
   IDENTICALLY on every page. The LOGIN / password pages deliberately do NOT get
   .eu-app: their <body> keeps its own centered flex layout, so this leaves them
   untouched (the flex rule is scoped to body.eu-app, never bare body). */
body.eu-app{min-height:100vh;display:flex;flex-direction:column;}
body.eu-app > .eu-shell,
body.eu-app > .wrap{flex:1 0 auto;}
body.eu-app > .eu-footer{flex-shrink:0;}

/* ---------- Accessibility utilities (added 2026-08 UI hardening) ---------- */
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:var(--radius-sm);}
.skip-link{position:absolute;left:.5rem;top:-4rem;z-index:100;background:var(--navy);color:#fff;padding:.55rem 1rem;border-radius:var(--radius);font-size:.85rem;font-weight:600;transition:top var(--ease);}
.skip-link:focus{top:.5rem;box-shadow:var(--ring);}
.eu-btn:active{transform:translateY(1px);}

/* ---------- Collapsible section boxes (Settings / Activity) ----------
   Native <details>/<summary>: zero JS to toggle, accessible, keyboard-driven. The
   summary keeps each section's existing header styling (.section-title or a wrapped
   <h2>); we only drop the default disclosure triangle and add a rotating chevron keyed
   off the [open] state. State is remembered in localStorage (see _COLLAPSE_JS). Theme-
   aware via currentColor. */
details.eu-collapse > summary{list-style:none;cursor:pointer;position:relative;padding-right:1.6rem;-webkit-user-select:none;user-select:none;}
details.eu-collapse > summary::-webkit-details-marker{display:none;}
details.eu-collapse > summary::after{content:"";position:absolute;right:.35rem;top:.35rem;width:.5rem;height:.5rem;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform .15s;opacity:.5;}
details.eu-collapse[open] > summary::after{transform:rotate(45deg);}
details.eu-collapse > summary:hover::after{opacity:.85;}
details.eu-collapse > summary:focus-visible{outline:none;box-shadow:var(--ring);border-radius:6px;}
details.eu-collapse > summary h2{display:inline;}

/* ---------- Table search + pagination controls (.eu-paginate + paginateTables) ----------
   A search box (.eu-search) is injected above the table and a controls bar after it. Rows
   outside the current page hide; a zero-match search shows the .eu-nomatch row. Theme-aware
   via tokens; the disabled state dims the Prev/Next button at an edge. */
.eu-pager{display:flex;align-items:center;gap:.6rem;padding:.6rem 1rem;border-top:1px solid var(--border-2);border-radius:0 0 var(--radius) var(--radius);}
.eu-pager-btn{background:var(--card);color:var(--muted);border:1px solid var(--border);border-radius:var(--radius-sm);padding:.3rem .7rem;font-size:.78rem;font-weight:600;cursor:pointer;transition:var(--ease);}
.eu-pager-btn:hover:not(:disabled){border-color:var(--navy);color:var(--ink);}
.eu-pager-btn:disabled{opacity:.4;cursor:default;}
.eu-pager-info{color:var(--faint);font-size:.76rem;}
.eu-tbl-search{display:block;width:calc(100% - 2rem);max-width:calc(100% - 2rem);box-sizing:border-box;margin:.7rem 1rem;padding:.4rem .7rem;font-size:.82rem;color:var(--ink);background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm);}
.eu-tbl-search:focus{outline:none;border-color:var(--navy);box-shadow:var(--ring);}
.eu-tbl-search::placeholder{color:var(--faint);}
.eu-nomatch td{color:var(--faint);font-size:.82rem;text-align:center;padding:.9rem 1rem;}

/* ---------- Responsive: tablet / phone (base had no breakpoints) ---------- */
@media (max-width:760px){
  .eu-shell{padding:1rem .9rem 2.5rem;}
  .eu-topbar-in{padding:.55rem .9rem;gap:.5rem;flex-wrap:wrap;}
  .eu-nav{width:100%;margin-left:0;overflow-x:auto;flex-wrap:nowrap;gap:.1rem;padding-bottom:.15rem;scrollbar-width:thin;}
  .eu-nav a{white-space:nowrap;flex:0 0 auto;padding:.4rem .65rem;}
  .eu-nav .eu-acct-wrap{order:-1;margin-left:auto;}
  .eu-table{display:block;overflow-x:auto;white-space:nowrap;-webkit-overflow-scrolling:touch;}
  .eu-footer-in,.eu-footer-meta{justify-content:flex-start;}
}
@media (max-width:480px){
  .eu-btn{padding:.55rem .85rem;}
}

/* ---------- Respect reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:.001ms!important;animation-duration:.001ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;}
}

/* ---------- Dark mode (LIVE 2026-08). ----------
   Resolution happens in JS before paint (see _THEME_HEAD, injected into every page's
   <head> via {{ theme_head|safe }}): it reads localStorage['eu-theme'] and, absent an
   explicit choice, falls back to matchMedia('(prefers-color-scheme: dark)') - so dark
   applies BOTH automatically (following the OS) AND manually (the topbar toggle), while an
   explicit 'light' choice overrides the OS. The script always stamps
   documentElement.dataset.theme = 'dark'|'light' BEFORE first paint, so the CSS needs only
   the :root[data-theme="dark"] rules below (no @media duplication) and there is no flash.
   Tokens flip here; each authenticated page's inline <style> has been migrated to those
   tokens, and the handful of unmapped light surfaces/accents get explicit page-level
   overrides further down so nothing goes light-on-light or dark-on-dark. Light mode is
   byte-identical: every override lives under [data-theme="dark"] and touches nothing in the
   default :root. */
:root[data-theme="dark"]{
  color-scheme:dark;
  /* Foreground-only lightening. The previous values (#2f6fb3 / #ff5d6a) were derived
     from the pre-identity-sheet palette and had drifted to a mid blue and a pink;
     these are lifted from Ironclad Navy and Break Red and stay in family. The
     brand GROUND does not lighten - see --rail. */
  --navy:#5C87BD; --navy-700:#7AA0CE; --navy-600:#3F6DA4;
  --brand:#FF7A70; --brand-600:#FF9A92; --brand-tint:#3A1F1D;
  --rail:#14273F;
  /* Declared ONCE per block. Until EU-415 a second, later line in this block
     re-declared the retired pink/mid-blue triplets and silently won, so every dark
     tint built on them was off-brand. tests/test_brand_identity.py now fails on any
     custom property declared twice in a theme block. */
  --brand-rgb:255,122,112; --navy-rgb:92,135,189;
  --bg:#0b1220; --card:#141d2e; --ink:#e9f0f8; --muted:#a9b8ce; --faint:#8194ad;
  --border:#26324a; --border-2:#1b2537;
  --ok:#4bd39a; --ok-tint:#123227; --warn:#e0b25a; --warn-tint:#332a15;
  --ok-rgb:75,211,154; --warn-rgb:224,178,90;
  --info:#7ea9e0; --info-tint:#182742;
  --shadow-sm:0 .125rem .375rem rgba(0,0,0,.35);
  --shadow:0 .125rem 1rem rgba(0,0,0,.4);
  --shadow-lg:0 .5rem 2.5rem rgba(0,0,0,.4);
  /* Focus ring = the dark --brand (#FF7A70) at .4. It was rgba(255,93,106,.4), the
     retired pink, until EU-415. */
  --ring:0 0 0 3px rgba(255,122,112,.4);
  --ink-rgb:233,240,248;
  --card-title:#96a8bf;
}
:root[data-theme="dark"] .eu-topbar{background:rgba(20,29,46,.85);box-shadow:0 0 2rem 0 rgba(0,0,0,.35);}
:root[data-theme="dark"] ::placeholder{color:var(--faint);opacity:1;}
:root[data-theme="dark"] .eu-mark path[stroke="#E63B34"]{stroke:var(--brand);}
:root[data-theme="light"]{color-scheme:light;}

/* ---------- Theme toggle control (topbar) ---------- */
.eu-theme-select{font-family:inherit;font-size:.78rem;font-weight:600;color:var(--muted);
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius);padding:.32rem .5rem;
  cursor:pointer;line-height:1;}
.eu-theme-select:hover{border-color:var(--navy);color:var(--ink);}
.eu-theme-select:focus-visible{outline:none;box-shadow:var(--ring);}

/* ---------- Page-level dark overrides: unmapped light surfaces/accents from the pages'
   own inline <style> that the token swap could not map to an exact token. Backgrounds/
   large surfaces get a dark value so nothing renders light-on-dark; a few dark-on-dark
   text accents (info pills, amber pills, the group-member list) get a legible token.
   Selectors are page-specific (dashboard/settings/activity) but harmless where absent.
   Every rule is scoped to [data-theme="dark"], so LIGHT mode is untouched. See KB 2.6. */
:root[data-theme="dark"] .cat-link:hover,
:root[data-theme="dark"] .job-btn:hover,
:root[data-theme="dark"] .add-rule-form,
:root[data-theme="dark"] .sev-low,
:root[data-theme="dark"] .log-table thead th{background:var(--border-2);}
:root[data-theme="dark"] .eis-q1{background:var(--brand-tint);}
:root[data-theme="dark"] .eis-q2{background:var(--ok-tint);}
:root[data-theme="dark"] .task-undo:hover,
:root[data-theme="dark"] .grp-badge.grp-toggle:hover{background:var(--info-tint);}
:root[data-theme="dark"] .stack-ask{background:var(--warn-tint);color:var(--warn);border-color:var(--border);}
:root[data-theme="dark"] .pill-Needs.Reply,
:root[data-theme="dark"] .sev-high{color:var(--warn);}
:root[data-theme="dark"] .pill-Lead,
:root[data-theme="dark"] .pill-FYI,
:root[data-theme="dark"] .log-badge-info,
:root[data-theme="dark"] .eis-q4 .eis-q-label,
:root[data-theme="dark"] .eis-q4 .eis-q-count,
:root[data-theme="dark"] .grp-member .gm-from{color:var(--info);}
:root[data-theme="dark"] .grp-member{color:var(--ink);}
:root[data-theme="dark"] .log-badge-task{background:#241d3a;color:#c3b4ec;}
:root[data-theme="dark"] .google-btn{color:var(--ink);}
:root[data-theme="dark"] #verify-banner{background:var(--warn-tint)!important;color:var(--warn)!important;border-color:var(--border)!important;}
:root[data-theme="dark"] .eu-learned-box{background:var(--ok-tint)!important;color:var(--ok)!important;border-color:var(--border)!important;}
:root[data-theme="dark"] .eu-note-amber{background:var(--warn-tint)!important;color:var(--warn)!important;border-color:var(--border)!important;}

/* Thin tinted 1px borders: on dark, a near-white tint reads as a bright out-of-place line,
   so each is darkened to a border that matches its tint family. Grouped by family. */
:root[data-theme="dark"] .alert.success,
:root[data-theme="dark"] .pill-Expense,
:root[data-theme="dark"] .eis-q2{border-color:#2b4d3d;}          /* on --ok-tint */
:root[data-theme="dark"] .alert.error,
:root[data-theme="dark"] .pill-Urgent,
:root[data-theme="dark"] .eis-q1,
:root[data-theme="dark"] .err{border-color:#5a2a2e;}            /* on --brand-tint */
:root[data-theme="dark"] .pill-Lead,
:root[data-theme="dark"] .pill-FYI{border-color:#2f4166;}       /* on --info-tint */
:root[data-theme="dark"] .pill-Needs.Reply,
:root[data-theme="dark"] .pill-Spam{border-color:#4d3f1f;}      /* on --warn-tint */
:root[data-theme="dark"] .task-clear-defer,
:root[data-theme="dark"] .task-undo{border-color:var(--border);}
:root[data-theme="dark"] .stack-q3{border-left-color:var(--warn);}
/* Inline-styled borders (higher specificity than a class rule -> need !important). */
:root[data-theme="dark"] .eu-step-dot{border-color:var(--border)!important;}
:root[data-theme="dark"] .eu-divider{border-top-color:var(--border)!important;}
:root[data-theme="dark"] .eu-ok-box{border-color:#2b4d3d!important;}

/* ================================================================================
   MOBILE + ACCESSIBILITY POLISH (2026-08-26)
   The owner's users are trades SMBs working from a phone in a van, so the phone is
   the PRIMARY surface. This whole pass is CSS-only and lives in ONE appended block
   so nothing above is restructured and no product template had to be touched: every
   page that renders the shared shell picks the rules up for free. Everything is
   token-driven, so the dark-theme token flip above still applies unchanged.
   ================================================================================ */

/* ---------- 1. Wide tables ----------
   Tables are emitted WITHOUT a scroll wrapper all over the app (table.acct,
   .log-table, .crm-table, .inv-table, .qt-table, .cal-table, .tm-table, .ex-table,
   .rt, .eu-table ...). Rather than edit ~14 templates, make the table itself the
   scroll container - the same `display:block;overflow-x:auto` hack .eu-table already
   used at <=760px, generalized to any table inside a card/section/shell.
   `min-width:100%` keeps a NARROW table full-bleed instead of shrink-to-fit.
   Applied at ALL widths because multi-column tables (e.g. tracking, history) can
   overflow even on 1100px-capped layouts.
   Deliberately NOT `.card{overflow-x:auto}`: an overflow container on the card would
   clip any absolutely-positioned menu inside it and break `position:sticky`
   descendants, and it would not help a table that is a grandchild of the card. */
.card table,.eu-card table,.section table,.eu-shell>table,.eu-scroll-x table{
  display:block;min-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;}
@media (max-width:900px){
  .card,.eu-card,.section{max-width:100%;}
}
/* Sticky header for a LONG, unpaginated list (EU-423). A header cannot stick to the
   viewport while the table is its own overflow container (the rule above), so the
   opt-in is a WRAPPER: <div class="eu-sticky-head"><table>...</table></div>. The wrapper
   scrolls on both axes inside a viewport-relative max height, the table goes back to a
   real full-width table (undoing the display:block hack, which also stops it being the
   nearest scroll container), and the header cells stick to the top of the wrapper.
   Do not use it on a short or paginated (.eu-paginate) table: a nested scroll box with
   10 rows is only friction. */
.eu-sticky-head{max-height:70vh;overflow:auto;-webkit-overflow-scrolling:touch;}
.eu-sticky-head>table{display:table;width:100%;overflow:visible;}
.eu-sticky-head>table thead th{position:sticky;top:0;z-index:1;background:var(--card);}
/* Tabular figures stay right-aligned and column-aligned wherever a page uses the
   `.num` convention (accounting.html defines this locally; now every page has it). */
td.num,th.num,td.amount,th.amount,.eu-tabnum{
  text-align:right;font-variant-numeric:tabular-nums;}
/* Long unbroken strings (email addresses, URLs, vendor ids) must not push the page
   sideways on a 360px screen. `break-word` only breaks when nothing else fits. */
.eu-shell,.card,.eu-card{overflow-wrap:break-word;}
img{max-width:100%;}

/* ---------- 2. Touch targets (WCAG 2.5.5 AAA / Apple HIG: 44px) ----------
   Applied ONLY at <=900px (the phone/tablet range that also turns the sidebar into
   the off-canvas drawer), so the dense desktop layout is untouched. */
@media (max-width:900px){
  .eu-btn,button,input[type=submit],input[type=button],input[type=reset],
  select,summary,a.eu-badge,a.eu-badge-soft{
    min-height:44px;}
  /* The status bar is a deliberately slim always-on strip; a 44px floor there would
     eat a chunk of a phone viewport on every page, so its links get the WCAG 2.5.8
     (AA) 24px floor with room to spare instead of the AAA 44px. */
  a.eu-sb-seg,a.eu-sb-billing{min-height:32px;}
  .eu-btn,button,input[type=submit],input[type=button],input[type=reset]{
    min-width:44px;padding-top:.55rem;padding-bottom:.55rem;}
  /* Small inline row buttons keep their compact type but gain the touch height. */
  .eu-btn--sm{padding-left:.7rem;padding-right:.7rem;}
  .eu-pager-btn{min-height:44px;min-width:44px;}
  /* Off-canvas sidebar rows: a comfortable thumb target, icon + label centred. */
  .eu-side-link,summary.eu-side-link{min-height:48px;align-items:center;}
  .eu-side-children .eu-side-link{min-height:44px;}
  /* The hamburger and the bell are the two chrome controls a thumb reaches for. */
  .eu-side-toggle,.eu-bell,.eu-acct-btn{min-width:44px;min-height:44px;
    align-items:center;justify-content:center;}
  /* Action links that sit in a table row (Manage / Wrong? / Bring back ...). */
  td a.eu-btn,td button,.hrow-actions a,.hrow-actions button{min-height:44px;}
}

/* ---------- 3. Focus visibility ----------
   The base `:focus-visible` rule above paints only a box-shadow ring, which several
   component rules then cancel with their own `outline:none` (and which disappears
   entirely in forced-colors / Windows high-contrast mode, and gets clipped by any
   overflow container). Re-assert a REAL outline plus the ring, listing the component
   selectors explicitly so this block wins on specificity rather than by restructuring
   the rules above. */
a:focus-visible,button:focus-visible,select:focus-visible,textarea:focus-visible,
input:focus-visible,summary:focus-visible,[tabindex]:focus-visible,
.eu-btn:focus-visible,.eu-theme-select:focus-visible,.eu-bell:focus-visible,
.eu-acct-btn:focus-visible,.eu-acct-item:focus-visible,
.eu-bell-item:focus-visible,.eu-pager-btn:focus-visible,.eu-search:focus-visible,.eu-tbl-search:focus-visible,
.eu-input:focus-visible,.eu-reclassify-select:focus-visible,
.eu-subnav a:focus-visible,.eu-side-toggle:focus-visible,
details.eu-collapse>summary:focus-visible{
  outline:2px solid var(--brand);outline-offset:2px;box-shadow:var(--ring);}
/* Inside the fixed, scrolling navy rail an outward offset is clipped, so inset it. */
.eu-side-link:focus-visible,summary.eu-side-link:focus-visible,
.eu-side-brand:focus-visible{
  outline:2px solid #fff;outline-offset:-2px;box-shadow:none;}
/* Skip link: already positioned off-screen until focused - make its ring unmissable. */
.skip-link:focus,.skip-link:focus-visible{outline:2px solid #fff;outline-offset:2px;}
/* #main is the skip-link target and carries tabindex="-1"; never paint a ring on it
   just because script/skip focus landed there. */
#main:focus,#main:focus-visible{outline:none;box-shadow:none;}
/* Windows high-contrast / forced-colors: system colors only. */
@media (forced-colors:active){
  a:focus-visible,button:focus-visible,select:focus-visible,input:focus-visible,
  textarea:focus-visible,summary:focus-visible{
    outline:3px solid Highlight;outline-offset:2px;}
  .eu-side-link.active{border-left:3px solid Highlight;padding-left:calc(1.625rem - 3px);}
}

/* ---------- 3b. Reduced motion (extends the global guard above) ----------
   The global rule zeroes transition/animation DURATIONS, which stops the bell
   dropdown keyframe and the sidebar slide, but a duration of ~0 still leaves the
   `transform` states in play. Kill the decorative ones outright. */
@media (prefers-reduced-motion:reduce){
  .eu-bell-menu{animation:none;}
  .eu-acct-menu{animation:none;}
  .eu-sidebar{transition:none;}
  .eu-btn:active{transform:none;}
  .eu-sb-spin,.eu-run-spin{animation:none;}
  .eu-side-children .eu-side-link:hover::before{transform:none;}
  summary.eu-side-link::after,.eu-side-group[open]>summary.eu-side-link::after{
    transition:none;}
  .eu-run-fill{transition:none;}
}

/* ---------- 4. Forms on mobile ----------
   iOS Safari ZOOMS the viewport whenever a focused text field renders below 16px,
   and every field in this app is .82-.875rem (13-14px). 16px at <=900px removes the
   zoom-and-never-zoom-back trap. The topbar theme <select> is deliberately excluded
   (a select opens a native picker - no zoom - and 16px would break the slim topbar).
   `.eu-nav select{width:auto}` above is more specific than the full-width rule, so
   the account switcher is likewise unaffected. */
@media (max-width:900px){
  input,textarea,select,.eu-input,.eu-search,.eu-tbl-search,.eu-reclassify-select{font-size:16px;}
}
@media (max-width:600px){
  input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]),
  textarea,select,.eu-input{width:100%;max-width:100%;}
  .eu-label{font-size:.85rem;}
}

/* ---------- 5. Contextual help tooltips ---------- */
.eu-help{
  display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:50%;
  font-size:11px;font-weight:700;line-height:1;
  color:var(--brand);background:var(--info-tint,rgba(59,130,246,.1));
  border:1px solid var(--brand);cursor:pointer;
  position:relative;vertical-align:middle;margin-left:6px;
  user-select:none;flex-shrink:0;
}
.eu-help:hover,.eu-help:focus-visible{
  background:var(--brand);color:#fff;outline:none;
}
.eu-help-pop{
  display:none;position:absolute;z-index:1200;
  left:50%;transform:translateX(-50%);top:calc(100% + 8px);
  width:340px;max-width:92vw;
  background:var(--card);border:1px solid var(--border);
  border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.12);
  padding:0;text-align:left;font-weight:400;
  font-size:.85rem;line-height:1.5;color:var(--ink);
}
.eu-help.eu-help--open .eu-help-pop{display:block;}
.eu-help-pop-head{
  display:flex;align-items:flex-start;justify-content:space-between;gap:8px;
  padding:12px 14px 8px;border-bottom:1px solid var(--border);
  font-weight:600;font-size:.88rem;
}
.eu-help-pop-close{
  background:none;border:none;font-size:18px;cursor:pointer;
  color:var(--muted);padding:0;line-height:1;flex-shrink:0;
}
.eu-help-pop-close:hover{color:var(--ink);}
.eu-help-pop-body{padding:10px 14px 12px;}
.eu-help-pop-body ol{margin:0;padding-left:20px;}
.eu-help-pop-body li{margin-bottom:6px;}
.eu-help-pop-body li:last-child{margin-bottom:0;}
.eu-help-pop-body code{
  background:var(--info-tint,rgba(59,130,246,.1));
  padding:1px 5px;border-radius:3px;font-size:.82rem;
  word-break:break-all;
}
.eu-help-pop-body a{color:var(--brand);text-decoration:underline;}
.eu-help-pop-link{
  display:flex;align-items:center;gap:6px;
  padding:8px 14px 12px;font-size:.82rem;color:var(--brand);
  text-decoration:none;
}
.eu-help-pop-link:hover{text-decoration:underline;}
@media (max-width:600px){
  .eu-help-pop{
    position:fixed;left:4vw;right:4vw;top:auto;bottom:16px;
    transform:none;width:auto;max-width:none;
  }
}
