/* ============================================================================
   ui.css -- the rebuilt UI layer (task #144).
   Direction "Console": dense display text, monospace for copy-pasteable
   identifiers, flat surfaces, small radii.

   RULES THIS FILE MUST OBEY (enforced by tests/test_ui_tokens.py):
     1. No raw hex colour outside the :root blocks. Components use var(--token).
     2. The light block defines exactly the same token NAMES as the dark block.
     3. --control-font is 16px: iOS Safari zooms a focused control below 16px and
        never zooms back out. Density applies to display text, never to controls.
     4. --tap is the minimum size of anything tappable.

   Never loaded alongside app.css. base.html -> app.css; shell.html -> ui.css.
   app.css styles bare elements (body, table, input), so two sheets on one page
   bleed into each other in both directions -- see the spec's coexistence rule.
   ========================================================================== */

:root {
  /* colour -- dark, from the approved direction-A mockup */
  --bg: #0b0d10;
  --surface: #11151b;
  --surface-2: #161b22;
  --border: #1e242e;
  --border-dim: #1a1f27;
  --text: #dfe4ec;
  --text-dim: #8b93a7;
  --text-faint: #5c6473;
  --accent: #6fa8ff;
  --accent-soft: rgba(111, 168, 255, 0.12);
  --on-accent: #0b0d10;
  --brand: #5ad1a0;
  --ok: #5ad1a0;
  --warn: #d9a23a;
  --bad: #e5534b;
  --info: #6fa8ff;
  --ok-soft: rgba(90, 209, 160, 0.10);
  --warn-soft: rgba(217, 162, 58, 0.10);
  --bad-soft: rgba(229, 83, 75, 0.10);
  --p0: #ff4d4f;
  --p1: #e5534b;
  --p2: #d9a23a;
  --p3: #6b7280;

  /* type */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --t-xs: 11px;
  --t-sm: 12.5px;
  --t-base: 14px;
  --t-md: 15.5px;
  --t-lg: 18px;
  --t-xl: 22px;

  /* hard floors -- not stylistic. see rules 3 and 4 above. */
  --control-font: 16px;
  --tap: 44px;

  /* space */
  --s1: 4px; --s2: 6px; --s3: 8px; --s4: 11px; --s5: 14px; --s6: 18px; --s7: 24px;

  /* radius + chrome */
  --r: 4px;
  --r-lg: 5px;
  --topbar-h: 52px;
  --tabbar-h: 54px;
}

/* The light block repeats the non-colour tokens verbatim on purpose: the parity
   test compares NAME SETS, and repeating them keeps the two blocks mechanically
   comparable instead of relying on a reader to remember which tokens are
   theme-independent. Surfaces inverse-ramp (--bg is the lightest and --surface
   steps down) so elevation still reads, and the state hues darken rather than
   carry over -- the dark values fail contrast on a light surface. */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f8fa;
    --surface: #ffffff;
    --surface-2: #f0f2f5;
    --border: #d8dce3;
    --border-dim: #e4e7ec;
    --text: #1b1f27;
    --text-dim: #5b6472;
    --text-faint: #858e9c;
    --accent: #1f5fd0;
    --accent-soft: rgba(31, 95, 208, 0.08);
    --on-accent: #ffffff;
    --brand: #0f8f63;
    --ok: #0f8f63;
    --warn: #8a5a12;
    --bad: #b42318;
    --info: #1f5fd0;
    --ok-soft: rgba(15, 143, 99, 0.08);
    --warn-soft: rgba(138, 90, 18, 0.08);
    --bad-soft: rgba(180, 35, 24, 0.07);
    --p0: #9e1b16;
    --p1: #b42318;
    --p2: #8a5a12;
    --p3: #5b6472;

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --t-xs: 11px;
    --t-sm: 12.5px;
    --t-base: 14px;
    --t-md: 15.5px;
    --t-lg: 18px;
    --t-xl: 22px;
    --control-font: 16px;
    --tap: 44px;
    --s1: 4px; --s2: 6px; --s3: 8px; --s4: 11px; --s5: 14px; --s6: 18px; --s7: 24px;
    --r: 4px;
    --r-lg: 5px;
    --topbar-h: 52px;
    --tabbar-h: 54px;
  }
}

/* ===== reset ================================================================ */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font: var(--t-base)/1.45 var(--font);
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: var(--t-xl); line-height: 1.2; margin: 0; letter-spacing: -0.01em; }
h2 { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.14em;
     color: var(--text-dim); font-weight: 800; margin: var(--s7) 0 var(--s4); }
code, .mono { font-family: var(--font-mono); }
/* long unbreakable identifiers -- refs, SHAs, hostnames, secret names -- are
   everywhere in this app. `anywhere` (not break-word) also shrinks min-content
   size, which is what a grid or flex track measures. Learned in task #143. */
body { overflow-wrap: anywhere; }

/* ===== topbar =============================================================== */
.topbar {
  /* min-height, not height: the nav wraps to a second row on narrow laptops and a
     fixed height would let that row escape the bar. This is why the legacy header
     overflows instead (task #146) -- it neither wraps nor grows. */
  display: flex; align-items: center; gap: var(--s6); flex-wrap: wrap;
  min-height: var(--topbar-h); padding: var(--s2) var(--s5);
  background: var(--surface); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}
.brand { font-weight: 800; font-size: var(--t-md); letter-spacing: -0.01em;
         color: var(--text); white-space: nowrap; }
.brand i { color: var(--brand); font-style: normal; }
/* min-width: 0 so the nav can shrink instead of widening the document -- the
   legacy header overflows every viewport from 821px to ~1713px for exactly this
   reason (task #146), and flex-wrap means it falls to a second row instead. */
.navdesk { display: flex; align-items: center; gap: var(--s1); flex: 1;
           min-width: 0; flex-wrap: wrap; }
.navdesk a { color: var(--text-dim); padding: var(--s2) var(--s4); border-radius: var(--r);
             white-space: nowrap; font-size: var(--t-sm); }
.navdesk a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.navdesk a.on { background: var(--surface-2); color: var(--text); }
.navgroup { font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 700;
            letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint);
            padding: 0 var(--s2) 0 var(--s3); user-select: none; }
.topbar .right { margin-left: auto; display: flex; align-items: center; gap: var(--s3); }
.bell { position: relative; color: var(--text-dim);
        min-width: var(--tap); min-height: var(--tap);
        display: inline-flex; align-items: center; justify-content: center; }
.who { color: var(--text-dim); font-size: var(--t-sm); white-space: nowrap;
       padding: var(--s2) var(--s3); border-radius: var(--r); }
.who:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }

/* ===== page frame =========================================================== */
.container { max-width: 1080px; margin: 0 auto; padding: var(--s6) var(--s5) var(--s7); }
.pagehead { display: flex; align-items: flex-start; gap: var(--s4);
            margin-bottom: var(--s5); flex-wrap: wrap; }
