/* ── Base: the page itself ──────────────────────────────────────────────
   Elements, the app shell (header, sidebar, main), form controls, focus,
   the busy bar, flash toasts and the other furniture every console page has.
   Loaded after tokens.css and before components.css (templates/core/_stylesheets.html). */

@font-face{
  font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url("../../vendor/jetbrains-mono/jetbrains-mono-latin-500-normal.a21e038a03f7.woff2") format('woff2');
}
*{margin:0;padding:0;box-sizing:border-box;}
body{
  font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','SF Pro Text','Segoe UI',sans-serif;
  background:var(--bg);color:var(--text);
  height:100vh;display:flex;flex-direction:column;overflow:hidden;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
.brand-lockup{display:flex;align-items:center;text-decoration:none;color:inherit;}
.brand-lockup:hover{color:inherit;}
.brand-logo{height:34px;width:auto;display:block;flex-shrink:0;}
header p{font-size:11px;color:var(--muted);margin-top:1px;letter-spacing:.4px;text-transform:uppercase;font-weight:600;}
.hbtns{display:flex;gap:8px;align-items:center;}
.avatar-menu{position:relative;}
.avatar-menu summary{list-style:none;cursor:pointer;}
.avatar-menu summary::-webkit-details-marker{display:none;}
.avatar-menu summary::after{content:"▾";margin-left:6px;font-size:10px;}
.avatar-menu-panel{position:absolute;top:calc(100% + 6px);right:0;min-width:200px;background:var(--surface);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow-lg);padding:6px;display:flex;flex-direction:column;z-index:120;}
.avatar-menu-panel a{padding:8px 10px;border-radius:7px;color:var(--text);font-size:13px;text-decoration:none;}
.avatar-menu-panel a:hover{background:rgba(255,107,26,.08);}
.avatar-menu-sep{border:0;border-top:1px solid var(--border);margin:6px 4px;}
.avatar-menu-form{margin:0;}
/* The menu's name button stays put when pressed; the global .btn:active
   shrink made it jiggle as the menu opened (Sam, 2026-09-26). */
.avatar-menu summary.btn:active{transform:none;}
/* Dark mode switch row in the account menu. */
.avatar-menu-switch{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:7px;color:var(--text);font-size:13px;cursor:pointer;}
.avatar-menu-switch:hover{background:rgba(255,107,26,.08);}
.avatar-menu-switch input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none;}
.avatar-menu-switch .switch-track{position:relative;flex:none;width:32px;height:18px;border-radius:999px;background:var(--border-strong);transition:background .15s;}
.avatar-menu-switch .switch-track::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:1px 1px 2px rgba(0,0,0,.25);transition:transform .15s;}
.avatar-menu-switch input:checked + .switch-track{background:var(--orange);}
.avatar-menu-switch input:checked + .switch-track::after{transform:translateX(14px);}
.avatar-menu-switch input:focus-visible + .switch-track{outline:2px solid var(--orange);outline-offset:2px;}
.avatar-menu-form button{width:100%;text-align:left;padding:8px 10px;border:0;border-radius:7px;background:none;color:var(--text);font:inherit;font-size:13px;cursor:pointer;}
.avatar-menu-form button:hover{background:rgba(255,107,26,.08);}
nav{background:var(--surface);border-bottom:1px solid var(--border);display:flex;flex-wrap:wrap;flex-shrink:0;position:relative;}
nav button, nav .nav-item{flex:1;padding:13px 10px;background:none;border:none;border-bottom:2px solid transparent;color:var(--soft);font-size:13px;font-weight:500;cursor:pointer;transition:color .15s, background .15s, border-color .15s;letter-spacing:-.1px;display:flex;align-items:center;justify-content:center;gap:6px;font-family:inherit;}
nav button:hover, nav .nav-item:hover{color:var(--text);background:rgba(255,107,26,.04);}
nav button.active, nav .nav-item.active{color:var(--orange);border-bottom-color:var(--orange);background:rgba(255,107,26,.06);font-weight:600;}

/* Routines hover dropdown */
main{flex:1;overflow:hidden;}
/* --content-x is where page content starts across the window (the open
   sidebar, or the collapsed one's toggle, plus the tab's padding), which
   `.window-center` reads to center a column on the whole window. */
:root{--tab-pad-x:28px;--content-x:calc(var(--sb-w) + var(--tab-pad-x));}
.tab{display:none;height:100%;overflow-y:auto;padding:26px var(--tab-pad-x);}
.tab.active{display:block;}
#chat-tab.active{display:flex;flex-direction:column;padding:0;}
button{cursor:pointer;border:none;border-radius:8px;font-family:inherit;}
/* Global busy bar. Fades in only after a ~300ms grace period: a navigation
   that lands quickly (cached page, prefetched link, fast fragment swap) never
   flashes the bar at all — the flash itself read as "the whole page reloaded". */
