/* Forge — design system.
 *
 * Implements design_handoff_forge/README.md. Light is primary; dark is designed from the same
 * token set, not inverted. Every colour is a token on :root and is redefined in BOTH dark
 * blocks — never defined only inside a theme.
 *
 * Fonts are self-hosted (rule 9: no CDN, no icon fonts). install-buildvm.sh places the Plex
 * files in static/fonts/; until they are there the stack below falls back to the system faces
 * so the app degrades rather than breaks.
 */

@font-face{font-family:"IBM Plex Sans";font-weight:400;font-display:swap;
  src:url("/static/fonts/IBMPlexSans-Regular-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Regular.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Regular.ttf") format("truetype")}
@font-face{font-family:"IBM Plex Sans";font-weight:450;font-display:swap;
  src:url("/static/fonts/IBMPlexSans-Text-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Text.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Text.ttf") format("truetype")}
@font-face{font-family:"IBM Plex Sans";font-weight:500;font-display:swap;
  src:url("/static/fonts/IBMPlexSans-Medium-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Medium.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-Medium.ttf") format("truetype")}
@font-face{font-family:"IBM Plex Sans";font-weight:600;font-display:swap;
  src:url("/static/fonts/IBMPlexSans-SemiBold-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-SemiBold.woff2") format("woff2"),
      url("/static/fonts/IBMPlexSans-SemiBold.ttf") format("truetype")}
@font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;
  src:url("/static/fonts/IBMPlexMono-Regular-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2"),
      url("/static/fonts/IBMPlexMono-Regular.ttf") format("truetype")}
@font-face{font-family:"IBM Plex Mono";font-weight:500;font-display:swap;
  src:url("/static/fonts/IBMPlexMono-Medium-Latin1.woff2") format("woff2"),
      url("/static/fonts/IBMPlexMono-Medium.woff2") format("woff2"),
      url("/static/fonts/IBMPlexMono-Medium.ttf") format("truetype")}

:root{
  --ground:#F6F7F9; --raised:#FFFFFF; --sunken:#F6F7F9;
  --line:#E3E6EB; --line-soft:#EEF0F3; --line-strong:#C9CED7;
  --ink:#14171E; --ink-2:#3C424E; --ink-muted:#5B6270; --ink-faint:#98A2B3;
  --accent:#4A3FC7; --accent-hover:#3C32A8; --accent-active:#322A9E;
  --accent-tint:#F2F1FB; --accent-tint-line:#C6C2EE; --accent-ink:#FFFFFF;
  --accent-focus-glow:rgba(74,63,199,0.12);
  /* run status */
  --run-working:#0E7490; --run-needs:#B45309; --run-good:#15803D; --run-failed:#B42318; --run-idle:#98A2B3;
  /* surfaces for error and warning */
  --err-bg:#FDF6F5; --err-line:#F0C9C4; --err-line-strong:#C0796F; --err-ink:#B42318; --err-ink-deep:#8A2118;
  --warn-bg:#FCF8EF; --warn-line:#C0A45E; --warn-ink:#8A4208;
  --ok-line:#15803D;
  --sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --gutter:20px;
}
/* Dark is a companion, from the same tokens. Hues held; only lightness and chroma move. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0E1116; --raised:#161A21; --sunken:#0E1116;
    --line:#262B34; --line-soft:#1E232B; --line-strong:#39404B;
    --ink:#F5F6F8; --ink-2:#C4CAD3; --ink-muted:#99A1AE; --ink-faint:#6B7480;
    --accent:#8079F0; --accent-hover:#9691F4; --accent-active:#6F67E8;
    --accent-tint:#1B1E2E; --accent-tint-line:#3A3570; --accent-ink:#0E1116;
    --accent-focus-glow:rgba(128,121,240,0.18);
    --run-working:#4FD1C5; --run-needs:#F0B357; --run-good:#55C27E; --run-failed:#F4776A; --run-idle:#6B7480;
    --err-bg:#251619; --err-line:#4A2A2C; --err-line-strong:#7A4038; --err-ink:#F4776A; --err-ink-deep:#F9A79C;
    --warn-bg:#241E12; --warn-line:#5A4823; --warn-ink:#F0B357;
    --ok-line:#55C27E;
  }
}
:root[data-theme="dark"]{
  --ground:#0E1116; --raised:#161A21; --sunken:#0E1116;
  --line:#262B34; --line-soft:#1E232B; --line-strong:#39404B;
  --ink:#F5F6F8; --ink-2:#C4CAD3; --ink-muted:#99A1AE; --ink-faint:#6B7480;
  --accent:#8079F0; --accent-hover:#9691F4; --accent-active:#6F67E8;
  --accent-tint:#1B1E2E; --accent-tint-line:#3A3570; --accent-ink:#0E1116;
  --accent-focus-glow:rgba(128,121,240,0.18);
  --run-working:#4FD1C5; --run-needs:#F0B357; --run-good:#55C27E; --run-failed:#F4776A; --run-idle:#6B7480;
  --err-bg:#251619; --err-line:#4A2A2C; --err-line-strong:#7A4038; --err-ink:#F4776A; --err-ink-deep:#F9A79C;
  --warn-bg:#241E12; --warn-line:#5A4823; --warn-ink:#F0B357;
  --ok-line:#55C27E;
}

*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0;background:var(--ground);color:var(--ink);
  font:400 17px/1.5 var(--sans);-webkit-font-smoothing:antialiased}
body{padding-bottom:env(safe-area-inset-bottom);overflow-x:hidden}
button,input,textarea,select{font:inherit;color:inherit}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
/* Every number, timing, version and count is tabular so values do not shimmer as they update. */
.num,.mono,.log,.cmd,input.mono,td.num{font-variant-numeric:tabular-nums}

