/* Hephaestus — the stylesheet.
 *
 * One document. Every page links it; no page carries CSS of its own.
 *
 * It was three: the console, the portal and the sign-in pages each had an
 * inline <style> block with its own palette, and the three disagreed --
 * different blues, different greens, --ok in one and --good in another. They
 * also each redefined body, h1, .panel, .btn, label, input, .err and .pill
 * differently, which is why merging them meant scoping rather than
 * concatenating.
 *
 * Structure:
 *   1. Tokens         one palette, light and dark
 *   2. Shared         reset and the few things every page agrees on
 *   3. body.app       the technician console
 *   4. body.portal    what a requester sees
 *   5. body.auth      sign in, two-factor, password reset
 *
 * Each area is scoped by the class on <body>, so a rule written for one
 * cannot reach into another. Anything genuinely shared belongs in section 2,
 * not copied into two places.
 */

/* ---------------------------------------------------------------- 1. tokens
 * The palette every surface draws from. Defined on bare :root so it applies
 * whatever the theme, then redefined for dark. Never give a colour its only
 * definition inside the dark block.
 */
:root{
  --bg:#F2F4F6;--panel:#fff;--panel-2:#F8F9FB;--ink:#131820;--ink-2:#414B5A;--muted:#6B7684;
  --line:#DBE0E6;--line-2:#EDF0F3;--primary:#2563EB;--primary-soft:#E6EDFD;
  --good:#059669;--warn:#D97706;--crit:#DC2626;--ember:#C2410C;
  /* The two notification colours the rest of the interface had no use for.
     Gold is VIP, tangerine is "the requester replied". */
  --gold:#96780F;--tangerine:#B85F09;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
}
@media (prefers-color-scheme:dark){:root{
  color-scheme:dark;
  --bg:#0E1116;--panel:#161A20;--panel-2:#1B2028;--ink:#E7EBF0;--ink-2:#B4BDC9;--muted:#8B95A3;
  --line:#252B34;--line-2:#1E242C;--primary:#3B82F6;--primary-soft:#16233B;
  --good:#10B981;--warn:#F59E0B;--crit:#EF4444;--ember:#F97316;
  --gold:#E3B341;--tangerine:#F08A3C;
}}

/* ---------------------------------------------------------------- 2. shared */
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 var(--sans);
  -webkit-font-smoothing:antialiased}
a{color:var(--primary)}
:focus-visible{outline:2px solid var(--primary);outline-offset:2px}

/* -------------------------------------------- 3. console  (body.app)
 * The technician's side: queue, ticket detail, setup, notifications.
 */

body.app{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 var(--sans);-webkit-font-smoothing:antialiased}
.app a{color:var(--primary)}
.app .wrap{max-width:1200px;margin:0 auto;padding:0 16px 48px}
.app header.top{background:var(--bg);border-bottom:1px solid var(--line);padding:14px 0 0}
.app .brandrow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.app .mark{width:26px;height:26px;flex:none}
.app .brand{font-weight:700;font-size:18px;letter-spacing:-.02em}
.app .brand span{color:var(--muted);font-weight:500}
.app .tenants{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap}
.app .tenants a{font-size:12.5px;padding:6px 11px;border:1px solid var(--line);border-radius:999px;
  background:var(--panel);color:var(--ink-2);text-decoration:none}