#busy-bar{position:fixed;top:0;left:0;right:0;height:3px;z-index:9999;background:linear-gradient(90deg,transparent 0%,var(--orange) 40%,var(--orange) 60%,transparent 100%);background-size:300% 100%;opacity:0;pointer-events:none;transition:opacity .15s ease;}
#busy-bar.show{opacity:1;transition-delay:.3s;animation:busy-slide 1.4s linear infinite;}
@keyframes busy-slide{0%{background-position:100% 0;}100%{background-position:-200% 0;}}

/* ── Page-to-page navigation (cross-document view transitions) ────────────
   The sticky chrome (header + sidebar) is its own named transition group with
   no animation, so it stays put like an app shell from one page to the next.
   The content swaps in a single frame. It used to crossfade over 160ms, which
   drew the old page's text and the new page's on top of each other for a few
   frames and read as the page flickering (Sam, 2026-09-26). The first frame of
   the new page is already complete (base.html holds paint until the page is
   parsed), so there is nothing to fade in. */
@media (prefers-reduced-motion: no-preference){
  @view-transition{ navigation: auto; }
  ::view-transition-old(root){ animation:none; opacity:0; }
  ::view-transition-new(root){ animation:none; }
  /* body > header only: inner card/section <header>s (e.g. the tasks board's
     post-its) must NOT get the name — a duplicate view-transition-name makes
     the browser skip the whole transition on that page. */
  body > header{ view-transition-name: oasis-header; }
  nav.sidebar{ view-transition-name: oasis-sidebar; }
  ::view-transition-group(oasis-header),
  ::view-transition-group(oasis-sidebar){ animation-duration:0s; }
  ::view-transition-old(oasis-header),
  ::view-transition-new(oasis-header),
  ::view-transition-old(oasis-sidebar),
  ::view-transition-new(oasis-sidebar){ animation:none; mix-blend-mode:normal; }
}
label{display:block;font-size:11.5px;color:var(--text);font-weight:500;margin-bottom:6px;}
input[type=text],input[type=email],input[type=password],input[type=search],input[type=number],input[type=tel],input[type=url],input[type=date],textarea{width:100%;background:var(--surface);border:1px solid var(--border-strong);color:var(--text);padding:10px 12px;border-radius:10px;font-size:13px;font-family:inherit;outline:none;transition:border-color .15s, box-shadow .15s;}
input[type=text]:focus,input[type=email]:focus,input[type=password]:focus,input[type=search]:focus,input[type=number]:focus,input[type=tel]:focus,input[type=url]:focus,input[type=date]:focus,textarea:focus{border-color:var(--orange);box-shadow:0 0 0 4px rgba(255,107,26,.14);}
/* `class="select"` — used repo-wide (see e.g. _wo_report_content.html,
   task_form.html) but never actually defined until now, so every <select>
   carrying it rendered as a bare, unstyled native control instead of
   matching the text/date inputs beside it. Mirrors the input rule above
   rather than introducing a third visual language. */
select.select{width:100%;background:var(--surface);border:1px solid var(--border-strong);color:var(--text);padding:10px 12px;border-radius:10px;font-size:13px;font-family:inherit;outline:none;transition:border-color .15s, box-shadow .15s;}
select.select:focus{border-color:var(--orange);box-shadow:0 0 0 4px rgba(255,107,26,.14);}
/* A select sitting in a row of buttons (`.cluster`), sized to its options. */
select.select.select--inline{width:auto;padding:6px 10px;}
/* A text box sitting in a row of buttons (`.cluster`): every text input fills
   its container, so one beside a Save button needs a width of its own. */
input[type=text].input--inline{width:min(100%,260px);padding:6px 10px;}
input[type=text]::placeholder,input[type=email]::placeholder,input[type=password]::placeholder,input[type=search]::placeholder,input[type=number]::placeholder,input[type=tel]::placeholder,input[type=url]::placeholder,textarea::placeholder{color:#b1b1b6;}
textarea{resize:vertical;min-height:80px;line-height:1.5;}
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;}
::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);}
/* Flash messages float as toasts (Ivan, 2026-08-20: shouldn't be part of the
   page) — the wrapper is fixed-position so it never pushes page content, and
   each message keeps its own background/shadow since it's now floating over
   whatever's underneath rather than sitting inline in the page flow. */
/* Flash toasts stack in the BOTTOM-right, vertically above the floating Chat
   launcher and Report-a-bug button (Ivan, 2026-09-17: "I'd just like our
   notification here to move vertically above the chat and bug buttons"). Both
   FABs sit at bottom:22px and stand 56px tall, so their top edge is ~78px;
   bottom:90px clears them with room to spare, and z-index:1201 keeps a toast
   above both (they are 1199/1200) if the two ever meet. */