/* ── motion: two keyframes, both driven by real activity ───────────────────── */
@keyframes forge-advance{0%{transform:translateX(-100%)}100%{transform:translateX(300%)}}
@keyframes forge-breathe{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){*{animation-duration:.001ms !important;animation-iteration-count:1 !important}}

/* ── layout ────────────────────────────────────────────────────────────────── */
.wrap{max-width:560px;margin:0 auto;padding:0 var(--gutter)}
.app-header{position:sticky;top:0;z-index:20;background:var(--raised);
  border-bottom:1px solid var(--line-soft);padding-top:env(safe-area-inset-top)}
.app-header .row{display:flex;align-items:center;justify-content:space-between;height:56px}
.brand{display:flex;align-items:center;gap:10px;font-size:17px;font-weight:600;letter-spacing:-.02em}
.conn{display:inline-flex;align-items:center;gap:7px;height:30px;padding:0 11px;border-radius:15px;
  background:var(--sunken);font-family:var(--mono);font-size:11px;letter-spacing:.06em;color:var(--ink-muted)}
.conn .d{width:7px;height:7px;border-radius:50%;background:var(--run-good)}
.conn[data-state="reconnecting"] .d{background:var(--run-needs)}
/* `main` also carries .wrap, whose `padding:0 var(--gutter)` shorthand resets padding-bottom to
   zero - a class beats an element selector, so a bare `main{padding-bottom:...}` here silently did
   nothing and the last card sat under the pinned footer with no way to scroll it clear.
   main.wrap wins on specificity; --foot-reserve is measured in fitFoot(), the literal is the
   no-JS fallback. */
main.wrap{padding-bottom:var(--foot-reserve,210px)}
/* The project and server views carry their primary action in a footer pinned above the tab bar,
   so "Say what you want" and "Add a server" are never below the fold however long the screen gets. */
.viewfoot{position:fixed;left:0;right:0;bottom:calc(57px + env(safe-area-inset-bottom));z-index:25;
  background:var(--raised);border-top:1px solid var(--line-soft);padding:12px 0 14px}
.viewfoot .wrap > * + *{margin-top:10px}
@media (min-width:1024px){main.wrap{padding-bottom:40px}.viewfoot{position:static;border-top:0;background:none;padding:18px 0 0}}

/* bottom bar — four tabs, room for a fifth. Notifications are not a button: the needs-you
   diamond sits beside Activity, so badge and status share one language. */