.app .tenants a.on{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.app .me{display:flex;align-items:center;gap:10px;font-size:12.5px;color:var(--muted);
  padding-left:12px;margin-left:2px;border-left:1px solid var(--line)}
.app .me a{color:var(--ink-2);text-decoration:none}
.app .me a:hover{text-decoration:underline}
.app .me form{margin:0}
.app .me button{font:12.5px var(--sans);background:none;border:0;color:var(--ink-2);cursor:pointer;padding:0}
.app .me button:hover{text-decoration:underline}
@media(max-width:700px){.app .me{border-left:0;padding-left:0;width:100%;margin-top:8px}}
.app .tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:20px 0}
.app .tile{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:13px 14px}
.app .tile .k{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:600}
.app .tile .v{font-size:25px;font-weight:700;letter-spacing:-.025em;margin-top:5px;font-variant-numeric:tabular-nums}
.app .tile.warn .v{color:var(--warn)} .app .tile.crit .v{color:var(--crit)}
.app .panel{background:var(--panel);border:1px solid var(--line);border-radius:8px;overflow:hidden}
.app .ph{padding:12px 15px;border-bottom:1px solid var(--line-2);display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.app .ph h2{font-size:14.5px;margin:0;font-weight:600}
.app .ph .sub{font-size:12px;color:var(--muted)}
.app .ph form{margin-left:auto;display:flex;gap:8px;align-items:center}
.app input[type=search]{font:13px var(--sans);padding:6px 10px;border:1px solid var(--line);border-radius:6px;
  background:var(--panel-2);color:var(--ink);min-width:180px}
.app label.chk{font-size:12.5px;color:var(--ink-2);display:flex;gap:5px;align-items:center;cursor:pointer;white-space:nowrap}
.app .tscroll{overflow-x:auto}
/* table-layout:fixed so the browser honours these widths instead of sizing
   every column to its longest value. Before this, one long category name
   pushed Assignee and everything after it off the right edge. */
.app table{border-collapse:collapse;width:100%;font-size:12.5px;min-width:940px;table-layout:fixed}
.app td, .app th{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.app td.subj, .app th.subj{white-space:normal;line-height:1.35}
/* Widths tuned against real values, not guessed: a 4-digit ticket number in
   the mono face, the longest status and SLA pills, and an ISO date. The two
   identity columns and the category are the ones allowed to truncate,
   because they carry a title attribute and the full value is one hover away. */
.app col.c-num{width:66px} .app col.c-subj{width:auto} .app col.c-status{width:112px}
.app col.c-sla{width:132px} .app col.c-cat{width:128px} .app col.c-who{width:116px}
.app col.c-mins{width:64px} .app col.c-n{width:56px} .app col.c-date{width:100px}
/* The number and the pills must never be cut: a half-shown ticket number is
   useless and a clipped pill misreads as a different status. */
.app td.mono:first-child, .app th.mono:first-child{overflow:visible}
.app td .pill{overflow:visible}
.app th{text-align:left;font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  font-weight:600;padding:9px 12px;border-bottom:1px solid var(--line);white-space:nowrap}
.app td{padding:9px 12px;border-bottom:1px solid var(--line-2);vertical-align:top}
.app tbody tr:hover{background:var(--panel-2)}
.app td.mono, .app th.mono{font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.app td.sub{color:var(--muted)}
.app td a{text-decoration:none;font-weight:500}
.app td a:hover{text-decoration:underline}
.app .pill{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:600;letter-spacing:.03em;
  text-transform:uppercase;padding:3px 8px;border-radius:4px;white-space:nowrap}
.app .pill::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}
.app .u-late{color:var(--crit);background:color-mix(in srgb,var(--crit) 11%,transparent)}
.app .u-due-today{color:var(--warn);background:color-mix(in srgb,var(--warn) 13%,transparent)}
.app .u-scheduled{color:var(--good);background:color-mix(in srgb,var(--good) 12%,transparent)}
.app .u-unscheduled{color:var(--muted);background:color-mix(in srgb,var(--muted) 12%,transparent)}
.app .s-open, .app .s-new, .app .s-in_progress{color:var(--primary);background:var(--primary-soft)}
.app .s-waiting_customer, .app .s-waiting_vendor{color:var(--warn);background:color-mix(in srgb,var(--warn) 13%,transparent)}
.app .s-closed, .app .s-cancelled{color:var(--muted);background:color-mix(in srgb,var(--muted) 12%,transparent)}
.app .s-resolved{color:var(--good);background:color-mix(in srgb,var(--good) 12%,transparent)}
.app .foot{padding:11px 15px;display:flex;gap:12px;align-items:center;font-size:12.5px;color:var(--muted);
  border-top:1px solid var(--line-2)}
.app .foot a{text-decoration:none}
.app .btn{font:13px var(--sans);padding:6px 12px;border:1px solid var(--line);border-radius:6px;
  background:var(--panel);color:var(--ink-2);text-decoration:none;cursor:pointer}
.app .chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:12px 0 0}
.app .chips .lbl{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);
  font-weight:600;margin-right:2px;min-width:58px}