.msg-toasts{position:fixed;right:18px;bottom:90px;z-index:1201;
  display:flex;flex-direction:column;gap:8px;width:min(360px,calc(100vw - 36px));pointer-events:none;}
.msg--success{background:var(--surface);color:var(--green-d);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);border:1px solid rgba(52,199,89,.3);pointer-events:auto;}
.msg--error{background:var(--surface);color:var(--red-d);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);border:1px solid rgba(255,59,48,.3);pointer-events:auto;}
.msg--info{background:var(--surface);color:var(--blue-d);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);border:1px solid rgba(0,122,255,.3);pointer-events:auto;}
.msg--warning{background:var(--surface);color:var(--yellow-d);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);border:1px solid rgba(255,149,0,.3);pointer-events:auto;}
/* A toast is a fixed 360px column, so one long unbroken run of characters --
   an email address, a one-time password -- used to spill past its own rounded
   edge and get clipped by the viewport. That is worst for exactly the messages
   that matter most: the hire confirmation is the ONLY place a new office
   assistant's PIN and password are ever shown. Wrap anywhere rather than
   widening the column. */
.muted{color:var(--muted);}.inline{display:inline;}.stack>*+*{margin-top:14px;}
/* ── Shell + grouped sidebar navigation (UI revamp) ───────────────────────
   The nav was a single cramped row of 16 equal-width tabs. This reorganizes it
   into a clear, grouped LEFT SIDEBAR so the app's sections are scannable at a
   glance. The brand header stays full-width on top; the sidebar is sticky while
   content scrolls, and collapses to a horizontal scrollable bar on narrow
   screens (no JS). Only the sidebar's own rules are overridden here — every
   design token, button, card, table and the .nav-item active color are reused
   unchanged. */
.shell{display:flex;align-items:flex-start;}
.shell > main{flex:1;min-width:0;}

nav.sidebar{
  display:flex;flex-direction:column;flex-wrap:nowrap;gap:2px;
  width:var(--sb-w);flex-shrink:0;
  background:var(--surface);
  border-bottom:none;border-right:1px solid var(--border);
  padding:14px 12px;
  position:sticky;top:var(--header-h,0px);align-self:flex-start;
  /* The full height under the header while open, not just its rows' height,
     so the panel reaches the bottom of the window (Sam, 2026-09-28). */
  height:calc(100vh - var(--header-h, 0px));overflow-y:auto;overflow-x:hidden;
}
nav.sidebar .nav-group:last-child{margin-bottom:0;}
/* Group labels double as collapse toggles: click to fold the group (▾/▸). */
nav.sidebar .nav-group.collapsed .nav-group-label::before{transform:rotate(-90deg);}
nav.sidebar .nav-group.collapsed .nav-item{display:none;}
/* While the live filter is active, matching items show even in a folded group. */
nav.sidebar.nav-filtering .nav-group.collapsed .nav-item{display:flex;}
/* A section whose every link was gated away renders as a bare header with
   nothing under it: a fold that opens onto nothing. That was invisible while
   the sidebar showed most items to most people, and became routine once roles
   got a focused sidebar (apps.core.nav's FOCUS), so the empty ones drop out
   here rather than each section growing a hand-kept {% if %} listing its own
   children. Not a security boundary: it hides a header, and the header never
   carried a link. */
nav.sidebar .nav-group:not(:has(.nav-item)){display:none;}
/* Vertical, left-aligned items (override the legacy horizontal flex:1 tab). */
/* Icons sit in a column of their own, each centered in the same width, so
   the labels beside them line up whatever width an emoji happens to render
   at (2026-09-27). */
nav.sidebar .nav-ico{flex:none;width:18px;display:inline-flex;justify-content:center;}
nav.sidebar .nav-group-label .nav-ico{width:14px;}
/* Search-only tab entries (apps.core.page_tabs / SEARCHABLE_TABS): hidden in
   the sidebar's resting state no matter what — the nav-filter script is the
   only thing that ever reveals one, and only on an actual query match. */
nav.sidebar .nav-item--tab-shadow{display:none;}

/* Collapsible sidebar: the header ☰ toggles html.sb-collapsed (persisted in JS). */
html.sb-collapsed nav.sidebar{display:none;}
/* Narrow screens: sidebar stays VERTICAL (scrolls up/down), just slimmer. */
@media (max-width:900px){
  nav.sidebar{
    width:170px;
    padding:10px 8px;
  }
}

/* ── Interaction & update feedback (UI revamp — front-end only) ────────────
   Makes every interaction and in-place update unmistakable, app-wide, with no
   backend or per-screen changes. The existing #busy-bar is now driven on
   navigation + HTMX requests (wired in base.html); here we (1) flash swapped-in
   content so an update is obvious, (2) dim+lock the control that's mid-request,
   and (3) give every interactive element a clear keyboard-focus + hover state. */