.pagehead .sub { color: var(--text-dim); font-size: var(--t-sm); margin: var(--s2) 0 0; }
.pagehead-actions { margin-left: auto; display: flex; gap: var(--s2); flex-wrap: wrap; }

/* ===== mobile: tab bar + sheet ============================================== */
.tabbar { display: none; }
.sheet { display: none; }

@media (max-width: 820px) {
  .topbar { padding: 0 var(--s4); }
  .navdesk { display: none; }
  .container { padding: var(--s5) var(--s4) calc(var(--tabbar-h) + var(--s7)); }

  /* The primary action stays reachable without scrolling back up -- the
     structural answer to "add task should be higher" (task #144). */
  .pagehead {
    position: sticky; top: var(--topbar-h); z-index: 15;
    background: var(--bg); padding: var(--s4) 0 var(--s3);
    margin: calc(var(--s5) * -1) 0 var(--s4);
    border-bottom: 1px solid var(--border-dim);
  }

  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
    height: var(--tabbar-h);
    background: var(--surface); border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .tabbar a, .tabbar summary {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 2px;
    min-height: var(--tap);
    font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-faint);
    text-decoration: none; list-style: none; cursor: pointer;
  }
  .tabbar a:hover { text-decoration: none; }
  .tabbar a.on { color: var(--accent); }
  .tabbar .ico { font-size: var(--t-md); line-height: 1; }

  /* More sheet: a <details>, so it needs no JavaScript.
     The <details> itself must be a flex item with an equal share -- flex: 1 on the
     inner <summary> only divides the details' OWN width, which is content-sized, so
     the label was clipped at the right edge of the bar. */
  .sheet { display: flex; flex: 1; }
  .sheet > summary::-webkit-details-marker { display: none; }
  .sheet > summary::marker { content: ""; }
  .sheet[open] .sheet-panel { display: block; }
  .sheet-panel {
    display: none; position: fixed; left: 0; right: 0; bottom: var(--tabbar-h);
    z-index: 26; max-height: 62vh; overflow-y: auto;
    background: var(--surface); border-top: 1px solid var(--border);
    padding: var(--s3) var(--s4) var(--s5);
  }
  .sheet-panel a {
    display: block; padding: var(--s4) var(--s2); min-height: var(--tap);
    color: var(--text); font-size: var(--control-font);
    border-bottom: 1px solid var(--border-dim);
  }
  .sheet-panel a:last-child { border-bottom: none; }
  .sheet-panel .navgroup { display: block; padding: var(--s4) var(--s2) var(--s1); }
}

/* ===== surface ============================================================== */
.surface { background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--r-lg); }
.surface.pad { padding: var(--s4) var(--s5); }

/* ===== row-list: the two-line row ==========================================
   Title line, then a meta line of chips with a right-aligned timestamp. Already
   vertical at every width, so it needs no reflow -- which is why this form ships
   first; the <table> variant (row-table) lands in R4-A with the first table page. */
.rows { list-style: none; margin: 0; padding: 0; }
/* a bare .surface has no padding of its own, so the list supplies its own gutter
   and the day headings line up with the row text */
.surface > .rows { padding: 0 var(--s5); }
.rows li { padding: var(--s4) 0; border-bottom: 1px solid var(--border-dim);
           display: flex; gap: var(--s4); align-items: flex-start; }
.rows li:last-child { border-bottom: none; }
.rows li.dim { opacity: 0.62; }
.rows .dot { width: 7px; height: 7px; border-radius: 50%; margin-top: 7px;
             background: var(--accent); flex: none; }
.rows .dot.ok { background: var(--ok); }
.rows .dot.warn { background: var(--warn); }
.rows .dot.bad { background: var(--bad); }
.rows .dot.mute { background: var(--text-faint); }
.rows .r-body { flex: 1; min-width: 0; }
.rows .r-title { font-size: var(--t-base); line-height: 1.4; color: var(--text); }
.rows .r-meta { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
                margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-dim); }
.rows .r-ref { font-family: var(--font-mono); font-size: var(--t-xs);
               color: var(--text-faint); }
.rows .r-when { margin-left: auto; font-family: var(--font-mono);
                font-size: var(--t-xs); color: var(--text-faint); white-space: nowrap; }

/* ===== chip / priority ====================================================== */
.chip { display: inline-flex; align-items: center; gap: var(--s1);
        font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 700;
        letter-spacing: 0.06em; padding: 2px var(--s2); border-radius: var(--r);
        border: 1px solid var(--border); color: var(--text-dim); white-space: nowrap; }
.chip.ok { color: var(--ok); border-color: var(--ok); background: var(--ok-soft); }
.chip.warn { color: var(--warn); border-color: var(--warn); background: var(--warn-soft); }
.chip.bad { color: var(--bad); border-color: var(--bad); background: var(--bad-soft); }
.chip.on { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }
.prio { font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 700; }
.prio.p0 { color: var(--p0); } .prio.p1 { color: var(--p1); }
.prio.p2 { color: var(--p2); } .prio.p3 { color: var(--p3); }

/* filter chips are links, so they carry the tap floor */
.chiprow { display: flex; gap: var(--s2); flex-wrap: wrap; margin: 0 0 var(--s5); }
.chiprow a { display: inline-flex; align-items: center; min-height: var(--tap);
             padding: 0 var(--s4); border-radius: var(--r);
             border: 1px solid var(--border); color: var(--text-dim);
             font-size: var(--t-sm); }
.chiprow a:hover { text-decoration: none; color: var(--text); }
.chiprow a.on { color: var(--text); border-color: var(--accent);
                background: var(--accent-soft); }
.chiprow a b { font-weight: 700; margin-left: var(--s1); font-family: var(--font-mono); }

/* ===== form field =========================================================== */
.field { display: block; margin: var(--s5) 0 var(--s2);
         color: var(--text-dim); font-size: var(--t-sm); font-weight: 700; }
/* floor by EXCLUSION, not enumeration -- an <input> with no type attribute and
   type=search both escaped an enumerated list in app.css (task #143). */
input:not([type=checkbox]):not([type=radio]):not([type=button]):not([type=submit]):not([type=reset]):not([type=image]):not([type=color]):not([type=range]):not([type=hidden]),
textarea, select {
  width: 100%; background: var(--surface-2); border: 1px solid var(--border);
  color: var(--text); border-radius: var(--r);
  padding: var(--s3) var(--s4); min-height: var(--tap);
  font-family: var(--font);
  font-size: var(--control-font);
}
textarea { min-height: 120px; resize: vertical; }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); }

/* ===== button =============================================================== */
.btn { display: inline-flex; align-items: center; justify-content: center;
       gap: var(--s2); min-height: var(--tap); padding: 0 var(--s5);
       border-radius: var(--r); border: 1px solid var(--accent);
       background: var(--accent); color: var(--on-accent);
       font-family: var(--font); font-size: var(--t-sm); font-weight: 700;
       cursor: pointer; white-space: nowrap; }