.app .chips a{font-size:12.5px;color:var(--ink-2);background:var(--panel);border:1px solid var(--line);
  border-radius:999px;padding:4px 11px;text-decoration:none;white-space:nowrap}
.app .chips a:hover{border-color:var(--muted)}
.app .chips a.on{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.app .chips a b{font-variant-numeric:tabular-nums;font-weight:600;margin-left:5px;opacity:.65}
/* A status with nothing in it under the current filter stays visible but
   recedes: you still need to see it is there in order to click it. */
.app .chips a.zero{opacity:.5}
.app .pager{margin-left:auto;display:flex;gap:7px}
.app td.empty{color:var(--muted);padding:26px 12px;text-align:center;white-space:normal}
.app td.empty a{margin-left:8px}
/* A ticket sitting in a team's queue, unowned: legible as a state rather
   than as a person's name. */
.app td.q{color:var(--muted);font-style:italic}
.app .sections{display:flex;gap:2px;margin:10px 0 0}
.app .sections a{font-size:13px;color:var(--ink-2);padding:7px 14px;border-radius:7px 7px 0 0;
  border:1px solid transparent;border-bottom:none}
.app .sections a:hover{color:var(--ink)}
.app .sections a.on{background:var(--panel);border-color:var(--line);color:var(--ink);font-weight:600}
/* --- setup page --- */
.app .block{padding:13px 15px;border-top:1px solid var(--line-2)}
.app .block h3{font-size:14px;margin:0 0 3px;display:flex;align-items:baseline;gap:10px}
.app .block h3 .sub{font-weight:400}
.app .nextup{margin-left:auto;font-size:12px;color:var(--primary);font-weight:600}
.app table.mini{width:100%;border-collapse:collapse;font-size:13px}
.app table.mini th{text-align:left;font-size:10px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);font-weight:600;padding:7px 10px;border-bottom:1px solid var(--line-2)}
.app table.mini td{padding:7px 10px;border-bottom:1px solid var(--line-2);vertical-align:top}
.app table.mini tr:last-child td{border-bottom:none}
.app table.mini tr.off{opacity:.55}
.app .empty-note{padding:13px 15px;color:var(--muted);font-size:13px;margin:0}
/* --- notifications --- */
.app .bellwrap{position:relative}
.app .bellbtn{background:none;border:none;cursor:pointer;font-size:15px;padding:2px 4px;
  color:var(--ink-2);display:flex;align-items:center;gap:4px}
.app .bellbtn:hover{color:var(--ink)}
/* Reads "2/2" when two are new, "0/2" once they have been looked at: how
   much is new, over how much is there. Red only while something is new. */
.app #bellcount{font:11px var(--mono);font-weight:600;font-variant-numeric:tabular-nums;
  height:17px;border-radius:9px;display:none;align-items:center;justify-content:center;
  padding:0 6px;background:var(--line);color:var(--ink-2)}
.app #bellcount.on{display:inline-flex}
.app #bellcount.hot{background:var(--crit);color:#fff}
.app .bellpanel{position:absolute;right:0;top:8px;width:340px;z-index:60;background:var(--panel);
  border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 34px rgba(0,0,0,.20);
  overflow:hidden}
.app .bellhead{display:flex;align-items:center;gap:8px;padding:10px 12px;
  border-bottom:1px solid var(--line-2);font-size:13px}