.tabbar{position:fixed;left:0;right:0;bottom:0;z-index:30;display:flex;background:var(--raised);
  border-top:1px solid var(--line);padding-bottom:env(safe-area-inset-bottom)}
.tabbar button{flex:1;min-height:57px;border:0;background:none;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:4px;font-size:13px;color:var(--ink-muted);cursor:pointer;padding:0}
.tabbar button .lbl{display:inline-flex;align-items:center;gap:6px}
.tabbar button[aria-selected="true"]{color:var(--ink);font-weight:500}
.tabbar button[aria-selected="true"]::after{content:"";width:18px;height:2px;background:var(--ink);border-radius:1px}
.tabbar button:not([aria-selected="true"])::after{content:"";width:18px;height:2px;background:transparent}

/* ── type scale ────────────────────────────────────────────────────────────── */
.h-statement{font-size:40px;font-weight:600;letter-spacing:-.035em;line-height:1.05;margin:0}
.h-outcome{font-size:32px;font-weight:600;letter-spacing:-.03em;line-height:1.12;margin:0}
.h-screen{font-size:28px;font-weight:600;letter-spacing:-.025em;margin:0}
.h-sheet{font-size:21px;font-weight:600;letter-spacing:-.02em;margin:0 0 14px}
.body{font-size:17px;line-height:1.48;margin:0}
.dense{font-size:15px}
.meta{font-size:13px;color:var(--ink-muted)}
.ulabel{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.065em;
  text-transform:uppercase;color:var(--ink-muted)}
.mdata{font-family:var(--mono);font-size:12px;font-variant-numeric:tabular-nums;color:var(--ink-muted)}
/* Account identity. Two facts, two lines: the name is who the account belongs to, the address is
   how he signs in. Never "Signed in as" — he is the only one, and he knows who he is. */
.acct-name{font-size:17px;font-weight:500;color:var(--ink);line-height:1.3}
.acct-email{font-size:13px;color:var(--ink-muted);margin-top:4px;word-break:break-all}

/* Notification categories. The row is the 44px hit target; the switch inside it is 44×26 as drawn,
   so the visible control and the tappable one are not the same box and neither has to compromise. */
.noti{display:flex;align-items:flex-start;gap:14px;min-height:44px;padding:11px 0}
.noti + .noti{border-top:1px solid var(--line-soft)}
.noti .t{flex:1;min-width:0}
.noti .t b{display:block;font-size:15px;font-weight:500;color:var(--ink);line-height:1.3}
.noti .t span{display:block;font-size:13px;color:var(--ink-muted);margin-top:3px;line-height:1.35}
/* The always-on note belongs to its row, not to the bottom of the card — read next to the switch
   it explains, it says why that one is not yours to turn off. */
.noti .t em{display:block;font-style:normal;font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.05em;color:var(--accent-active);margin-top:6px}
/* The button is a real 44×44 box so the thumb has somewhere to land; the switch drawn inside it is
   44×26 as specified. Two boxes, because one box cannot honour both numbers — and the hit area is
   real geometry, not a pseudo-element, which whatever paints next would sit on top of. */
.sw{flex:none;width:44px;height:44px;border:none;padding:0;background:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent}
.sw .tr{position:relative;width:44px;height:26px;border-radius:13px;background:var(--line-strong);
  transition:background .15s ease}
.sw .tr::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:10px;
  background:#fff;box-shadow:0 1px 2px rgba(20,23,30,.28);transition:transform .15s ease}
.sw[aria-checked="true"] .tr{background:var(--accent)}
.sw[aria-checked="true"] .tr::after{transform:translateX(18px)}
.sw:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:14px}
/* Always-on: on, and visibly not yours to change — the accent tint, not the accent. */
.sw[disabled]{cursor:default}
.sw[disabled] .tr{background:var(--accent-tint-line)}
.sw[disabled] .tr::after{box-shadow:none}
@media (prefers-reduced-motion: reduce){ .sw .tr,.sw .tr::after{transition:none} }