/* HTMX: dim + lock the control that fired the in-flight request, and only a
   control. htmx marks whatever element made the request, which for a lazy
   section or a tab panel is the content area itself: the bare .htmx-request
   rule faded and locked whole pages while they loaded, as if the site were
   refusing input (Sam, 2026-09-26). A loading region shows its skeleton and
   stays usable; a text field being typed into is never dimmed either. */
:is(button,a,select,summary,[role="button"],
    input[type="checkbox"],input[type="radio"],input[type="submit"],input[type="button"]).htmx-request,
form.htmx-request :is(button:not([type="button"]),input[type="submit"]){
  opacity:.55;pointer-events:none;transition:opacity .1s;}
/* HTMX: freshly swapped-in content briefly highlights so the change is seen.
   Every lazy section (hx-trigger="load", always with hx-target="this") is
   exempted, on the swap target itself AND its newly inserted children: for
   an innerHTML swap htmx marks the target htmx-settling and marks each
   top-level node it inserts htmx-added, and for a whole card/table/board
   filling in for the first time (or a tab switch replacing the same
   fragment) that first fill-in is already announced by the skeleton-to-
   content swap itself. Flashing orange across a freshly-loaded section
   reads as an error state rather than "an update landed" (2026-09-21,
   reported as "parts of the page flash orange before the data loads in").
   A lazy container filled from script (htmx.ajax) rather than by its own
   hx-trigger="load" says so with `data-lazy`, and is exempt the same way:
   the Work orders tabs flashed on their first open until it did
   (2026-09-25). A tab swap replaces #tab-panel itself
   (static/js/tab_swap.js), so the panel is exempt the same way; every tab
   swap on the site blinked orange until it was (2026-09-26). A lazy section
   that replaces itself (swap="outerHTML", the To-do board's All tasks table)
   marks its fragment's root data-lazy, which the rules below also cover. The animation stays for genuine small in-place updates
   elsewhere (an assignment chip, a status badge changing) that aren't
   inside one of these lazy containers. A sheet's rows region
   (templates/core/sheet/) is exempt too: it is re-fetched whole on every
   keystroke in its search box, and a table blinking as you type is noise.
   So is a .self-refresh region: one that polls itself while background work
   runs (the packet's drawing progress), where nobody changed anything. */
@keyframes oasis-flash-in{from{background:rgba(255,107,26,.16);}to{background:transparent;}}
.htmx-added,.htmx-settling{animation:oasis-flash-in .85s ease-out;}
[hx-trigger="load"].htmx-settling,[hx-trigger="load"]>.htmx-added,
[data-lazy].htmx-settling,[data-lazy]>.htmx-added,
.lazy-section.htmx-added,.lazy-section.htmx-settling,[data-lazy].htmx-added,
#tab-panel.htmx-added,#tab-panel.htmx-settling,#tab-panel>.htmx-added,
.sheet__rows.htmx-added,.sheet__rows.htmx-settling,
.self-refresh.htmx-added,.self-refresh.htmx-settling{animation:none;}

/* Clear keyboard focus everywhere (accessibility + "what am I on"). */
a:focus-visible,button:focus-visible,.btn:focus-visible,.nav-item:focus-visible,
summary:focus-visible,[role="button"]:focus-visible,
input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:2px solid var(--blue);outline-offset:2px;border-radius:6px;
}
/* Checkboxes and radios take the brand accent, not the browser's blue, so
   a checked box matches the card or row it highlights. A page rule naming
   its own accent-color still wins. */
input[type="checkbox"],input[type="radio"]{accent-color:var(--accent);}
/* Obvious row interaction on data tables. */
.table tbody tr{transition:background .12s;}
/* Everything clickable reads as clickable. */
.btn,.nav-item,[role="button"]{cursor:pointer;}
/* Flash messages slide in so a result/update is noticed. */
@keyframes oasis-msg-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}

/* Sidebar quick-switcher: a compact "Jump to…" filter above the nav groups. */
.nav-search{padding:2px 4px 9px;}
.nav-search input{
  width:100%;padding:7px 11px;font-size:12.5px;
  border:1px solid var(--border-strong);border-radius:8px;
  background:var(--card);color:var(--text);outline:none;
}
.nav-search input::placeholder{color:var(--muted);}
/* On the narrow-screen horizontal strip, drop the filter to save room. */
@media (max-width:900px){.nav-search{display:none;}}

/* ── Dismissible flash messages (× to close; injected by base.html) ──────── */
.msg--success,.msg--error,.msg--info,.msg--warning{overflow-wrap:anywhere;animation:oasis-msg-in .2s ease-out;display:flex;align-items:center;gap:8px;}
.msg-close{margin-left:auto;background:none;border:none;cursor:pointer;font-size:18px;
  line-height:1;color:inherit;opacity:.55;padding:0 2px;border-radius:6px;flex-shrink:0;}