.app .bellhead strong{margin-right:auto}
.app .bellclose{background:none;border:none;color:var(--muted);cursor:pointer;font-size:17px;
  line-height:1;padding:0 3px}
.app .bellclose:hover{color:var(--ink)}
.app .bellhead form{margin:0}
.app .bellempty{padding:22px 12px;text-align:center;color:var(--muted);font-size:13px;margin:0}
.app .belllist{list-style:none;margin:0;padding:0;max-height:min(60vh,420px);overflow:auto}
.app .belllist li{display:flex;align-items:stretch;gap:4px;
  border-bottom:1px solid var(--line-2)}
.app .belllist li:last-child{border-bottom:none}
.app .belllist li form{margin:0;display:flex}
.app .rowform{flex:1;min-width:0}
/* The whole row is the acknowledge control: click it and the colour comes
   off. The arrow beside it opens the ticket. */
.app .nrow{flex:1;min-width:0;display:block;text-align:left;background:none;border:none;
  cursor:pointer;padding:9px 4px 9px 12px;font:inherit;color:inherit}
.app .nrow:hover{background:var(--panel-2)}
.app .ntitle{display:block;font-size:13px;font-weight:600;color:var(--c);line-height:1.35}
.app .nbody{display:block;font-size:11.5px;color:var(--muted);margin-top:1px}
.app .nfoot{display:flex;align-items:center;gap:6px;margin-top:4px}
.app .nbell{width:13px;height:13px;flex:none;color:var(--c)}
.app .nmeta{font-size:11px;color:var(--muted);letter-spacing:.02em}
/* Read: the text goes quiet, the entry and its bell stay. */
.app .belllist li.done .ntitle{color:var(--ink-2);font-weight:500}
.app .belllist li.done .nbell{opacity:.55}
.app .nacts{display:flex;align-items:center;gap:3px;padding:0 9px 0 0}
.app .nacts .mini-btn{padding:2px 6px}
/* The colours the owner specified. They carry the meaning, so they are the
   one part of this that must not drift. */
.app .n-green{--c:var(--good)}
.app .n-gold{--c:var(--gold)}
.app .n-orange{--c:var(--tangerine)}
.app .n-blue{--c:var(--primary)}
.app .n-yellow{--c:var(--warn)}
.app .n-red{--c:var(--crit)}
.app .toastrail{position:fixed;top:14px;right:16px;z-index:80;display:flex;flex-direction:column;
  gap:9px;width:330px;max-width:calc(100vw - 32px);pointer-events:none}
.app .toast{pointer-events:auto;display:flex;gap:9px;align-items:flex-start;background:var(--panel);
  border:1px solid var(--line);border-left:5px solid var(--line);border-radius:9px;
  padding:10px 11px;box-shadow:0 10px 30px rgba(0,0,0,.18);
  animation:toastin .18s ease-out, toastout .5s ease-in 9s forwards}
@keyframes toastin{from{opacity:0;transform:translateX(14px)}to{opacity:1;transform:none}}
@keyframes toastout{to{opacity:0;transform:translateX(14px);visibility:hidden}}
@media(prefers-reduced-motion:reduce){.app .toast{animation:none}}
.app .toast .bell{font-size:14px;line-height:1.3}
.app .toast .tbody{flex:1;min-width:0}
.app .ttitle{display:block;font-size:13px;font-weight:600;color:var(--ink);text-decoration:none}
.app .ttitle:hover{text-decoration:underline}
.app .tsub{font-size:12px;color:var(--ink-2);margin-top:1px}
.app .tmeta{font-size:11px;color:var(--muted);margin-top:3px}
.app .tclose{background:none;border:none;color:var(--muted);cursor:pointer;font-size:16px;
  line-height:1;padding:0 2px}