/* ── cards ─────────────────────────────────────────────────────────────────── */
.card{background:var(--raised);border:1px solid var(--line);border-radius:14px;padding:15px}
.card + .card{margin-top:12px}
.card.err{background:var(--err-bg);border-color:var(--err-line)}
.card.warn{background:var(--warn-bg);border-color:var(--warn-line)}
.card.proof{border:1.5px solid var(--ink)}
.rowset > div{display:flex;justify-content:space-between;gap:12px;padding:11px 0;font-size:15px}
.rowset > div + div{border-top:1px solid var(--line-soft)}
.rowset .k{color:var(--ink-muted)}
.rowset .v{text-align:right}

/* ── buttons ───────────────────────────────────────────────────────────────── */
button.btn{width:100%;min-height:52px;border-radius:12px;border:0;background:var(--accent);
  color:var(--accent-ink);font-size:17px;font-weight:500;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:8px}
button.btn:hover{background:var(--accent-hover)}
button.btn:active{background:var(--accent-active)}
button.btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
button.btn2{width:100%;min-height:48px;border-radius:12px;background:var(--raised);
  border:1px solid var(--line-strong);color:var(--ink);font-size:16px;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:8px}
button.btn2:hover{border-color:var(--ink)}
button.btn2:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
button.btn-danger{background:var(--raised);border:1px solid var(--err-line-strong);color:var(--err-ink-deep)}
button.btn-danger:hover{background:var(--err-bg)}
button.btn-quiet{width:100%;min-height:44px;border:0;background:none;color:var(--ink-muted);
  font-size:15px;cursor:pointer;text-decoration:underline;text-decoration-color:var(--line-strong);
  text-underline-offset:4px}
button.btn-quiet:hover{color:var(--ink)}
button.btn:disabled,button.btn2:disabled{background:var(--sunken);border:1px solid var(--line);
  color:var(--ink-faint);cursor:not-allowed}
button.btn:disabled:hover,button.btn2:disabled:hover{background:var(--sunken);border-color:var(--line)}
.btnrow{display:flex;gap:10px}
.btnrow > *{flex:1}
.stack > * + *{margin-top:10px}

/* ── inputs ────────────────────────────────────────────────────────────────── */
/* span.f is for a group of controls that has no single control to label — the kind chips. */
label.f,span.f{display:block;font-size:13px;color:var(--ink-muted);margin:0 0 6px}
input.f,textarea.f,select.f{width:100%;min-height:48px;border-radius:10px;background:var(--raised);
  border:1px solid var(--line-strong);padding:12px 13px;font-size:17px;color:var(--ink)}
input.f.mono,textarea.f.mono{font-family:var(--mono);font-size:16px}
textarea.f{min-height:124px;resize:vertical;line-height:1.5}
input.f:focus,textarea.f:focus,select.f:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-focus-glow)}
.field{margin-bottom:16px}
/* Password reveal. 44x44 tap target per the mobile floor; the input reserves room so the
   dots never run under it. Pressed state drives which glyph shows, so there is one source
   of truth for "is it visible" and screen readers read it from the same attribute. */
/* Sign-in fields. Sized here rather than inline so the handset block below can scale them with
   everything else, and so the revealed password restyles itself from its own type attribute
   instead of JavaScript reaching in to set a font size. */
.lf{min-height:50px;font-size:17px}
#p{font-size:19px;letter-spacing:.18em}
#p[type="text"]{font-size:16px;letter-spacing:normal}
#t{font-size:19px;letter-spacing:.22em}
.pw{position:relative}
.pw input.f{padding-right:54px}
.eyeb{position:absolute;right:3px;top:50%;transform:translateY(-50%);width:44px;height:44px;
  display:flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:9px;
  background:none;color:var(--ink-muted);cursor:pointer;-webkit-tap-highlight-color:transparent}
.eyeb:hover{color:var(--ink)}
.eyeb:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-focus-glow)}
.eyeb svg{width:21px;height:21px;display:block}
.eyeb .off{display:none}
.eyeb[aria-pressed="true"]{color:var(--accent)}
.eyeb[aria-pressed="true"] .on{display:none}
.eyeb[aria-pressed="true"] .off{display:block}
/* An explanation a field genuinely needs goes INSIDE its border, not as grey text under the sheet. */
.field.withnote input.f,.field.withnote textarea.f{border-bottom-left-radius:0;border-bottom-right-radius:0}
.fnote{background:var(--sunken);border:1px solid var(--line-strong);border-top:1px solid var(--line-soft);
  border-radius:0 0 10px 10px;padding:9px 12px;font-size:13px;color:var(--ink-muted);
  display:flex;gap:8px;align-items:flex-start}