.msg-close:hover{opacity:1;}
/* ── Back-to-top button (appears after scrolling; wired in base.html) ────── */
#to-top{position:fixed;right:18px;bottom:18px;z-index:45;width:40px;height:40px;
  border-radius:50%;border:1px solid var(--border-strong);background:var(--surface);
  color:var(--text);font-size:17px;line-height:1;cursor:pointer;box-shadow:var(--shadow);
  opacity:0;pointer-events:none;transform:translateY(8px);
  transition:opacity .15s, transform .15s, border-color .15s, color .15s;}
#to-top.show{opacity:1;pointer-events:auto;transform:none;}
#to-top:hover{border-color:var(--orange);color:var(--orange);}

/* ── Show/hide password toggle (wired in base.html) ───────────────────────────
   The wrapper is injected around every input[type=password] by script, so every
   password field on the site gets this without its template knowing. Toggling
   the input to type=text keeps the same styling — the base input rule covers
   text and password identically — so the field doesn't jump when revealed.
   An eye icon rather than a "Show"/"Hide" text label — one glyph reads faster
   than a word, and it's the one convention every password field on the web
   already trained people on. */
.pw-wrap{position:relative;display:block;}
.pw-wrap > input{padding-right:46px;}
.pw-toggle{position:absolute;right:2px;top:0;bottom:0;width:44px;
  display:flex;align-items:center;justify-content:center;
  border:0;background:none;color:var(--muted);cursor:pointer;
  border-radius:0 10px 10px 0;transition:color .12s;}
.pw-toggle svg{width:20px;height:20px;}
.pw-toggle:hover{color:var(--orange);}
.pw-toggle:focus-visible{outline:2px solid var(--orange);outline-offset:-2px;color:var(--orange);}
/* Links read as text, accent on hover — kills the underline noise everywhere. */
a{color:inherit;text-decoration:none;}
a:hover{color:var(--accent);text-decoration:none;}
.btn,.btn:hover,.nav-item,.nav-item:hover,.pill,.howto>summary{text-decoration:none;}

/* Header: drop the glass blur + gradient underline → one quiet hairline. */
header{border-bottom:1px solid var(--border);color:var(--text);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;position:sticky;top:0;z-index:100;padding:12px 22px;background:var(--surface);backdrop-filter:none;-webkit-backdrop-filter:none;}
header::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;background:linear-gradient(90deg,transparent 0%, rgba(255,107,26,.35) 40%, rgba(255,107,26,.35) 60%, transparent 100%);display:none;}
header h1{color:var(--text);font-size:15px;font-weight:600;letter-spacing:-.2px;}

/* Sidebar: neutral hovers; the active row is highlighted, with no rail (an
   inset shadow on a rounded row drew a curved sliver at its left edge; Sam,
   2026-09-26). The tint is the accent, so it reads in light and dark alike. */
nav.sidebar .nav-item{flex:none;justify-content:flex-start;text-align:left;border:none;border-bottom:none;line-height:1.25;gap:9px;color:var(--soft);font-size:13px;padding:7px 10px;border-radius:8px;font-weight:500;}
nav.sidebar .nav-item:hover{background:rgba(17,17,26,.05);color:var(--text);}
nav.sidebar .nav-item.active{border-bottom:none;background:color-mix(in srgb, var(--accent) 14%, transparent);color:var(--text);font-weight:600;}
/* A workspace heading reads as a row of its own (Sam, 2026-09-28): 17px,
   four up from the rows' 13px, in regular weight and case (bold and all caps
   both drew the eye too hard), with a fold arrow sized to match and no icon;
   the emoji stay on the rows. Its rows sit indented under a hairline that hangs from the heading's fold
   caret, so what a heading contains is drawn rather than implied. */
nav.sidebar .nav-group{display:flex;flex-direction:column;gap:1px;margin-bottom:10px;position:relative;}
nav.sidebar .nav-group-label{display:flex;align-items:center;cursor:pointer;user-select:none;border-radius:7px;transition:color .12s,background .12s;
  font-size:17px;font-weight:400;letter-spacing:-.1px;text-transform:none;
  color:var(--text);padding:7px 10px;gap:9px;
}
nav.sidebar .nav-group-label:hover{background:rgba(17,17,26,.04);color:var(--text);}
nav.sidebar .nav-group-label::before{content:"▾";line-height:1;color:var(--muted);transition:transform .15s;display:inline-block;font-size:14px;width:14px;text-align:center;flex:none;}
nav.sidebar .nav-group .nav-item{margin-left:14px;}
nav.sidebar .nav-group::after{
  content:"";position:absolute;left:16px;top:36px;bottom:4px;width:1px;
  background:var(--border);pointer-events:none;
}
nav.sidebar .nav-group.collapsed::after{display:none;}
.nav-search input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-weak);}
#sb-toggle:hover{background:rgba(17,17,26,.05);color:var(--text);}
#sb-toggle svg{display:block;}
/* The pane's toggle has one fixed spot just under the top bar (Sam,
   2026-09-28): open, it heads the pane beside "Jump to", which starts after
   it; closed, it stays at the page's top left, with the page's left padding
   widened so no content runs under it. Nothing in the top bar depends on the
   pane, so nothing there moves when it opens or closes. The top bar keeps its
   z-index above the pane and the toggle. */