.app .tclose:hover{color:var(--ink)}
.app .t-green{border-left-color:var(--good)}
.app .t-gold{border-left-color:var(--gold)}
.app .t-orange{border-left-color:var(--tangerine)}
.app .t-blue{border-left-color:var(--primary)}
.app .t-yellow{border-left-color:var(--warn)}
.app .t-red{border-left-color:var(--crit)}
.app .rowacts{white-space:nowrap;text-align:right}
.app .rowacts form{display:inline-flex;gap:3px}
.app .mini-btn{font:12px var(--sans);padding:3px 7px;border:1px solid var(--line);border-radius:5px;
  background:var(--panel);color:var(--ink-2);cursor:pointer;text-decoration:none;display:inline-block}
.app .mini-btn:hover{border-color:var(--muted);color:var(--ink)}
.app .mini-btn.danger:hover{border-color:var(--crit);color:var(--crit)}
.app .editrow{display:flex;align-items:center;gap:12px;padding:9px 0;
  border-top:1px solid var(--line-2)}
.app .editrow:first-of-type{margin-top:8px}
.app .rowlbl{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);
  font-weight:600;min-width:104px}
.app form.inline{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.app form.inline input[type=text], .app form.inline select{width:auto;font:13px var(--sans);padding:6px 9px}
.app .tierform select{width:auto;padding:3px 6px;font:12px var(--sans)}
.app h3.fsec{font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);
  margin:18px 15px 0;padding-top:13px;border-top:1px solid var(--line-2)}
.app .rulewarn{font-size:12px;color:var(--warn);margin-top:3px}
.app .warnnote{margin:0;padding:11px 15px;font-size:12.5px;color:var(--warn);
  border-top:1px solid var(--line-2)}
.app code{font:12.5px var(--mono);background:var(--panel-2);padding:1px 5px;border-radius:4px}
.app .cats a{max-width:210px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The chip row shows the busiest categories; this select reaches the rest.
   Sized to the widest name a tenant is likely to have without letting one
   long one push the search box off the header. */
.app .ph select{width:auto;max-width:190px;font:13px var(--sans);padding:6px 8px;
  border:1px solid var(--line);border-radius:6px;background:var(--panel);color:var(--ink)}
.app .thread{display:flex;flex-direction:column;gap:12px;padding:15px}
.app .cmt{border:1px solid var(--line-2);border-radius:7px;padding:11px 13px;background:var(--panel-2)}
.app .cmt.inbound{background:var(--panel);border-left:3px solid var(--primary)}
.app .cmt .meta{font-size:11.5px;color:var(--muted);margin-bottom:6px;display:flex;gap:8px;flex-wrap:wrap}
.app .cmt .body{white-space:pre-wrap;word-break:break-word;font-size:13px;color:var(--ink-2);max-width:78ch}
.app .kv{display:grid;grid-template-columns:auto 1fr;gap:7px 16px;padding:15px;font-size:13px;align-items:baseline}
.app .kv dt{color:var(--muted);font-size:11px;letter-spacing:.06em;text-transform:uppercase;font-weight:600}
.app .kv dd{margin:0;font-variant-numeric:tabular-nums}
.app .grid-detail{display:grid;grid-template-columns:1fr 300px;gap:14px;align-items:start}
@media(max-width:820px){.app .grid-detail{grid-template-columns:1fr}}
.app h1.subject{font-size:20px;margin:0;letter-spacing:-.02em;text-wrap:balance}
.app .crumb{font-size:12.5px;color:var(--muted);margin:18px 0 8px}
.app .crumb a{text-decoration:none}
.app .note{font-size:12px;color:var(--muted);margin-top:14px;max-width:70ch}
.app .htmx-request tbody{opacity:.5;transition:opacity .1s}
/* --- forms --- */
.app label{display:block;font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  font-weight:600;margin-bottom:5px}
.app input[type=text], .app input[type=number], .app select, .app textarea{width:100%;font:13.5px var(--sans);padding:8px 10px;
  border:1px solid var(--line);border-radius:6px;background:var(--panel-2);color:var(--ink)}
.app textarea{resize:vertical;min-height:80px;line-height:1.55}
.app input:focus, .app select:focus, .app textarea:focus{outline:2px solid var(--primary);outline-offset:1px;border-color:transparent}
.app .formgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:15px}
.app .formgrid .wide{grid-column:1/-1}
@media(max-width:700px){.app .formgrid{grid-template-columns:1fr}}
.app .formfoot{display:flex;gap:10px;align-items:center;justify-content:flex-end;
  padding:13px 15px;border-top:1px solid var(--line-2)}