/* ── run status: bare glyphs, never labelled, always beside a project name ──── */
.rs{display:inline-block;flex:none}
.rs-working{width:10px;height:10px;background:var(--run-working)}
.rs-working.live{animation:forge-breathe 1.6s ease-in-out infinite}
.rs-needs{width:11px;height:11px;background:var(--run-needs);transform:rotate(45deg)}
.rs-good{width:11px;height:11px;border-radius:50%;background:var(--run-good)}
.rs-failed{width:12px;height:3px;background:var(--run-failed)}
.rs-idle{width:11px;height:11px;border-radius:50%;border:1.5px solid var(--run-idle)}

/* ── server state: always a bordered box containing a word, always right-aligned.
      A different STRUCTURE from run status, so the two cannot be confused in greyscale. */
.ss{display:inline-flex;align-items:center;gap:7px;height:29px;padding:0 10px;border-radius:8px;
  border:1px solid var(--line-strong);font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;
  white-space:nowrap;flex:none}
.ss .m{width:15px;height:10px;border-radius:2px;border:1.5px solid currentColor;flex:none;position:relative;overflow:hidden}
.ss-never{border-style:dashed;border-color:var(--ink-faint);color:var(--ink-2)}
.ss-never .m{border-style:dashed;border-color:var(--ink-muted)}
.ss-testing{color:var(--run-working)}
.ss-testing .m::after{content:"";position:absolute;inset:0;width:40%;background:currentColor;
  animation:forge-advance 1.9s linear infinite}
.ss-reachable{color:var(--run-good)}
.ss-reachable .m{background:var(--run-good)}
.ss-unreachable,.ss-refused{border-color:var(--err-line-strong);background:var(--err-bg);color:var(--err-ink)}
.ss-unreachable .m::after,.ss-refused .m::after{content:"";position:absolute;left:0;top:50%;width:13px;height:1.5px;
  background:currentColor;transform:rotate(-38deg);transform-origin:left center}
/* The half-filled machine appears nowhere else. That is what stops "installed but not verified"
   being read as success. */
.ss-half{border-color:var(--warn-line);background:var(--warn-bg);color:var(--warn-ink)}
.ss-half .m{background:linear-gradient(90deg,currentColor 50%,transparent 50%)}

/* ── stage rail ────────────────────────────────────────────────────────────── */
.rail{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:8px}
.rail .seg{height:4px;border-radius:2px;background:var(--line);overflow:hidden;position:relative}
.rail .seg.done{background:var(--run-good)}
.rail .seg.now::after{content:"";position:absolute;inset:0;width:35%;background:var(--run-working);
  animation:forge-advance 1.9s linear infinite}