.btn:hover { text-decoration: none; filter: brightness(1.08); }
.btn.ghost { background: transparent; color: var(--text-dim); border-color: var(--border); }
.btn.ghost:hover { color: var(--text); border-color: var(--accent); }
.btn.danger { background: transparent; color: var(--bad); border-color: var(--bad); }
.btn.sm { min-height: 34px; padding: 0 var(--s4); font-size: var(--t-xs); }

/* ===== empty / pager / day heading ========================================== */
.empty { color: var(--text-dim); font-size: var(--t-sm); padding: var(--s7) var(--s5);
         text-align: center; }
.pager { display: flex; gap: var(--s6); margin: var(--s5) var(--s1);
         font-size: var(--t-sm); }
.pager a { display: inline-flex; align-items: center; min-height: var(--tap); }
.dayhead { font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 700;
           letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint);
           margin: var(--s6) var(--s5) var(--s1); padding-bottom: var(--s2);
           border-bottom: 1px solid var(--border-dim); }
.dayhead:first-child { margin-top: var(--s4); }

/* ===== project cards ======================================================== */
/* 220px, not 280: .cards holds compact KPI tiles (four across on a desktop
   container), while .pcards below holds project cards, which need the room.
   The front door renders both, which is why they are separate classes. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
         gap: var(--s5); }
/* .pcards is the same grid under the name the legacy templates use for the
   PROJECT grid specifically -- the front door needs both it and .cards (its KPI
   tiles) on the same page, so they cannot be the same class there (task #164). */
.pcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
          gap: var(--s5); }
.pcard { background: var(--surface); border: 1px solid var(--border);
         border-radius: var(--r-lg); padding: var(--s5);
         display: flex; flex-direction: column; gap: var(--s3); min-width: 0; }
.pcard:hover { border-color: var(--accent); }
.pcard.archived { opacity: 0.55; }
.pcard .phead { display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap; }
.pcard .pname { font-size: var(--t-md); font-weight: 700; }
.pcard .pname a { color: var(--text); }
.pcard .pwhen { margin-left: auto; font-family: var(--font-mono);
                font-size: var(--t-xs); color: var(--text-faint); white-space: nowrap; }
.pcard .pdesc { font-size: var(--t-sm); color: var(--text-dim); line-height: 1.45; }
.pstats { display: flex; gap: var(--s5); flex-wrap: wrap; align-items: flex-end; }
.pstats .stat b { display: block; font-family: var(--font-mono); font-size: var(--t-md);
                  font-weight: 700; line-height: 1.1; color: var(--text); }
.pstats .stat span { font-size: var(--t-xs); text-transform: uppercase;
                     letter-spacing: 0.06em; color: var(--text-dim); font-weight: 700; }
.bar { display: flex; align-items: center; gap: var(--s3);
       font-size: var(--t-xs); color: var(--text-dim); }
.bar .track { flex: 1; height: 5px; background: var(--surface-2);
              border-radius: var(--r); overflow: hidden; }
/* width is data-driven, so it is set from a data- attribute by a three-line
   script in shell.html rather than an inline style -- templates must stay free
   of style= (tests/test_shell_render.py). */
.bar .fill { height: 5px; background: var(--ok); width: 0; }
.pcard .pcommit { font-family: var(--font-mono); font-size: var(--t-xs);
                  color: var(--text-faint); white-space: nowrap; overflow: hidden;
                  text-overflow: ellipsis; border-top: 1px solid var(--border-dim);
                  padding-top: var(--s3); }
.pcard .plinks { display: flex; gap: var(--s5); font-size: var(--t-sm); flex-wrap: wrap; }
.pcard .plinks a { color: var(--text-dim); min-height: 34px; display: inline-flex;
                   align-items: center; }

/* ===== toasts + passkey nudge (carried over so the pilots lose no behaviour) == */
.toaststack { position: fixed; right: var(--s5); bottom: var(--s5); z-index: 40;
              width: 320px; display: flex; flex-direction: column; gap: var(--s2); }
.toast { background: var(--surface); border: 1px solid var(--border);
         border-left: 3px solid var(--accent); border-radius: var(--r);
         padding: var(--s3) var(--s4); font-size: var(--t-sm); color: var(--text);
         opacity: 1; transition: opacity 0.3s, transform 0.3s; }
.toast.out { opacity: 0; transform: translateY(6px); }
.pknudge { background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--r-lg); padding: var(--s4) var(--s5);
           margin-bottom: var(--s5); display: flex; align-items: center;
           gap: var(--s4); font-size: var(--t-sm); flex-wrap: wrap; }
@media (max-width: 820px) {
  .toaststack { left: var(--s4); right: var(--s4); width: auto;
                bottom: calc(var(--tabbar-h) + var(--s4)); }
}

/* ===== ops surfaces (task #162) ===========================================
   Restyled into direction A, but under the SAME class names the ops templates
   already use. That is deliberate: _ops_block.html and _ops_row.html encode a
   lot of carefully-tested behaviour (computed block order, ack/owner badges,
   snooze reason + expiry, "unknown is not healthy"), and renaming their markup
   is what silently dropped four of those behaviours on the first attempt at the
   front door. Restyling touches no template, so every one of those tests keeps
   passing unchanged. */
.panel { background: var(--surface); border: 1px solid var(--border);
         border-radius: var(--r-lg); }
.card { background: var(--surface); border: 1px solid var(--border);
        border-radius: var(--r-lg); padding: var(--s4) var(--s5); display: block;
        color: var(--text); }
.card:hover { text-decoration: none; }
.card .big { font-family: var(--font-mono); font-size: var(--t-xl); font-weight: 800;
             line-height: 1.1; }
.card .lbl { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .08em;
             color: var(--text-dim); font-weight: 700; margin-top: var(--s2); }