.app .formfoot .chk{margin-right:auto;text-transform:none;letter-spacing:0;font-size:12.5px;font-weight:400}
.app .btn.primary{background:var(--primary);color:#fff;border-color:var(--primary);font-weight:600}
.app .btn.primary:hover{filter:brightness(1.06)}
.app .err{background:color-mix(in srgb,var(--crit) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--crit) 35%,transparent);
  color:var(--crit);font-size:13px;padding:10px 12px;border-radius:7px;margin:0 0 14px}
/* --- activity --- */
.app .cmt.internal{background:color-mix(in srgb,var(--warn) 7%,var(--panel-2));
  border-left:3px solid var(--warn)}
.app .tag-int{color:var(--warn);font-weight:600}
.app .chg{display:flex;gap:9px;align-items:baseline;font-size:12.5px;color:var(--ink-2);padding:2px 2px 2px 4px}
.app .chg .dot{width:6px;height:6px;border-radius:50%;background:var(--line);flex:none;translate:0 -1px}
.app .chg .when{margin-left:auto;color:var(--muted);font-family:var(--mono);font-size:11.5px;white-space:nowrap}
.app .newbtn{margin-left:auto}
.app .files{list-style:none;margin:0;padding:6px 15px 13px}
.app .files li{padding:7px 0;border-bottom:1px solid var(--line-2);display:flex;flex-wrap:wrap;
  gap:4px 10px;align-items:baseline}