.hdr-brand{display:flex;align-items:center;gap:14px;}
#sb-toggle{flex-shrink:0;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--soft);font-size:17px;line-height:1;cursor:pointer;display:grid;place-items:center;transition:background .12s,color .12s;
  position:fixed;left:16px;top:calc(var(--header-h, 56px) + 16px);z-index:90;
  width:30px;height:30px;
}
nav.sidebar .nav-search{padding-left:42px;}
html.sb-collapsed .shell > main{padding-left:62px;}
html.sb-collapsed{--content-x:calc(62px + var(--tab-pad-x));}
/* Bare element fallbacks for any page not using a component class. Low specificity
   so .btn/.input/.table/.form-control always win where present. */
main h1{font-size:20px;font-weight:600;letter-spacing:-.3px;}
main h2{font-size:16px;font-weight:600;letter-spacing:-.2px;}

/* ── Phone off-canvas sidebar (≤700px) — additive (UI polish pass) ─────────
   Below ~700px the old rule only slimmed the in-flow sidebar to 170px, which
   left the content column ~166px wide (unusable). Make the sidebar an
   off-canvas drawer instead: hidden by default so content gets the full
   viewport, sliding in over the page via the header ☰ toggle base.html
   already has (html.sb-collapsed). On phones the persisted "collapsed" class
   therefore means "drawer open" — first paint is always content-first. */
@media (max-width:700px){
  nav.sidebar{
    position:fixed;left:0;top:var(--header-h,56px);bottom:0;z-index:80;
    width:min(78vw,270px);max-height:none;
    transform:translateX(-102%);transition:transform .18s ease;
    box-shadow:8px 0 30px rgba(0,0,0,.18);
    border-right:1px solid var(--border);
  }
  html.sb-collapsed nav.sidebar{display:flex;transform:none;}
}
/* Light/dark asset swap (e.g. the brand logo, which is dark ink and needs a
   light-colored twin on an OLED background). Same two triggers as the token
   swap above: explicit data-theme, or system preference with no override. */
.light-only{display:inline;}
.dark-only{display:none;}
:root[data-theme="dark"] .light-only{display:none;}
:root[data-theme="dark"] .dark-only{display:inline;}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .light-only{display:none;}
  :root:not([data-theme="light"]) .dark-only{display:inline;}
}

/* Theme toggle button (base.html header) */
.theme-toggle{background:var(--surface);color:var(--text);border:1px solid var(--border-strong);border-radius:8px;
  width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;font-size:15px;cursor:pointer;padding:0;}
.theme-toggle:hover{background:var(--card);}
.theme-toggle .icon-dark{display:none;}
:root[data-theme="dark"] .theme-toggle .icon-light{display:none;}
:root[data-theme="dark"] .theme-toggle .icon-dark{display:inline;}


/* ── Language switcher (EN | ES) ──────────────────────────────────────────
   Shared by every page shell: console header, landing nav, sign-in header and
   both mobile shells (templates/_language_switcher.html). Built off the theme
   tokens so it reads correctly in light and dark; the active side uses the
   brand orange, which carries on both backgrounds. */