.card .cardsub { color: var(--text-faint); font-size: var(--t-xs); margin-top: var(--s2); }
.row { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.spacer { flex: 1; }
.muted { color: var(--text-dim); }

.opspanel { padding: var(--s4) var(--s5); margin: var(--s4) 0 var(--s6); }
.opspanel.minor > summary { cursor: pointer; color: var(--text-dim);
                            font-size: var(--t-sm); }
.opshead { font-family: var(--font-mono); font-size: var(--t-xs); letter-spacing: .14em;
           text-transform: uppercase; color: var(--bad); margin-bottom: var(--s3);
           font-weight: 800; }
.card.unknown { border-color: var(--text-faint); }
.card.unknown .opshead, .card.unknown .big { color: var(--text-dim); }
.opslist { list-style: none; margin: 0; padding: 0; }
.opsrow { display: flex; align-items: baseline; gap: var(--s3); padding: var(--s3) 0;
          font-size: var(--t-base); flex-wrap: wrap; }
.opsrow + .opsrow { border-top: 1px solid var(--border-dim); }
.opsrow a { color: var(--text); }
.opsrow a:hover { color: var(--bad); }
.opsrow .opsdetail { color: var(--text-dim); font-size: var(--t-sm); }
.opsrow .opsreason { font-style: italic; }
.opsrow .opssrc { color: var(--text-faint); font-size: var(--t-xs); margin-left: auto;
                  font-family: var(--font-mono); }
.opsrow .opslink { font-size: var(--t-sm); color: var(--accent); }
.opsrow .opsdur { color: var(--text-faint); font-size: var(--t-xs);
                  font-family: var(--font-mono); }
.opsrow.srcdown { color: var(--text-dim); }

.sev { font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 800;
       letter-spacing: .06em; padding: 1px var(--s2); border-radius: var(--r);
       border: 1px solid var(--border); color: var(--text-dim); flex: none; }
.sev.p0 { color: var(--p0); border-color: var(--p0); background: var(--bad-soft); }
.sev.p1 { color: var(--p1); border-color: var(--p1); }
.sev.p2 { color: var(--p2); border-color: var(--p2); }
.sev.p3 { color: var(--p3); }
/* grey, never green: a source we could not read is "unknown", not "healthy" --
   conflating the two is the bug the whole ops sub-project exists to fix */
.sev.unknown { color: var(--text-dim); border-color: var(--text-faint); }

.oclear { display: flex; align-items: center; gap: var(--s3); color: var(--text-dim);
          font-size: var(--t-sm); margin: var(--s4) 0 var(--s6); }
.oclear .okdot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok);
                 flex: none; }

.chip.flap { color: var(--warn); border-color: var(--warn); background: var(--warn-soft); }
.chip.ack { color: var(--ok); border-color: var(--ok); background: var(--ok-soft); }
.chip.owner { width: 20px; height: 20px; padding: 0; display: inline-flex;
              align-items: center; justify-content: center; border-radius: 50%;
              font-weight: 800; }
/* a suppressed signal is quiet, never healthy -- grey like .sev.unknown */
.chip.snooze { color: var(--text-dim); border-color: var(--text-faint); }

/* ===== activity feed + commit matrix (task #164) ========================
   Ported VERBATIM from app.css so the shared partials -- _activity_feed.html
   and _activity_matrix.html, both still included by unconverted pages --
   render identically under either stylesheet. Same reasoning as the ops
   surfaces: restyle the names, never rename the markup. These are the last
   two legacy components the front door needs.
   Colours resolve through the compatibility aliases above. */
.activity { list-style: none; padding: 0; margin: 0; }
.activity li { padding: 11px 0; border-bottom: 1px solid var(--border); display: flex; gap: 10px; }
.activity li:last-child { border: none; }
.activity .dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 7px; background: var(--accent); flex: none; }
.activity .meta { color: var(--text-dim); font-size: 13px; }
.activity .evbody { flex: 1; min-width: 0; }
.activity .evtitle { font-size: 14px; line-height: 1.4; color: var(--text); word-break: break-word; }
.activity .evtitle a { color: var(--text); }
.activity .evtitle a:hover { color: var(--accent); text-decoration: none; }
.activity .evmeta {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 7px; margin-top: 5px; font-size: 12px; color: var(--text-dim);
}
.activity .evtype {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 20px;
  font-size: 11px; font-weight: 600; letter-spacing: .2px;
  line-height: 1.5; white-space: nowrap; border: 1px solid transparent;
  background: rgba(107,114,128,.16); color: var(--text-faint);
}
.activity .evtype .evglyph { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; opacity: .9; }
.activity .evtype.t-issue     { background: rgba(79,140,255,.14); color: var(--accent); }
.activity .evtype.t-pr        { background: rgba(46,204,113,.14); color: var(--ok); }
.activity .evtype.t-push      { background: rgba(124,92,255,.16); color: var(--accent); }
.activity .evtype.t-task      { background: rgba(242,201,76,.14); color: var(--warn); }
.activity .evtype.t-milestone { background: rgba(124,92,255,.14); color: var(--accent); }
.activity .evtype.t-key       { background: rgba(255,93,93,.15); color: var(--bad); }
.activity .evtype.t-caddy     { background: rgba(107,114,128,.18); color: var(--text-dim); }
.activity .evtype.t-other     { background: var(--surface-2); color: var(--text-dim); border-color: var(--border); }
.activity .evrepo {
  display: inline-flex; align-items: center; gap: 3px;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 2px 9px; border-radius: 16px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
}
.activity .evrepo .evrepoglyph { color: var(--text-dim); font-weight: 700; flex: none; }
.activity .evuser {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px 9px 2px 7px; border-radius: 16px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  white-space: nowrap; transition: border-color .12s, color .12s;
}
.activity a.evuser:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.activity .evuser .evuserglyph { color: var(--text-dim); flex: none; }
.activity a.evuser:hover .evuserglyph { color: var(--accent); }
.activity .evuser.ai { background: rgba(124,92,255,.10); border-color: rgba(124,92,255,.30); cursor: default; }
.activity .evuser.ai .evuserglyph { color: var(--accent); }
.activity .evuser .aimark {
  margin-left: 4px; padding: 1px 5px; border-radius: 8px;
  background: rgba(124,92,255,.20); color: var(--accent);
  font-size: 9px; font-weight: 700; letter-spacing: .5px;
}
.activity .evtime { margin-left: auto; color: var(--text-dim); white-space: nowrap; cursor: default; flex: none; }
.activity .dot.issue { background: var(--accent); }
.activity .dot.task { background: var(--warn); }
.activity .dot.milestone { background: var(--accent); }
.activity .dot.key { background: var(--bad); }
.activity .dot.caddy { background: var(--text-faint); }
  .activity .evtime { margin-left: 0; flex-basis: 100%; }