.app .files li:last-child{border-bottom:0}
.app .files a{font-size:13px;font-weight:500;text-decoration:none}
.app .files a:hover{text-decoration:underline}
.app .files .meta{font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.app input[type=file]{width:100%;font:13px var(--sans);color:var(--ink-2)}
.app input[type=file]::file-selector-button{font:13px var(--sans);padding:6px 11px;margin-right:10px;
  border:1px solid var(--line);border-radius:6px;background:var(--panel-2);color:var(--ink-2);cursor:pointer}
.app .hint{margin-right:auto;font-size:12px;color:var(--muted)}

/* ------------------------------------------ 4. portal  (body.portal)
 * What a requester sees. Larger type and more air: it is used occasionally
 * by people who do not live in it.
 */
body.portal{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 var(--sans)}
.portal a{color:var(--primary);text-decoration:none}
.portal a:hover{text-decoration:underline}
.portal .wrap{max-width:780px;margin:0 auto;padding:0 20px 56px}
.portal header.top{background:var(--panel);border-bottom:1px solid var(--line);margin-bottom:26px}
.portal header .wrap{display:flex;align-items:center;gap:14px;padding-top:16px;padding-bottom:16px}
.portal .brand{font-weight:700;font-size:17px;letter-spacing:-.01em}
.portal .brand span{font-weight:400;color:var(--muted)}
.portal header .me{margin-left:auto;display:flex;align-items:center;gap:12px;font-size:13px;
  color:var(--muted)}
.portal header .me form{margin:0}
.portal header .me button{font:13px var(--sans);background:none;border:none;color:var(--primary);
  cursor:pointer;padding:0}
.portal h1{font-size:23px;letter-spacing:-.015em;margin:0 0 4px}
.portal h1+.sub{margin:0 0 22px}
.portal .sub{color:var(--muted);font-size:13.5px}
.portal .panel{background:var(--panel);border:1px solid var(--line);border-radius:11px;
  margin-bottom:18px;overflow:hidden}
.portal .panel .ph{display:flex;align-items:center;gap:12px;padding:14px 18px;
  border-bottom:1px solid var(--line-2)}
.portal .panel h2{font-size:15px;margin:0}
.portal .btn{display:inline-block;font:14px var(--sans);padding:9px 16px;border-radius:8px;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer;
  text-decoration:none}
.portal .btn:hover{border-color:var(--muted);text-decoration:none}
.portal .btn.primary{background:var(--primary);border-color:var(--primary);color:#fff;font-weight:600}
.portal .btn.primary:hover{filter:brightness(1.07)}
.portal .btn.big{font-size:15px;padding:11px 20px}
.portal label{display:block;font-size:12.5px;font-weight:600;color:var(--ink-2);margin:0 0 5px}
.portal input[type=text], .portal textarea, .portal select, .portal input[type=file]{width:100%;font:15px var(--sans);
  padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:var(--panel-2);
  color:var(--ink)}
.portal textarea{min-height:150px;resize:vertical;line-height:1.55}
.portal input:focus, .portal textarea:focus, .portal select:focus{outline:2px solid var(--primary);outline-offset:1px;
  border-color:transparent}
.portal .field{padding:0 18px 16px}
.portal .field .hint{font-size:12.5px;color:var(--muted);margin:5px 0 0}
.portal .formfoot{display:flex;gap:10px;align-items:center;justify-content:flex-end;
  padding:14px 18px;border-top:1px solid var(--line-2);background:var(--panel-2)}
.portal .err{background:color-mix(in srgb,var(--crit) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--crit) 35%,transparent);color:var(--crit);
  font-size:14px;padding:11px 14px;border-radius:8px;margin:0 18px 16px}
.portal .tlist{list-style:none;margin:0;padding:0}
.portal .tlist li{border-bottom:1px solid var(--line-2)}
.portal .tlist li:last-child{border-bottom:none}
.portal .tlist a.row{display:block;padding:14px 18px;color:inherit}
.portal .tlist a.row:hover{background:var(--panel-2);text-decoration:none}
.portal .tlist .subj{font-weight:600;font-size:14.5px}
.portal .tlist .meta{color:var(--muted);font-size:12.5px;margin-top:3px}
.portal .pill{display:inline-block;font-size:11px;font-weight:600;padding:2px 9px;border-radius:999px;
  border:1px solid var(--line);color:var(--ink-2);background:var(--panel-2);vertical-align:1px}
.portal .pill.act{border-color:color-mix(in srgb,var(--warn) 45%,transparent);color:var(--warn);
  background:color-mix(in srgb,var(--warn) 10%,transparent)}
.portal .pill.done{border-color:color-mix(in srgb,var(--good) 40%,transparent);color:var(--good);
  background:color-mix(in srgb,var(--good) 10%,transparent)}
.portal .empty{padding:34px 18px;text-align:center;color:var(--muted)}
.portal .thread{display:flex;flex-direction:column;gap:13px;padding:18px}
.portal .msg{border:1px solid var(--line-2);border-radius:9px;padding:12px 14px;background:var(--panel-2)}
.portal .msg.mine{background:color-mix(in srgb,var(--primary) 7%,var(--panel));
  border-color:color-mix(in srgb,var(--primary) 22%,transparent)}
.portal .msg .who{font-size:12.5px;color:var(--muted);margin-bottom:5px}
.portal .msg .body{white-space:pre-wrap;font-size:14.5px}
.portal dl.facts{display:grid;grid-template-columns:auto 1fr;gap:7px 16px;margin:0;padding:16px 18px;
  font-size:13.5px}
.portal dl.facts dt{color:var(--muted)}
.portal dl.facts dd{margin:0}
.portal .files{padding:0 18px 16px;font-size:13.5px}
.portal .files a{margin-right:14px}
@media(max-width:620px){.portal .wrap{padding:0 14px 40px}.portal header .wrap{padding-top:13px}}

/* ------------------------------------------- 5. sign-in  (body.auth)
 * One centred card. Shared by login, two-factor, enrolment and reset.
 */

body.auth{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 var(--sans);
  min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px 16px}
.auth .card{background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:26px 26px 24px;width:100%;max-width:392px;box-shadow:0 1px 3px rgba(0,0,0,.06)}
.auth .brandrow{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.auth .mark{width:26px;height:26px;flex:none}
.auth .brand{font-weight:700;font-size:17px;letter-spacing:-.02em}
.auth .brand span{color:var(--muted);font-weight:500}
.auth h1{font-size:17px;margin:16px 0 4px;letter-spacing:-.015em}
.auth p.lede{color:var(--muted);font-size:13px;margin:0 0 18px;max-width:46ch}
.auth label{display:block;font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  font-weight:600;margin:0 0 5px}
.auth input[type=text], .auth input[type=email], .auth input[type=password]{width:100%;font:14px var(--sans);
  padding:9px 11px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
.auth input:focus{outline:2px solid var(--primary);outline-offset:1px;border-color:transparent}
.auth .field{margin-bottom:14px}
.auth button{width:100%;font:600 14px var(--sans);padding:10px;border:0;border-radius:7px;
  background:var(--primary);color:#fff;cursor:pointer}
.auth button:hover{filter:brightness(1.06)}
.auth button:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.auth .err{background:color-mix(in srgb,var(--crit) 10%,transparent);border:1px solid color-mix(in srgb,var(--crit) 35%,transparent);
  color:var(--crit);font-size:13px;padding:9px 11px;border-radius:7px;margin-bottom:16px}
.auth .ok{background:color-mix(in srgb,var(--good) 10%,transparent);border:1px solid color-mix(in srgb,var(--good) 35%,transparent);
  color:var(--good);font-size:13px;padding:9px 11px;border-radius:7px;margin-bottom:16px}
.auth .code-input{font-family:var(--mono);font-size:22px;letter-spacing:.34em;text-align:center}
/* The QR keeps its own white card in both themes: scanners want dark modules
   on light, and an inverted code fails on enough phones to matter. */
.auth .qr{display:flex;justify-content:center;margin:2px 0 16px}
.auth .qr svg{display:block;background:#fff;padding:10px;border-radius:8px;
  box-shadow:0 0 0 1px var(--line)}
.auth .manual{margin-bottom:4px;border:1px solid var(--line);border-radius:8px;padding:10px 12px}
.auth .manual summary{font-size:12.5px;color:var(--ink-2);cursor:pointer;list-style:none}
.auth .manual summary::-webkit-details-marker{display:none}
.auth .manual summary::before{content:"▸ ";color:var(--muted)}
.auth .manual[open] summary::before{content:"▾ "}
.auth .manual p{font-size:12.5px;color:var(--muted);margin:10px 0 8px}
.auth .secret{font-family:var(--mono);font-size:13.5px;letter-spacing:.09em;word-break:break-all;
  background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:9px 11px;
  -webkit-user-select:all;user-select:all}
.auth .copy{width:auto;margin-top:9px;padding:6px 12px;font-size:12.5px;font-weight:500;
  background:var(--bg);color:var(--ink-2);border:1px solid var(--line);border-radius:6px}
.auth .copy:hover{filter:none;background:var(--panel)}
.auth .recovery{display:grid;grid-template-columns:1fr 1fr;gap:6px 14px;font-family:var(--mono);
  font-size:13px;background:var(--bg);border:1px solid var(--line);border-radius:8px;
  padding:13px 15px;margin-bottom:16px}
.auth .foot{margin-top:16px;font-size:12px;color:var(--muted);text-align:center}
.auth .foot a{color:var(--primary)}