.rail .seg.now.stalled::after{animation:none;opacity:.5}
.raill{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:8px;margin-top:8px}
.raill span{font-size:12px;color:var(--ink-faint);text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.raill span.done{color:var(--run-good)}
.raill span.now{color:var(--run-working);font-weight:500}

/* ── activity chip: the working-vs-stuck answer ────────────────────────────── */
.chip{display:inline-flex;align-items:center;gap:9px;height:32px;padding:0 13px;border-radius:16px;
  background:var(--sunken);border:1px solid var(--line);font-family:var(--mono);font-size:12px;
  font-variant-numeric:tabular-nums;color:var(--ink-muted)}

/* ── project chip strip ────────────────────────────────────────────────────── */
.chips{display:flex;gap:8px;overflow-x:auto;padding:12px 0 6px;scrollbar-width:none}
.chips::-webkit-scrollbar{display:none}
/* The spec draws these 36px tall; rule 2 requires a 44px hit target. Both are honoured with real
   geometry rather than a pseudo-element (which gets painted over by whatever follows it): the
   button is a transparent 44px box, the visible pill inside it is 36px. */
.chips button{display:inline-flex;align-items:center;height:44px;min-height:44px;padding:0;border:0;
  background:none;cursor:pointer;flex:none}
.chips button .pill{display:inline-flex;align-items:center;gap:8px;height:36px;padding:0 14px;
  border-radius:18px;background:var(--raised);border:1px solid var(--line);color:var(--ink);
  font-size:15px;white-space:nowrap}
.chips button[aria-selected="true"] .pill{background:var(--ink);border-color:var(--ink);color:var(--raised)}

/* ── log / commands ────────────────────────────────────────────────────────── */
.log{background:var(--sunken);border:1px solid var(--line);border-radius:10px;padding:12px 13px;
  font-family:var(--mono);font-size:12px;line-height:1.75;color:var(--ink-2);
  white-space:pre-wrap;word-break:break-word;max-height:42vh;overflow:auto}
.log .new{color:var(--run-working)}
.log .bad{color:var(--err-ink)}
.caret{display:inline-block;width:7px;height:13px;background:var(--ink-2);vertical-align:-2px;
  animation:forge-breathe 1.2s steps(1) infinite}
.cmd{background:var(--sunken);border:1px solid var(--line);border-radius:10px;padding:13px;
  font-family:var(--mono);font-size:14px;line-height:1.6;color:var(--ink);
  user-select:all;-webkit-user-select:all;word-break:break-all}
.cmd.expired{opacity:.45;text-decoration:line-through}

/* ── step-up: one component, two placements, visually identical ────────────── */
.stepup{background:var(--accent-tint);border:1.5px solid var(--accent-tint-line);border-radius:16px;
  padding:16px;margin:0 -4px}
.stepup .hd{display:flex;align-items:center;gap:9px;margin-bottom:10px}
.stepup .hd .ulabel{color:var(--accent-active);margin:0}
.stepup .say{font-size:15px;color:var(--ink);margin:0 0 12px}
.stepup input{width:100%;min-height:50px;border-radius:10px;background:var(--raised);
  border:1px solid var(--accent-tint-line);padding:12px 13px;font-size:19px;letter-spacing:.16em;color:var(--ink)}
.stepup input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-focus-glow)}
.stepup input.code{font-family:var(--mono);font-size:22px;letter-spacing:.34em;text-align:center}
.stepup .foot{margin-top:10px}

/* ── raw error, three layers ───────────────────────────────────────────────── */
.steps{counter-reset:s}
.steps > li{display:flex;gap:11px;padding:9px 0;font-size:15px;line-height:1.35;list-style:none}
.steps > li::before{counter-increment:s;content:counter(s);font-family:var(--mono);font-size:13px;
  color:var(--accent);flex:none;min-width:14px}
.steps{padding:0;margin:0}
details.exact summary{cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center}
details.exact summary::-webkit-details-marker{display:none}
details.exact .hide{font-size:13px;color:var(--accent)}

/* ── sheets ────────────────────────────────────────────────────────────────── */
.sheet{position:fixed;inset:0;background:rgba(20,23,30,.32);display:none;align-items:flex-end;z-index:50}
.sheet.on{display:flex}
.sheet .inner{background:var(--raised);width:100%;max-height:92vh;overflow:auto;
  border-radius:26px 26px 0 0;padding:16px var(--gutter) 0;display:flex;flex-direction:column}
.grab{width:40px;height:4px;background:var(--line-strong);border-radius:2px;margin:0 auto 16px;flex:none}
.sheetbody{flex:1}
.sheetfoot{position:sticky;bottom:0;background:var(--raised);margin-top:18px;
  padding:12px 0 calc(16px + env(safe-area-inset-bottom));border-top:1px solid var(--line-soft)}
.sheetfoot > * + *{margin-top:10px}