.vlabel { font-size: 10px; color: var(--text-dim); white-space: nowrap; }
.vlegend { margin-top: 12px; font-size: 12px; color: var(--text-dim); display: flex; align-items: center; gap: 6px; }
.vlegend .vdot { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.vlegend .vdot.income { background: var(--ok); }
.vlegend .vdot.expense { background: var(--bad); margin-left: 12px; }
.actbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.actfilter { margin: 0; }
.actfilter select {
  width: auto; min-width: 150px; max-width: 220px;
  padding: 6px 10px; border-radius: 8px;
}
#activityfeed.htmx-request { opacity: .5; transition: opacity .12s ease; }
.activity .dot.push { background: var(--accent); }
.activity .dot.pr { background: var(--ok); }
.activity li.unread { background: rgba(79,140,255,.06); margin: 0 -18px; padding-left: 18px; padding-right: 18px; }
.activity li.unread .dot { background: var(--accent); }
.activity .dot.sla { background: var(--bad); }
.cgraph { padding: 16px 18px; }
.cgraph-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.cgraph-total { font-weight: 600; }
.cgraph-sub { color: var(--text-dim); font-size: 13px; }
.cgraph-scroll { overflow-x: auto; padding-bottom: 4px; }
.cgraph-grid { display: flex; gap: 6px; align-items: stretch; width: 100%; min-width: max-content; }
.cgraph-weekdays { display: flex; flex-direction: column; gap: 3px; padding-top: 16px; flex: none; }
.cgraph-weekdays span { flex: 1 1 0; min-height: 0; display: flex; align-items: center;
  line-height: 1; font-size: 9px; color: var(--text-dim); }
.cgraph-cols { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.cgraph-months { display: flex; gap: 3px; height: 13px; margin-bottom: 3px; }
.cgraph-months span { flex: 1 1 0; width: 0; min-width: 0; font-size: 10px; color: var(--text-dim);
  white-space: nowrap; overflow: visible; }
.cgraph-weeks { display: flex; gap: 3px; }
.cgraph-week { display: flex; flex-direction: column; gap: 3px; flex: 1 1 0; min-width: 9px; }
.cgraph-day { width: 11px; height: 11px; border-radius: 2px; flex: none;
  background: var(--surface-2); border: 1px solid rgba(255,255,255,.04); }
.cgraph-week > .cgraph-day { width: 100%; height: auto; aspect-ratio: 1 / 1; }
.cgraph-day.l0 { background: var(--surface-2); }
.cgraph-day.l1 { background: rgba(46,204,113,.22); }
.cgraph-day.l2 { background: rgba(46,204,113,.45); }
.cgraph-day.l3 { background: rgba(46,204,113,.70); }
.cgraph-day.l4 { background: var(--ok); }
.cgraph-day.blank { background: transparent; border-color: transparent; }
a.cgraph-day { cursor: pointer; text-decoration: none; }
a.cgraph-day:hover { outline: 1.5px solid var(--text); outline-offset: 1px; }
.cgtip { position: absolute; z-index: 60; display: none; transform: translate(-50%, -100%);
  background: var(--surface-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 6px; padding: 6px 9px; font-size: 12px; line-height: 1.4;
  white-space: pre; max-width: 320px; pointer-events: none; box-shadow: var(--shadow); }
.cgtip::after { content: ""; position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--surface-2); }
.cgtip.below { transform: translate(-50%, 0); }
.cgtip.below::after { top: auto; bottom: 100%; border-top-color: transparent;
  border-bottom-color: var(--surface-2); }
.cgraph-legend { display: flex; align-items: center; gap: 4px; justify-content: flex-end;
  margin-top: 10px; font-size: 11px; color: var(--text-dim); }
.cgraph-legend .cgraph-day { margin: 0 1px; }

/* ticket summary rows on the front door, lifted out of dashboard.html's
   page-local <style> (task #164): a converted template carries none. */
.tixrow{display:flex;align-items:baseline;gap:14px;padding:9px 0;flex-wrap:wrap;}
.tixrow+.tixrow{border-top:1px solid var(--border);}
.tixrow .tname{min-width:170px;}
.tixrow .tname a{color:var(--text);}
.tixrow .tcount{color:var(--text-dim);font-size:13px;}
.tixrow .tcount b{color:var(--text);font-weight:650;}
.tixrow .pchip{font-size:11px;padding:1px 8px;border-radius:9px;border:1px solid var(--border);color:var(--text-dim);}
.tixrow .pchip.p0,.tixrow .pchip.p1{color:var(--warn);border-color:var(--warn);}

/* ===== project, task and ticket surfaces (task #166) =====================
   Ported from app.css under the SAME class names, like the ops and activity
   components before them -- these templates keep their markup, so everything
   already tested about them stays true. Four colours that predate the token
   layer were mapped to their meanings on the way across: a scrollbar grey to
   --border, danger red to --bad, overdue amber to --warn, and on-accent white
   to --on-accent. */
.badge { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 12px; font-weight: 600; border: 1px solid transparent; }
.badge.new { background: rgba(46,204,113,.12); color: var(--ok); }
.badge.triage, .badge.blocked { background: rgba(242,201,76,.12); color: var(--warn); }
.badge.in-progress { background: rgba(79,140,255,.14); color: var(--blue); }
.badge.done { background: rgba(124,92,255,.16); color: var(--accent); }
.badge.wont-do { background: rgba(107,114,128,.18); color: var(--text-faint); }
input[type=date].datepick:invalid { border-color: var(--red, var(--bad)); }
.filters { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.filters a { padding: 5px 11px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); color: var(--text-dim); font-size: 13px; }
.filters a.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.markdown { line-height: 1.6; overflow-wrap: anywhere; }
/* A 2560px screenshot in a comment would push the page wider than a phone viewport
   -- exactly the horizontal-overflow class tasks #143 and #146 were spent on. This
   ships WITH the feature rather than after someone reports it (task #172). */
.markdown img { max-width: 100%; height: auto; display: block; margin: var(--s3) 0;
                border: 1px solid var(--border); border-radius: var(--r); }
.comment { border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; margin: 10px 0; background: var(--surface-2); }
.comment .who { font-weight: 600; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.ticketmeta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 24px;
  padding: 12px 18px; margin-bottom: 18px; }
.tmfield { display: flex; align-items: center; gap: 8px; min-height: 30px; }
.tmlabel { font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--text-dim); font-weight: 600; }
.tmstatus { display: flex; align-items: center; gap: 6px; }
.tmstatus select { margin: 0; }
.taskgrid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 18px; align-items: start; }
.taskmeta { margin-bottom: 16px; }
.taskmeta #taskstatus, .taskmeta #taskprio { display: inline-flex; }
.taskmeta #taskstatus select, .taskmeta #taskprio select { margin: 0; padding: 4px 26px 4px 9px;
  color: var(--text); border: 1px solid var(--border); border-radius: 7px; cursor: pointer; appearance: none; -webkit-appearance: none;
  background: var(--surface-2) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238b93a7' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 9px center; }
.taskmeta #taskstatus select:hover, .taskmeta #taskprio select:hover { border-color: var(--accent); }
.taskmeta .tmval { font-size: 13px; }
.projgrid { display: grid; grid-template-columns: minmax(0, 1.9fr) minmax(240px, 1fr);
  gap: 16px; align-items: start; }