.lang-switch{
  display:inline-flex; align-items:stretch; overflow:hidden;
  border:1px solid var(--border-strong); border-radius:8px;
  background:var(--card); box-shadow:var(--shadow-sm);
}
.lang-switch__opt{
  appearance:none; border:0; background:transparent; cursor:pointer;
  font:inherit; font-size:12px; font-weight:700; letter-spacing:.03em;
  line-height:1; padding:0 11px; min-height:32px; color:var(--muted);
  transition:background .12s ease, color .12s ease;
}
.lang-switch__opt + .lang-switch__opt{ border-left:1px solid var(--border); }
.lang-switch__opt:hover{ background:var(--card-2); color:var(--text); }
.lang-switch__opt.is-active{ background:var(--orange); color:#fff; }
.lang-switch__opt:focus-visible{ outline:2px solid var(--orange); outline-offset:-2px; }
/* big: the crew phone's version, full width, each language in its own words
   and tall enough to hit with a thumb. */
.lang-switch--big{ display:flex; width:100%; border-radius:14px; }
.lang-switch--big .lang-switch__opt{
  flex:1; min-height:52px; font-size:16px; letter-spacing:0; padding:0 6px;
  color:var(--text);
}
/* On the landing page the nav sits on a dark bar of its own, so the control
   has to read against that rather than against the page surface. */
.lp-nav .lang-switch{ background:rgba(255,255,255,.08); border-color:rgba(255,255,255,.28); }
.lp-nav .lang-switch__opt{ color:#e8e8ea; }
.lp-nav .lang-switch__opt:hover{ background:rgba(255,255,255,.16); color:#fff; }
.lp-nav .lang-switch__opt.is-active{ background:var(--orange); color:#fff; }

/* ── Privileged and Confidential notice ────────────────────────────────────
   Rendered at the bottom of every internal console page (see base.html and the
   two standalone shells). Deliberately quiet: it is a marking, not content, so
   it reads as a footnote under a hairline rule. Both colors are theme
   variables, so it stays legible in light and in OLED dark. */
.confidential-note{
  margin:40px 0 0; padding-top:12px;
  border-top:1px solid var(--border);
  font-size:11px; line-height:13px; font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted);
}
/* base.html's page body fills the window less the header, the .tab's top and
   bottom padding (26px each) and this notice (40 + 12 + 1 + 13 = 66px), so
   the notice sits at the bottom of the window. Content arriving over htmx
   grows into the space above it instead of pushing a visible footer down
   the screen (2026-09-26), and a short page no longer scrolls just to show
   the notice (Sam, 2026-09-28). */
.tab-body{min-height:calc(100vh - var(--header-h, 56px) - 52px - 66px);}
/* Fullscreen shells clip their overflow, so the notice pins to the bottom
   instead of sitting below the fold. It never intercepts clicks. */
.confidential-note--fixed{
  position:fixed; right:10px; bottom:8px; left:auto; margin:0;
  padding:4px 10px; border:1px solid var(--border); border-radius:6px;
  background:var(--bg); pointer-events:none;
}
/* Modal iframes are small; the notice gives back most of its breathing room. */
.confidential-note--compact{
  margin-top:20px; padding-top:8px; font-size:10px; letter-spacing:.06em;
}

/* Private-message widget (templates/pm/_widget.html): the floating
   bottom-right launcher + panel included on every signed-in page. Moved here
   from an inline <style> block so it ships via the cacheable stylesheet
   instead of being re-sent, byte-identical, on every page load. */
.pmw-launch{position:fixed;right:22px;bottom:22px;z-index:1200;display:flex;
  align-items:center;justify-content:center;width:56px;height:56px;border-radius:50%;
  background:var(--orange);color:#fff;border:none;cursor:pointer;font-size:13px;font-weight:800;
  box-shadow:var(--shadow-lg);transition:transform .12s, background .15s;}
.pmw-launch:hover{background:var(--orange-d);transform:translateY(-2px);}
.pmw-badge{position:absolute;top:-3px;right:-3px;min-width:20px;height:20px;padding:0 5px;
  border-radius:10px;background:var(--red);color:#fff;font-size:11.5px;font-weight:800;
  display:flex;align-items:center;justify-content:center;border:2px solid var(--bg);}
.pmw{position:fixed;right:22px;bottom:88px;z-index:1200;width:360px;max-width:calc(100vw - 44px);
  height:520px;max-height:calc(100vh - 130px);display:none;flex-direction:column;
  background:var(--card);border:1px solid var(--border-strong);border-radius:16px;
  box-shadow:var(--shadow-lg);overflow:hidden;}
.pmw.open{display:flex;}
.pmw-head{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:14px 16px;border-bottom:1px solid var(--border);}
.pmw-head strong{font-size:15px;color:var(--text);}
.pmw-x{background:none;border:none;color:var(--muted);font-size:20px;cursor:pointer;line-height:1;padding:2px 6px;border-radius:8px;}
.pmw-x:hover{background:var(--card-2);color:var(--text);}
.pmw-body{flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0;}

/* Contact / thread list */
.pmw-list{list-style:none;margin:0;padding:6px;}
.pmw-row{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  padding:11px 12px;border:none;background:none;border-radius:12px;cursor:pointer;}
.pmw-row:hover{background:var(--card-2);}
.pmw-av{flex:none;width:38px;height:38px;border-radius:50%;background:var(--orange-light);
  color:#fff;font-weight:800;font-size:15px;display:flex;align-items:center;justify-content:center;text-transform:uppercase;}
.pmw-av-wrap{position:relative;flex:none;}
.pmw-dot{position:absolute;right:0;bottom:0;width:11px;height:11px;border-radius:50%;
  background:var(--muted);border:2px solid var(--card);}
.pmw-dot.online{background:var(--green);}
.pmw-meta{flex:1;min-width:0;}
.pmw-name{font-weight:700;color:var(--text);font-size:14px;display:flex;align-items:center;gap:6px;}
.pmw-status{color:var(--muted);font-size:11.5px;margin-top:1px;display:block;}
.pmw-prev{color:var(--muted);font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px;}
.pmw-unread{flex:none;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:var(--red);
  color:#fff;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;}
.pmw-empty{color:var(--muted);font-size:13px;text-align:center;padding:34px 20px;}

/* One conversation */
.pmw-thread-head{display:flex;align-items:center;gap:10px;padding:11px 14px;border-bottom:1px solid var(--border);}
.pmw-back{background:none;border:none;color:var(--muted);cursor:pointer;font-size:18px;padding:2px 6px;border-radius:8px;}
.pmw-back:hover{background:var(--card-2);color:var(--text);}
.pmw-msgs{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:8px;min-height:0;}
.pmw-bub{max-width:78%;padding:9px 13px;border-radius:15px;font-size:13.5px;line-height:1.4;word-wrap:break-word;white-space:pre-wrap;}
.pmw-bub.me{align-self:flex-end;background:var(--orange);color:#fff;border-bottom-right-radius:5px;}
.pmw-bub.them{align-self:flex-start;background:var(--card-2);color:var(--text);border-bottom-left-radius:5px;}
.pmw-time{font-size:10.5px;opacity:.7;margin-top:3px;display:block;}
.pmw-attach{margin-bottom:6px;}
.pmw-attach-img{max-width:100%;max-height:160px;border-radius:10px;display:block;}
.pmw-attach-file{color:inherit;text-decoration:underline;font-size:13px;}
.pmw-compose{display:flex;align-items:flex-end;gap:8px;padding:10px;border-top:1px solid var(--border);}
.pmw-compose textarea{flex:1;resize:none;border:1px solid var(--border-strong);border-radius:12px;
  padding:9px 12px;font:inherit;font-size:13.5px;max-height:96px;background:var(--bg);color:var(--text);}
.pmw-attach-btn{flex:none;display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;
  border-radius:12px;cursor:pointer;font-size:12px;font-weight:700;white-space:nowrap;color:var(--muted);}
.pmw-attach-btn:hover{background:var(--card-2);color:var(--text);}
.pmw-attach-btn input{display:none;}
.pmw-attach-btn.has-file{color:var(--orange);}
.pmw-send{flex:none;background:var(--orange);color:#fff;border:none;border-radius:12px;padding:0 16px;height:36px;font-weight:800;cursor:pointer;}
.pmw-send:hover{background:var(--orange-d);}

/* ── Page title in the top bar ────────────────────────────────────────
   templates/base.html lifts each page's first h1 into #topbar-title and
   places it on the content's left edge; the page's "What is this screen?"
   box becomes the help tip beside it (.help-tip--page, components.css). */
header .topbar-title{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:10px;min-width:0;}
header .topbar-title[hidden]{display:none;}
header .topbar-title h1{margin:0;font-size:20px;font-weight:700;letter-spacing:-.2px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
header .topbar-title__sep{color:var(--muted);font-size:18px;font-weight:400;}
header .topbar-title__tab{font-size:18px;font-weight:600;color:var(--soft);white-space:nowrap;}
/* A detail page's title with the pages above it (.page-crumb links in its
   h1): the parents read softer than the page itself. */
.page-crumb{color:var(--soft);font-weight:600;text-decoration:none;}
.page-crumb:hover{color:var(--text);text-decoration:underline;}
.page-crumb__sep{margin:0 10px;color:var(--muted);font-weight:400;font-size:.9em;}
/* The card sits inside <header>, whose own p style is a small uppercase
   caption; the explanation is body text. */
header .help-tip__card p,header .help-tip__card li{font-size:12.5px;text-transform:none;letter-spacing:normal;font-weight:400;color:var(--text);margin-top:0;}

/* ── Count badges (apps.core.nav_badges) ─────────────────────────────────────
   Approvals and Communications: a pill at the end of their sidebar rows and
   an icon in the top bar with the number on its corner, the same numbers the
   office's phone drawer shows. Filled after the page paints; hidden at zero. */
.nav-count{margin-left:auto;min-width:18px;height:18px;padding:0 5px;border-radius:99px;
  background:var(--orange);color:#fff;font-size:11px;font-weight:800;line-height:18px;
  text-align:center;font-variant-numeric:tabular-nums;}
.nav-count[hidden]{display:none;}
.topbar-badge{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:10px;color:var(--soft);text-decoration:none;flex:none;}
.topbar-badge:hover{background:rgba(255,107,26,.08);color:var(--orange);}
.topbar-badge svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;}
.topbar-badge__n{position:absolute;top:1px;right:0;min-width:17px;height:17px;padding:0 4px;
  border-radius:99px;background:var(--orange);color:#fff;font-size:10.5px;font-weight:800;
  line-height:17px;text-align:center;font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 2px var(--surface);}
.topbar-badge__n[hidden]{display:none;}