/* ── step indicator (add a server, 1 of 2) ─────────────────────────────────── */
.steps2{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.steps2 .b{width:22px;height:3px;border-radius:2px;background:var(--line)}
.steps2 .b.on{background:var(--ink)}

.toast{position:fixed;left:var(--gutter);right:var(--gutter);bottom:calc(76px + env(safe-area-inset-bottom));
  background:var(--ink);color:var(--ground);border-radius:12px;padding:14px 16px;z-index:60;display:none;
  font-size:15px;box-shadow:0 12px 32px -12px rgba(20,23,30,.4)}
.toast.on{display:block}
.thumb{max-width:100%;border-radius:10px;margin-top:10px;border:1px solid var(--line)}
.center{text-align:center}
/* ── 12 Activity ───────────────────────────────────────────────────────────────
   Turns lead and the figure follows, because turns are the only real ceiling here. Neither is ever
   drawn as a bar or a meter: a ratio is a fact about room left, not a loading state, and the money
   is not money at all — nothing on this screen is charged. */
.figure{font-size:34px;font-weight:600;letter-spacing:-.03em;line-height:1.05;
  font-variant-numeric:tabular-nums;color:var(--ink)}
.prov{margin:12px -15px -15px;padding:12px 15px;background:var(--sunken);
  border-top:1px solid var(--line-soft);border-radius:0 0 13px 13px;
  font-size:13px;line-height:1.4;color:var(--ink-muted)}
.rowset.inset{background:var(--sunken);border-radius:10px;padding:2px 12px}
.turnrow{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-top:12px}
.turns{font-family:var(--mono);font-size:22px;font-variant-numeric:tabular-nums;
  color:var(--ink);margin:0;line-height:1.1}
.chiprow{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.mchip{display:inline-block;font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.06em;
  padding:4px 8px;border:1px solid var(--line);border-radius:6px;color:var(--ink-muted);white-space:nowrap}
.ulabel.sect{display:flex;align-items:center;gap:8px;margin:24px 0 10px}
/* A finished run. The whole row is the target — 44px minimum, real geometry. */
button.rowbtn{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  min-height:52px;padding:11px 15px;background:var(--raised);border:1px solid var(--line);
  border-radius:14px;text-align:left;cursor:pointer;color:var(--ink);font-size:15px}
button.rowbtn + button.rowbtn{margin-top:8px}
button.rowbtn:hover{border-color:var(--line-strong)}
button.rowbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
button.rowbtn .l{display:inline-flex;align-items:center;gap:10px;min-width:0}
button.rowbtn .nm{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
button.rowbtn .r{flex:none;font-size:12px}
/* Nothing broke, so this is the warning surface — not the error surface. */
.warncard{background:var(--warn-bg);border:1px solid var(--warn-line);border-radius:14px;padding:15px}
.warncard .h-outcome{font-size:31px}
/* Which machine: a row, not a field, with the hostname in mono and a Change affordance. The native
   select sits invisibly across the whole row, so the picker is the platform's own and the target is
   the full row rather than a small control at the end of it. */
.pickrow{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:6px 0 14px;margin-bottom:2px}
.pick{position:relative;display:inline-flex;align-items:center;gap:10px;min-height:44px;
  padding:0 2px;cursor:pointer}
.pick .mono{font-size:15px;color:var(--ink)}
.pick .chg{font-size:15px;color:var(--accent)}
.pick select{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;
  -webkit-appearance:none;appearance:none;border:0;font-size:16px}
.pick:focus-within{outline:2px solid var(--accent);outline-offset:3px;border-radius:8px}

/* What kind of thing it is. Three values, all visible — a select would hide two of the three
   behind a tap and they are the whole choice. 44px target, 38px pill, real geometry. */
.kchips{display:flex;gap:8px;flex-wrap:wrap}
.kchips button{display:inline-flex;align-items:center;min-height:44px;padding:0;border:0;
  background:none;cursor:pointer;-webkit-tap-highlight-color:transparent}
.kchips button > span{display:inline-flex;align-items:center;height:38px;padding:0 15px;
  border-radius:19px;border:1px solid var(--line-strong);background:var(--raised);
  font-family:var(--mono);font-size:13px;color:var(--ink-2)}
.kchips button[aria-checked="true"] > span{border:1.5px solid var(--accent);
  background:var(--accent-tint);color:var(--accent-active);font-weight:500}
.kchips button:focus-visible > span{outline:2px solid var(--accent);outline-offset:2px}
/* One row for everything the form writes but does not lead with. Same affordance as
   "Show all 34 lines" — the app has one way of saying "there is more underneath". */
button.disclose{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  min-height:44px;padding:0 2px;margin:2px 0 14px;border:0;background:none;cursor:pointer;
  border-top:1px solid var(--line-soft);padding-top:12px}
button.disclose .n{font-size:15px;color:var(--accent)}
button.disclose .adv{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.06em;
  color:var(--ink-muted)}
button.disclose:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:8px}
/* Publishing. A held-back row stays where it was, struck through, with its Undo — the exclusion
   happens here, not in a settings page found afterwards. */
.pubrow{display:flex;align-items:center;gap:10px;min-height:44px;padding:8px 0}
.pubrow + .pubrow{border-top:1px solid var(--line-soft)}
.pubrow .p{flex:1;min-width:0;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pubrow .c{flex:none;font-size:12px}
.pubrow.out .p{text-decoration:line-through;color:var(--ink-faint)}
.pubrow.out .c{color:var(--ink-faint)}
button.hb{flex:none;min-height:44px;padding:0 10px;border:0;background:none;cursor:pointer;
  color:var(--accent);font-size:13px}
button.hb:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:8px}
.card.warn{background:var(--warn-bg);border-color:var(--warn-line)}
.mut{color:var(--ink-muted)}
.sp{display:flex;align-items:center;justify-content:space-between;gap:12px}

/* ── desktop: not a stretched phone ────────────────────────────────────────── */
@media (min-width:1024px){
  .wrap{max-width:1180px;padding:0 28px}
  .tabbar{position:sticky;bottom:auto;top:0;border-top:0;border-bottom:1px solid var(--line);max-width:1180px;margin:0 auto}
  .desk{display:grid;grid-template-columns:236px minmax(0,1fr) 280px;gap:28px;align-items:start}
  .desk .rail-l,.desk .rail-r{position:sticky;top:76px}
  .chips{display:none}
  .desk-only{display:block}
}
.desk-only{display:none}


/* -- handset scale -------------------------------------------------------------
   The scale above was drawn for a large phone. On a 13 mini the usable viewport is
   about 630px tall, and the header, pinned footer and tab bar were taking 251px of
   it - 40% of the screen spent on chrome before a single server appeared. This block
   pulls the type and the control heights down to suit the small end. Every tap
   target stays at or above 44px; nothing here is smaller than that. Desktop is
   untouched - this query does not match at 1024px. */
@media (max-width:640px){
  html,body{font-size:16px}

  .app-header .row{height:48px}
  .brand{font-size:16px}
  .conn{height:27px;padding:0 10px;font-size:10px}

  .h-statement{font-size:31px;letter-spacing:-.03em}
  .h-outcome{font-size:25px}
  .h-screen{font-size:22px}
  .h-sheet{font-size:19px;margin:0 0 12px}
  .body{font-size:16px}
  .dense{font-size:14px}
  .acct-name{font-size:16px}

  /* 46 and 44 - still above the 44px floor, but 6px and 4px shorter than before, which is
     18px off the pinned footer on the screens that carry two buttons. */
  button.btn{min-height:46px;font-size:16px;border-radius:11px}
  button.btn2{min-height:44px;font-size:15px;border-radius:11px}
  .btnrow{gap:8px}

  input.f,textarea.f,select.f{min-height:44px;font-size:16px;padding:10px 12px}
  textarea.f{min-height:110px}
  .field{margin-bottom:14px}
  .pw input.f{padding-right:50px}
  .lf{min-height:46px;font-size:16px}
  #p,#t{font-size:18px}
  .log{line-height:1.6}
  .chips button .pill{height:34px;font-size:14px;padding:0 12px}

  .card{padding:13px;border-radius:12px}
  .card + .card{margin-top:10px}
  .rowset > div{padding:9px 0;font-size:14px}

  .viewfoot{padding:10px 0 12px}
  .viewfoot .wrap > * + *{margin-top:8px}
  .tabbar button{min-height:51px;font-size:12px;gap:3px}
}