.bartrack { flex: 1; height: 10px; background: var(--surface-2); border-radius: 6px; overflow: hidden; }
.barfill { height: 100%; border-radius: 6px; background: var(--accent); transition: width .3s; }
.barfill.new, .barfill.p3 { background: var(--ok); }
.barfill.triage, .barfill.p2 { background: var(--warn); }
.barfill.in-progress { background: var(--blue); }
.barfill.blocked, .barfill.p0 { background: var(--bad); }
.barfill.p1 { background: var(--warn); }
.barfill.done { background: var(--accent); }
.barfill.wont-do { background: var(--text-faint); }
.searchbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.searchbar input[type=text] { flex: 1 1 170px; min-width: 0; }
.searchbar select { width: auto; flex: none; }
.markdown p { margin: 0 0 10px; }
.markdown pre { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px; overflow-x: auto; }
.markdown code { background: var(--bg); padding: 1px 5px; border-radius: 4px; font-size: 13px; }
.markdown pre code { background: none; padding: 0; }
.markdown table { margin: 10px 0; display: block; width: max-content; max-width: 100%; overflow-x: auto; }
.markdown blockquote { border-left: 3px solid var(--border); margin: 10px 0; padding: 2px 14px; color: var(--text-dim); }
.markdown ul, .markdown ol { margin: 0 0 10px; padding-left: 22px; }
.pctlabel { color: var(--accent); font-weight: 700; }
.pctlabel.big-pct { font-size: 22px; }
.mname { font-weight: 600; }
.phasetasks { margin-top: 7px; }
.phasetasks > summary { cursor: pointer; color: var(--text-dim); font-size: 12px; padding: 3px 0; }
.phasetasks > summary:hover { color: var(--text); }
.phasetasks[open] > summary { margin-bottom: 3px; }
.phasetask { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 6px; font-size: 13px; }
.phasetask:hover { background: var(--surface-2); text-decoration: none; }
.phasetask .badge { font-size: 11px; padding: 1px 7px; }
.phasetask-title { flex: 1; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip.tlink { color: var(--accent); border-color: rgba(124,92,255,.4); }
.chip.tlink:hover { border-color: var(--accent); text-decoration: none; }
.addform { margin-top: 12px; }
.addform summary { cursor: pointer; color: var(--accent); font-size: 14px; padding: 6px 0; }
.addform input, .addform textarea, .addform select { margin: 4px 0; }
details.collapse > summary { display: none; }
details.collapse[open] { margin-top: 12px; }
details.collapse:not([open]) {
  padding: 0 !important; border: none !important; background: none !important;
  box-shadow: none !important; margin: 0 !important;
}
.boardpanel { margin-top: 28px; border-top: 1px solid var(--border); padding-top: 12px; }
.boardpanel > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 10px; padding: 2px 0; }
.boardpanel > summary::-webkit-details-marker { display: none; }
.boardpanel > summary::before { content: "\25B8"; color: var(--text-dim); font-size: 12px; }
.boardpanel[open] > summary::before { content: "\25BE"; }
.boardpanel-title { font-size: 16px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: .6px; }
.boardpanel > summary:hover .boardpanel-title { color: var(--text); }
.boardpanel[open] > summary { margin-bottom: 14px; }
.board { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.boardcol { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 8px; min-height: 60px; }
.colhead { font-size: 12px; margin-bottom: 8px; display: flex; gap: 6px; align-items: center; justify-content: space-between; }
.taskcard { background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 9px 10px; margin-bottom: 7px; }
.tasktitle { display: block; font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.35; }
.tasktitle:hover { color: var(--accent); text-decoration: none; }
.taskmeta { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-top: 6px; font-size: 11px; }
.movebar { margin-top: 7px; }
.movebar select { width: 100%; padding: 4px 6px; }
.sibhead { gap: 8px; }
.sibhead .sibtotal { font-size: 11px; font-weight: 600; color: var(--text-dim);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 20px; padding: 1px 9px;
  font-variant-numeric: tabular-nums; }
.sibctl { display: flex; align-items: center; gap: 10px; margin: 10px 0 8px; }
.sibsel { color: var(--text); padding: 5px 28px 5px 10px; margin: 0; width: auto;
  border: 1px solid var(--border); border-radius: 7px; appearance: none; -webkit-appearance: none; cursor: pointer;
  background: var(--surface-2) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238b93a7' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 9px center; }
.sibsel:hover { border-color: var(--accent); }
.sibtoggle { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; font-size: 12px;
  color: var(--text-dim); cursor: pointer; user-select: none; }
.sibtoggle input { width: auto; accent-color: var(--accent); }
.siblist { position: relative; max-height: 380px; overflow-y: auto; margin: 0 -6px; padding: 0 6px;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.siblist::-webkit-scrollbar { width: 8px; }
.siblist::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.siblist::-webkit-scrollbar-thumb:hover { background: var(--border); }
.siblist::-webkit-scrollbar-track { background: transparent; }
.sibitem { display: flex; align-items: center; gap: 9px; padding: 6px 8px; margin: 2px 0; border-radius: 7px;
  border-left: 2px solid transparent; color: var(--text); text-decoration: none; transition: background .12s ease; }
a.sibitem:hover { background: var(--surface-2); text-decoration: none; }
.sibitem.cur { background: rgba(79,140,255,.10); border-left-color: var(--accent); }
.sibitem.cur .sibtitle { font-weight: 700; }
.sibdot.done { background: var(--accent); }
.sibdot.wont-do { background: var(--text-faint); }
.sibnum { flex: none; color: var(--text-dim); font-size: 11px; font-variant-numeric: tabular-nums; min-width: 26px; }
.sibtitle { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; font-weight: 500; line-height: 1.3; }
.checkrow { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
.checkrow input { width: auto; }
.assignlist { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.assignlist .checkrow { background: var(--surface-2); border: 1px solid var(--border); border-radius: 18px;
  padding: 6px 13px; font-size: 13px; gap: 6px; transition: background .12s, border-color .12s, color .12s; }
.assignlist .checkrow:hover { border-color: var(--accent); }
.assignlist .checkrow input { display: none; }
.assignlist .checkrow:has(input:checked) { background: rgba(79,140,255,.18); border-color: var(--accent); }
.assignlist .checkrow:has(input:checked)::before { content: "✓"; color: var(--accent); font-weight: 700; }
.linkrow { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.linkrow:last-of-type { border-bottom: none; }
.linkrow form { display: inline; margin: 0; }
.linkrow:has(+ details.keycaps[open]) { border-bottom: none; }
details.keycaps[open] { margin: 0 0 4px; border-bottom: 1px solid var(--border); }
details.keycaps:last-child { border-bottom: none; }
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin: 4px 0 20px; }
.tabs .tab { padding: 9px 15px; color: var(--text-dim); border-bottom: 2px solid transparent;
  font-weight: 600; font-size: 14px; margin-bottom: -1px; }
.tabs .tab:hover { color: var(--text); text-decoration: none; }
.tabs .tab.active { color: var(--text); border-bottom-color: var(--accent); }
.markdown h1 { font-size: 22px; margin: 8px 0 12px; }
.markdown h2 { font-size: 17px; color: var(--text); text-transform: none; letter-spacing: 0; margin: 20px 0 10px; }
.markdown h3 { font-size: 15px; margin: 16px 0 8px; }
.commitlist .cbody { white-space: pre-wrap; word-break: break-word; background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 8px 10px; margin: 6px 0; font-size: 12.5px; color: var(--text-dim); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.linkbtn { background: none; border: none; color: var(--accent); cursor: pointer; font: inherit;
  font-size: 13px; padding: 2px 5px; }
.linkbtn:hover { text-decoration: underline; }
.nowrap form, .comment .row form { display: inline; }
.btn.danger-btn { color: var(--bad); border-color: rgba(255,93,93,.4); background: var(--surface-2); }
.btn.danger-btn:hover { background: rgba(255,93,93,.14); }
details.addform[open] > summary { margin-bottom: 6px; }
@media (max-width: 600px) { .board { grid-template-columns: 1fr; } }
@media (max-width: 700px) { .grid2 { grid-template-columns: 1fr; } }
@media (max-width: 860px) { .taskgrid { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 900px) { .projgrid { grid-template-columns: 1fr; } }

/* ===== remaining admin, money and ops-console surfaces (task #168) =======
   The last batch: fleet, backups, monitoring, secrets, wiki, users, settings,
   customers, financials, caddy and the commit/pull views. Ported under the same
   class names as everything before them, plus the six page-local <style> blocks
   those pages carried -- a converted template keeps none. Six colours predating
   the token layer were mapped to their meanings. */
.vchart { display: flex; align-items: flex-end; gap: 6px; height: 150px; padding-top: 4px; }
.vcol { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; align-items: center; gap: 5px; justify-content: flex-end; }
.vbars { flex: 1; width: 100%; display: flex; align-items: flex-end; justify-content: center; gap: 2px; }
.vbar { width: 44%; min-height: 2px; border-radius: 3px 3px 0 0; }
.vbar.income { background: var(--ok); }
.vbar.expense { background: var(--bad); }
label.check { display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--text); cursor: pointer; }
label.check input { width: auto; margin: 0; }
.slabadge { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 12px; font-weight: 600; }
.slabadge.on-track { background: rgba(46,204,113,.12); color: var(--ok); }
.slabadge.due-soon { background: rgba(242,201,76,.14); color: var(--warn); }
.slabadge.breached { background: rgba(255,93,93,.16); color: var(--bad); }
.fresh-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 7px; background: var(--ok); vertical-align: 1px; }
.fresh-dot.stale { background: var(--bad); }
.fresh-dot.warn { background: var(--warn); }     /* drift / behind */
.fresh-dot.unknown { background: var(--text-faint); }    /* unreachable / can't compare */
.grid-wiki { display: grid; grid-template-columns: 220px 1fr; gap: 16px; align-items: start; }
.grid-wiki > * { min-width: 0; }
.wikiside { padding: 10px; }
.wikisidehead { font-size: 12px; text-transform: uppercase; letter-spacing: .5px; color: var(--text-dim); padding: 6px 10px; }
.wikilink { display: block; padding: 7px 10px; border-radius: 7px; color: var(--text); font-size: 14px; }
.wikilink:hover { background: var(--surface-2); text-decoration: none; }
.wikilink.active { background: var(--surface-2); color: var(--accent); }
.wikilink.new { color: var(--accent); margin-top: 6px; }
.commitlist .cmeta { min-width: 0; flex: 1; }
.commitlist .cmsg { font-size: 14px; font-weight: 600; }
.commitlist .sha { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--accent); }
.add { color: var(--ok); font-weight: 600; }
.del { color: var(--bad); font-weight: 600; }
.filelist { margin-top: 6px; }
.filelist > summary { display: none; }
.filelist .frow { display: flex; gap: 8px; padding: 3px 0; font-size: 13px; align-items: baseline; }
.filelist .fname { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); word-break: break-all; }
.filelist .fstat { font-size: 10px; font-weight: 700; text-transform: uppercase; border-radius: 4px; padding: 1px 5px; flex: none; }
.filelist .fstat.added { background: rgba(46,204,113,.15); color: var(--ok); }
.filelist .fstat.modified { background: rgba(242,201,76,.15); color: var(--warn); }
.filelist .fstat.removed, .filelist .fstat.deleted { background: rgba(255,93,93,.15); color: var(--bad); }
.filelist .fstat.renamed { background: rgba(79,140,255,.15); color: var(--blue); }
.usermanage { border: none; }
.usermanage > summary { display: none; }
.usermanage[open] { animation: fade .15s ease; }
.usermanage form { margin: 0; }
td:has(> details.usermanage) { border-bottom: none; padding: 0; }
tr:hover td:has(> details.usermanage) { background: transparent; }
@media (max-width: 760px) { .grid-wiki { grid-template-columns: 1fr; } }

/* --- lifted from subjects.html (task #168) --- */
.subjects-table td{vertical-align:top;}
.subjects-table select{margin-right:6px;}
.subjects-table input[type="text"]{width:150px;}
.subjects-table input[type="date"]{margin-left:4px;}
.subjects-table .dim{white-space:nowrap;}
.subjects-table .na{font-size:12px;color:var(--text-faint);font-style:italic;}
.subjects-table .exempt{display:block;font-size:11px;color:var(--text-dim);margin-top:2px;}
.subjects-table .alias-row{display:block;margin-top:6px;}
.subjects-table .alias-row input[type="text"]{width:120px;}
.subjects-table .alias-row .btn{font-size:12px;padding:3px 10px;}
/* --- lifted from backups.html (task #168) --- */
.verdicts{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:14px 0 22px;}
@media (max-width:760px){.verdicts{grid-template-columns:1fr;}}
.verdict{position:relative;padding:16px 18px 14px 22px;border-radius:10px;
  background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);overflow:hidden;}
.verdict::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--vc);}
.verdict.ok{--vc:var(--ok);}
.verdict.warn{--vc:var(--warn);}
.verdict.bad{--vc:var(--bad);}
.verdict.off{--vc:var(--text-faint);}
.verdict .eyebrow{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--text-dim);}
.verdict .big{font-size:19px;font-weight:650;margin:5px 0 3px;color:var(--vc);}
.verdict.off .big{color:var(--text-dim);}
.verdict .why{font-size:13px;color:var(--text-dim);line-height:1.45;}
.verdict .facts{display:flex;gap:18px;margin-top:10px;font-size:12px;color:var(--text-dim);flex-wrap:wrap;}
.verdict .facts b{color:inherit;font-weight:600;}
.rowrun{font-size:12px;padding:3px 10px;}
.runcard{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px;}
.runcard form{flex:1 1 180px;background:rgba(255,255,255,.02);border:1px solid rgba(255,255,255,.07);
  border-radius:8px;padding:12px 14px;}
.runcard .cap{font-size:12px;color:var(--text-dim);margin-top:6px;line-height:1.4;}
.settings{display:flex;gap:14px;align-items:end;flex-wrap:wrap;margin-top:12px;}
.settings label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--text-dim);}
/* --- lifted from monitoring.html (task #168) --- */
.mon-group{margin:4px 0 20px;}
.mon-group-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin:0 0 8px;
  border-bottom:1px solid rgba(255,255,255,.07);padding-bottom:5px;}
.mon-group-head h2{margin:0;font-size:15px;}
.mon-group-head h2 a{color:var(--text);text-decoration:none;}
.mon-group-head h2 a:hover{color:var(--accent);}
.mon-group-counts{font-size:12px;}
details.mon-group{margin:4px 0 20px;}
details.mon-group>summary{cursor:pointer;list-style:none;}
details.mon-group>summary::-webkit-details-marker{display:none;}
details.mon-group>summary:hover .mon-group-title{color:var(--accent);}
.mon-group-title{font-weight:600;font-size:14px;color:var(--text-dim);}
details.mon-group[open]>summary .mon-group-title::before{content:"\25be  ";}
details.mon-group:not([open])>summary .mon-group-title::before{content:"\25b8  ";}
details.mon-infra .mon-tiles{margin-top:10px;}
.mon-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px;}
.mon-tile{position:relative;display:block;text-decoration:none;color:inherit;padding:12px 14px 11px 17px;
  border-radius:10px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);overflow:hidden;}
.mon-tile::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--sc);}
.mon-tile.up{--sc:var(--ok);}
.mon-tile.degraded{--sc:var(--warn);}
.mon-tile.down{--sc:var(--bad);}
.mon-tile.unknown,.mon-tile.gone{--sc:var(--text-faint);}
.mon-tile .nm{font-weight:600;font-size:14px;display:flex;align-items:center;}
.mon-tile:hover .nm{color:var(--accent);}
.mon-tile .st{font-size:12px;color:var(--sc);margin-top:3px;font-weight:600;}
.mon-tile.unknown .st,.mon-tile.gone .st{color:var(--text-dim);}
.mon-tile .up-pct{font-size:12px;color:var(--text-dim);margin-top:5px;}
.mon-summary{display:flex;gap:16px;flex-wrap:wrap;font-size:13px;color:var(--text-dim);margin:4px 0 12px;align-items:center;}
.mon-summary b{color:var(--text);}
.mon-inc{list-style:none;padding:0;margin:6px 0 0;}
.mon-inc li{display:flex;gap:10px;align-items:baseline;padding:9px 0;border-bottom:1px solid rgba(255,255,255,.06);}
.mon-inc .sev{font-size:11px;font-weight:700;padding:1px 8px;border-radius:20px;border:1px solid transparent;white-space:nowrap;}
.mon-updated{font-size:12px;color:var(--text-faint);margin-top:16px;}
.mon-filter{margin:6px 0 14px;padding:7px 12px;width:min(340px,100%);border-radius:8px;
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);color:var(--text);font-size:13px;}
/* --- lifted from monitor_detail.html (task #168) --- */
.md-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;}
.md-head h1{margin:0;}
.md-state{font-size:13px;font-weight:600;padding:2px 11px;border-radius:20px;border:1px solid transparent;}
.md-state.up{color:var(--ok);border-color:var(--ok);}
.md-state.degraded{color:var(--warn);border-color:var(--warn);}
.md-state.down{color:var(--bad);border-color:var(--bad);}
.md-state.unknown,.md-state.gone{color:var(--text-dim);border-color:var(--text-faint);}
.md-meta{display:flex;gap:18px;flex-wrap:wrap;font-size:13px;color:var(--text-dim);margin:10px 0 20px;}
.md-meta b{color:var(--text);}
.md-meta a{color:var(--accent);}
.uptimebar{display:flex;height:34px;border-radius:6px;overflow:hidden;border:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.03);}
.uptimebar .seg{height:100%;min-width:1px;}
.uptimebar .seg.up{background:var(--ok);}
.uptimebar .seg.degraded{background:var(--warn);}
.uptimebar .seg.down{background:var(--bad);}
.uptimebar .seg.unknown{background:var(--text-faint);}
.md-scale{display:flex;justify-content:space-between;font-size:11px;color:var(--text-faint);margin-top:5px;}
.md-incs{list-style:none;padding:0;margin:6px 0 0;}
.md-incs li{display:flex;gap:10px;align-items:baseline;padding:9px 0;border-bottom:1px solid rgba(255,255,255,.06);}
.md-incs .sev{font-size:11px;font-weight:700;padding:1px 8px;border-radius:20px;border:1px solid transparent;white-space:nowrap;}
.md-incs .rs{color:var(--ok);} .md-incs .op{color:var(--bad);}
/* --- lifted from monitor_new.html (task #168) --- */
.mnew{max-width:560px;}
.mnew label{display:block;margin:14px 0 4px;font-size:13px;color:var(--text-dim);}
/* no font-size: these are controls, so they inherit the 16px floor from app.css.
   A 14px control is an iOS focus-zoom trigger (task #143). */
.mnew input[type=text],.mnew select,.mnew textarea{width:100%;padding:8px 11px;border-radius:8px;
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.14);color:var(--text);}
.mnew .hint{font-size:12px;color:var(--text-faint);margin-top:3px;}
.mnew .actions{margin-top:18px;display:flex;gap:10px;align-items:center;}
/* --- lifted from pull_detail.html (task #168) --- */
.diffview{background:var(--bg);border:1px solid var(--border);border-radius:6px;overflow-x:auto;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.45;
  padding:8px 0;max-height:70vh;overflow-y:auto;}
.diffview .dl{padding:0 12px;white-space:pre;}
.diffview .dl.add{background:rgba(46,160,67,.18);}
.diffview .dl.del{background:rgba(248,81,73,.15);}
.diffview .dl.hunk{color:var(--accent);background:rgba(56,139,253,.10);}
.diffview .dl.meta{color:var(--text-dim);}

/* ===== wide tables scroll inside their container, at every width ==========
   Carried over from app.css (tasks #143, #146). Matched by STRUCTURE, not by
   class, because the class form missed two real cases: /subjects puts its table
   inside a <form> and /backups puts one directly in .container. Without this,
   /subjects is 1832px wide at a 1280px viewport. */
.surface:has(table), .panel:has(table) { overflow-x: auto; }
.container :has(> table), .container:has(> table) { overflow-x: auto; }
/* long unbreakable identifiers are everywhere in this app */
.panel, .surface { overflow-wrap: anywhere; }

/* the "or" divider on the sign-in page (task #168) */
.ordiv { display: flex; align-items: center; text-align: center; color: var(--text-dim); margin: 16px 0; font-size: 13px; }
.ordiv::before, .ordiv::after { content: ""; flex: 1; border-bottom: 1px solid var(--border); }
.ordiv span { padding: 0 12px; }

/* An attachment row's thumbnail. Fixed box so a row of them stays a row and a tall
   screenshot cannot stretch the list; the image itself is never trusted to size
   anything (task #172). */
.attthumb {
  width: 44px; height: 44px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--border);
  background: var(--surface-2); display: block; flex: none;
}
