/* Keyda Business dashboard.
   ONE product, one look: these tokens, the type scale and the component
   shapes are taken from business-site/site.css so the marketing site, the
   docs and the app a customer signs into are visibly the same thing. If you
   change a colour or a radius here, change it there too.
   Dark mode is the owner's choice, not the OS's alone: `data-theme` on <html>
   wins in BOTH directions so an explicit Light stays light on a dark machine,
   and "System" (no attribute) follows prefers-color-scheme. Every colour is
   defined on bare :root first — a token whose ONLY definition lived inside a
   media query would be undefined for half the users. */
:root{
  /* Page is the site's tinted section colour; cards are its white. */
  --bg:#f7f8fc; --surface:#ffffff; --surface-2:#f1f3f9; --line:#e6e9f2;
  --ink:#101425; --ink-2:#3d4560; --ink-3:#727b96;
  --brand:#3b4ee0; --brand-ink:#fff; --brand-soft:#eef0ff;
  --ok:#0d9b6c; --ok-soft:#e3f7ef; --warn:#b8760b; --warn-soft:#fdf3e0;
  --bad:#d33b4d; --bad-soft:#fdeaec;
  --topbar:rgba(255,255,255,.86);
  --field:#f7f8fc;
  --r:12px; --r-lg:18px;
  --sh:0 1px 2px rgba(16,20,37,.05), 0 12px 34px -18px rgba(16,20,37,.28);
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#0b0e1a; --surface:#141a2e; --surface-2:#1c2340; --line:#29304f;
    --ink:#eef1fa; --ink-2:#aab3ce; --ink-3:#7d88a6;
    --brand:#8b98ff; --brand-ink:#0b0e1a; --brand-soft:#242c50;
    --ok:#35c795; --ok-soft:#11301f; --warn:#e0a53a; --warn-soft:#332612;
    --bad:#ff7183; --bad-soft:#3a1720;
    --topbar:rgba(20,26,46,.86);
    --field:#0f1424;
    --sh:0 1px 2px rgba(0,0,0,.34), 0 14px 36px -20px rgba(0,0,0,.7);
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  color-scheme:dark;
  --bg:#0b0e1a; --surface:#141a2e; --surface-2:#1c2340; --line:#29304f;
  --ink:#eef1fa; --ink-2:#aab3ce; --ink-3:#7d88a6;
  --brand:#8b98ff; --brand-ink:#0b0e1a; --brand-soft:#242c50;
  --ok:#35c795; --ok-soft:#11301f; --warn:#e0a53a; --warn-soft:#332612;
  --bad:#ff7183; --bad-soft:#3a1720;
  --topbar:rgba(20,26,46,.86);
  --field:#0f1424;
  --sh:0 1px 2px rgba(0,0,0,.34), 0 14px 36px -20px rgba(0,0,0,.7);
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--ink);font:15px/1.6 Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;-webkit-font-smoothing:antialiased}
a{color:var(--brand);text-decoration:none}
h1,h2,h3{margin:0;line-height:1.18;letter-spacing:-.02em;font-weight:700}
h1{font-size:23px}h2{font-size:17px}h3{font-size:14px}
p{margin:0}
.muted{color:var(--ink-2)}.dim{color:var(--ink-3)}.small{font-size:13px}.tiny{font-size:12px}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}
.boot{display:grid;place-items:center;height:100vh}
/* ── layout ─────────────────────────────────────────────────────────── */
.shell{display:grid;grid-template-columns:248px 1fr;grid-template-rows:auto 1fr;min-height:100vh}
/* Same translucent, blurred bar the marketing site uses for its nav. */
.shellbar{grid-column:1/-1;display:flex;align-items:center;gap:12px;padding:9px 20px;font-size:13.5px;line-height:1.35}
.shellbar.ops{background:var(--warn-soft);color:var(--warn);font-weight:600}
.shellbar.mail{background:var(--surface-2);color:var(--ink-2);border-bottom:1px solid var(--line)}
.shellbar .grow{flex:1;min-width:0}
.topbar{grid-column:1/-1;display:flex;align-items:center;gap:12px;padding:12px 20px;background:var(--topbar);backdrop-filter:blur(10px);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20}
.brand{display:flex;align-items:center;gap:9px;font-weight:800;letter-spacing:-.02em}
.brand .dot{width:26px;height:26px;border-radius:8px;background:var(--brand);display:grid;place-items:center;color:#fff;font-size:14px;font-weight:800}
.brand small{font-weight:500;color:var(--ink-3);font-size:12px;padding-inline-start:6px;border-inline-start:1px solid var(--line)}
.spacer{flex:1}
.sidebar{background:var(--surface);border-inline-end:1px solid var(--line);padding:14px 10px;display:flex;flex-direction:column;gap:18px}
.navgroup>.label{font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);padding:0 8px 7px}
.navitem{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r);color:var(--ink-2);font-weight:500;cursor:pointer;border:none;background:none;width:100%;text-align:start;font-size:14px;font-family:inherit}
.navitem:hover{background:var(--surface-2);color:var(--ink)}
.navitem.on{background:var(--brand-soft);color:var(--brand);font-weight:600}
.navitem .ic{width:18px;text-align:center;font-size:14px}
.navitem .badge{margin-inline-start:auto;background:var(--bad);color:#fff;border-radius:999px;font-size:11px;font-weight:700;padding:1px 6px}
.navitem.soon{opacity:.5;cursor:default}
.main{padding:26px 32px 64px;max-width:1240px;width:100%;margin:0 auto}
.head{display:flex;align-items:flex-start;gap:14px;margin-bottom:18px;flex-wrap:wrap}
.head .grow{flex:1;min-width:220px}
.head .sub{color:var(--ink-2);font-size:14px;margin-top:3px}
/* ── components ─────────────────────────────────────────────────────── */
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh);padding:16px}
.card+.card{margin-top:16px}
.card .ttl{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.card .desc{color:var(--ink-2);font-size:13px;margin-bottom:12px}
.grid{display:grid;gap:14px}
.grid.c2{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.grid.c3{grid-template-columns:repeat(auto-fit,minmax(215px,1fr))}
.grid.c4{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px}
.stat .n{font-size:26px;font-weight:700;letter-spacing:-.02em}
.stat .k{font-size:12px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;font-weight:600}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:9px 14px;border-radius:var(--r);border:1px solid var(--line);background:var(--surface);color:var(--ink);font:inherit;font-size:14px;font-weight:550;cursor:pointer;transition:.12s}
.btn:hover{background:var(--surface-2)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn.pri{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.btn.pri:hover{filter:brightness(1.08)}
.btn.danger{color:var(--bad);border-color:var(--bad)}
.btn.danger:hover{background:var(--bad-soft)}
.btn.sm{padding:6px 10px;font-size:13px}
.btn.link{border:none;background:none;color:var(--brand);padding:4px 6px}
.row{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
label.f{display:block;margin-bottom:12px}
label.f>span{display:block;font-size:13px;font-weight:600;margin-bottom:5px}
label.f>span em{font-weight:400;color:var(--ink-3);font-style:normal}
/* Text-like controls only. A checkbox, radio, range or file input is not a
   text box: the width:100% + padding + border here turned every checkbox
   into a full-row empty field with the real control centred inside it, and
   the flex row it sat in then wrapped its label underneath. */
input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),textarea,select{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:var(--r);background:var(--field);color:var(--ink);font:inherit;font-size:14px;transition:border-color .12s,box-shadow .12s}
input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):focus,textarea:focus,select:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
input[type=checkbox],input[type=radio]{width:18px;height:18px;flex:0 0 auto;margin:0;padding:0;accent-color:var(--brand);cursor:pointer}
input[type=checkbox]:focus-visible,input[type=radio]:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
input[type=range]{width:100%;height:auto;padding:0;border:0;background:transparent;accent-color:var(--brand);cursor:pointer}
input[type=range]:focus-visible{outline:2px solid var(--brand);outline-offset:3px;border-radius:999px}
/* A labelled on/off row: the box, a title and one line saying why. Used for
   every yes/no setting so they all read the same way. */
label.switch{display:flex;align-items:flex-start;gap:12px;margin-top:12px;padding:12px 14px;border:1px solid var(--line);border-radius:var(--r);background:var(--surface);cursor:pointer;transition:border-color .12s,background .12s}
label.switch:hover{background:var(--surface-2)}
label.switch:has(input:checked){border-color:var(--brand)}
label.switch>input[type=checkbox]{margin-top:3px}
label.switch>.grow{flex:1;min-width:0}
label.switch .t{font-weight:600;font-size:14.5px;line-height:1.35}
label.switch .tiny{margin-top:3px}
textarea{min-height:78px;resize:vertical}
.pill{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:650}
.pill.ok{background:var(--ok-soft);color:var(--ok)}
.pill.warn{background:var(--warn-soft);color:var(--warn)}
.pill.bad{background:var(--bad-soft);color:var(--bad)}
.pill.neutral{background:var(--surface-2);color:var(--ink-2)}
.list{border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.item{display:flex;gap:11px;align-items:center;padding:11px 13px;border-bottom:1px solid var(--line);background:var(--surface)}
.item:last-child{border-bottom:none}
.item .grow{flex:1;min-width:0}
.item .t{font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.empty{padding:26px;text-align:center;color:var(--ink-3);font-size:14px}
.code{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r);padding:12px;font-family:ui-monospace,Menlo,monospace;font-size:12.5px;overflow-x:auto;white-space:pre;color:var(--ink)}
.check{display:flex;gap:11px;align-items:flex-start;padding:10px 0;border-bottom:1px dashed var(--line)}
.check:last-child{border:none}
.check .box{width:20px;height:20px;border-radius:6px;display:grid;place-items:center;font-size:12px;font-weight:800;flex:0 0 auto}
.check .box.y{background:var(--ok);color:#fff}
.check .box.n{background:var(--surface-2);color:var(--ink-3);border:1px solid var(--line)}
.spinner{width:16px;height:16px;border:2px solid var(--line);border-top-color:var(--brand);border-radius:50%;animation:sp .7s linear infinite}
.spinner.lg{width:30px;height:30px;border-width:3px}
@keyframes sp{to{transform:rotate(360deg)}}
.toasts{position:fixed;inset-inline-end:16px;bottom:16px;display:flex;flex-direction:column;gap:8px;z-index:100}
.toast{background:var(--ink);color:var(--bg);padding:11px 14px;border-radius:var(--r);box-shadow:var(--sh);font-size:14px;max-width:380px;animation:in .18s}
.toast.err{background:var(--bad);color:#fff}
.toast.ok{background:var(--ok);color:#fff}
@keyframes in{from{opacity:0;transform:translateY(6px)}}
/* auth */
.auth{min-height:100vh;display:grid;place-items:center;padding:20px}
.auth .box{width:100%;max-width:392px}
.auth .card{padding:26px}
/* The mark on the sign-in card. Slightly larger than the sidebar's — this is
   the first screen anyone sees and there is room for it. */
.authbrand{margin-bottom:18px}
.authbrand .brand{font-size:19px}
.authbrand .brand .logo{width:34px;height:34px}
.authbrand .brand .dot{width:32px;height:32px;font-size:17px}
.auth h1{margin-bottom:6px}
.hr{display:flex;align-items:center;gap:10px;color:var(--ink-3);font-size:12px;margin:16px 0}
.hr:before,.hr:after{content:"";flex:1;height:1px;background:var(--line)}
/* chat */
.chat{display:flex;flex-direction:column;gap:10px;max-height:52vh;overflow-y:auto;padding:4px}
.msg{max-width:78%;padding:9px 12px;border-radius:14px;font-size:14px;white-space:pre-wrap;word-break:break-word}
.msg.u{align-self:flex-end;background:var(--brand);color:var(--brand-ink);border-end-end-radius:4px}
.msg.b{align-self:flex-start;background:var(--surface-2);border-end-start-radius:4px}
.msg .meta{display:block;margin-top:5px;font-size:11px;opacity:.75}
/* Rendered bot text (richText in ui.js). The bubble itself is pre-wrap for
   the owner's plainly-typed side; the rendered side turns that off because
   its paragraphs, breaks and lists are real nodes and pre-wrap would double
   every gap. */
.msg .rich{white-space:normal}
.msg .rich p,.msg .rich ul,.msg .rich ol{margin:0}
.msg .rich p+p,.msg .rich p+ul,.msg .rich p+ol,.msg .rich ul+p,.msg .rich ol+p,.msg .rich p+.hh{margin-top:7px}
.msg .rich ul,.msg .rich ol{padding-inline-start:19px;display:flex;flex-direction:column;gap:3px}
.msg .rich strong{font-weight:650}
.msg .rich .hh{font-weight:700;font-size:14.5px}
.msg .rich a{color:var(--brand);font-weight:600;text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}
.msg .rich code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;background:var(--surface);padding:1px 5px;border-radius:5px}
.tabs{display:flex;gap:5px;border-bottom:1px solid var(--line);margin-bottom:14px;overflow-x:auto}
.tab{padding:8px 12px;border:none;background:none;color:var(--ink-2);font:inherit;font-size:14px;font-weight:550;cursor:pointer;border-bottom:2px solid transparent}
.tab.on{color:var(--brand);border-bottom-color:var(--brand)}
.modal{position:fixed;inset:0;background:rgba(10,14,30,.55);display:grid;place-items:center;z-index:200;padding:20px}
.modal .card{max-width:440px;width:100%}
@media(max-width:820px){
  .shell{grid-template-columns:1fr}
  .sidebar{flex-direction:row;overflow-x:auto;border-inline-end:none;border-bottom:1px solid var(--line);padding:8px}
  .navgroup{display:flex;align-items:center;gap:4px}
  .navgroup>.label{display:none}
  .main{padding:14px}
}
/* ── phones. The topbar was the ONE element whose nowrap flex min-content
   (≈588px) set the width of the whole app — every screen then bled ~200px
   off the right edge of a 390px phone, taking the language/theme/sign-out
   controls with it. Wrapping it is the entire fix; the rest is fit and
   finish at that size. Proven by ablation: hide .topbar and the page
   measures exactly the viewport again. */
@media(max-width:640px){
  .topbar{flex-wrap:wrap;gap:8px 10px;padding:10px 12px}
  .topbar select{min-width:0;max-width:46vw}
  .main{padding:12px}
  .head{gap:10px}
  .card{padding:14px}
}

/* ── uploads: drop zone + visible queue ─────────────────────────────────
   The queue is the point. A bare <input type=file> shows one filename and
   silently replaces it on the next pick, so an owner adding six policies had
   no way to see what was about to be sent. */
.drop{border:1.5px dashed var(--line);border-radius:var(--r-lg);background:var(--surface-2);padding:20px;text-align:center;transition:.14s}
.drop.over{border-color:var(--brand);background:var(--brand-soft)}
.dropin{display:flex;flex-direction:column;align-items:center;gap:7px}
.dropin strong{font-size:14.5px}
.dropin .tiny{max-width:420px}
.filelist{margin-top:10px;display:flex;flex-direction:column;gap:6px}
.fileitem{display:flex;align-items:center;gap:10px;padding:8px 11px;border:1px solid var(--line);border-radius:var(--r);background:var(--surface);font-size:13.5px}
.fileitem .fname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:550}
.fileitem .x{border:none;background:none;color:var(--ink-3);cursor:pointer;font-size:14px;line-height:1;padding:2px 4px;border-radius:6px;font-family:inherit}
.fileitem .x:hover{background:var(--bad-soft);color:var(--bad)}

/* ── policies: type it / link it / upload it ────────────────────────────── */
.policies{display:flex;flex-direction:column;gap:12px}
.policy{border:1px solid var(--line);border-radius:var(--r-lg);padding:13px 14px;background:var(--surface)}
.policy .prow{display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap}
.policy label.f{margin-bottom:0}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:999px;overflow:hidden;background:var(--surface-2)}
.segb{border:none;background:none;font:inherit;font-size:13px;font-weight:550;color:var(--ink-2);padding:6px 13px;cursor:pointer;transition:.12s}
.segb:hover{color:var(--ink)}
.segb.on{background:var(--brand);color:#fff}

/* A non-fatal disclosure on a source that worked but was only partly read. */
.warnline{color:var(--warn);margin-top:3px}

/* A drafted answer awaiting approval — visibly not yet live. */
.item.draft{border-inline-start:3px solid var(--brand);background:var(--surface)}

/* Keep inputs on a grid row aligned even when one label's hint wraps to a
   second line — otherwise a longer hint shunts its field half a row down and
   the form reads as broken. */
.grid.c2,.grid.c3,.grid.c4{align-items:stretch}
.grid label.f{display:flex;flex-direction:column;height:100%;margin-bottom:0}
.grid label.f>input,.grid label.f>textarea,.grid label.f>select{margin-top:auto}

/* ── brand mark + theme toggle ──────────────────────────────────────────── */
.brand .logo{width:28px;height:28px;border-radius:8px;object-fit:contain;display:block}
.btn.icon{width:34px;padding:0;font-size:15px;line-height:1;flex:0 0 auto}
@media(max-width:1100px){.hide-sm{display:none}}

/* ── polish ─────────────────────────────────────────────────────────────
   Wider gutters, a clearer heading rhythm and more generous cards. The old
   values were tuned for a 1080px column; at full width the screen read as a
   narrow strip of dense boxes pushed against the sidebar. */
.head{margin-bottom:22px}
.head h1{font-size:25px}
.card{padding:20px 22px}
.card+.card{margin-top:16px}
.card .ttl{margin-bottom:6px}
.card .ttl h2{font-size:17.5px}
.card .desc{font-size:13.5px;margin-bottom:14px;max-width:78ch}
.grid{gap:16px}
.stat{padding:16px 18px;transition:border-color .14s}
.stat:hover{border-color:var(--brand)}
.stat .n{font-size:28px;line-height:1.1}
.stat .k{margin-top:4px}
.empty{padding:34px 26px;line-height:1.5}
.list{border-radius:var(--r-lg)}
.item{padding:13px 15px}
.item:hover{background:var(--surface-2)}
.tabs{gap:2px;margin-bottom:18px}
.tab{padding:9px 14px;font-size:14.5px}
.btn{border-radius:10px}
.pill{padding:3px 10px}

/* Contextual help — every screen should be able to point at the guide that
   explains it without the owner hunting through the docs hub. */
.helpline{display:flex;align-items:center;gap:6px;margin-top:10px;font-size:13px;color:var(--ink-3)}
.helpline a{font-weight:600}

@media(max-width:820px){
  .main{padding:16px 14px 48px}
  .card{padding:16px}
}

/* Line-icon buttons (the theme toggle). Sized here so the SVG never inherits
   a text font-size and lands off-centre next to real buttons. */
.btn.icon{width:36px;height:36px}
.btn.icon svg{width:17px;height:17px;display:block}

/* ── action spacing ─────────────────────────────────────────────────────
   A button that follows a field must never touch it.

   Fields inside a .grid have margin-bottom:0 (the grid's own `gap` spaces
   them, and equal heights are what keeps inputs on a row aligned). That left
   the Save button flush against the input below every such block — Business
   settings, Train, Install and anywhere else the same shape is used. Spacing
   belongs to the ACTION, not the field, so it is stated once here instead of
   as an inline style per view. */
.card > .row,
.card > .btn,
.grid + .row, .grid + .btn,
label.f + .row, label.f + .btn,
.list + .row, .list + .btn,
.drop + .row, .filelist + .row,
.code + .row, .code + .btn{margin-top:16px}
/* …but a row that is already the first thing in a card needs no gap. */
.card > .ttl + .row{margin-top:10px}

/* ── block rhythm ───────────────────────────────────────────────────────
   The previous rules only spaced things INSIDE a card. Half the layout is
   siblings AFTER one: the "Save details" button that follows the Policies
   card, the platform tabs that follow the key card, a guide card that follows
   a wrapper. Those all rendered flush against the card above them.

   Adjacent margins collapse in normal flow, so a card following .head or
   .tabs still gets one gap, not two. */
* + .card{margin-top:16px}
.card + .row, .card + .btn, .card + .tabs, .card + .grid, .card + .list{margin-top:16px}

/* A panel that only appears once you pick something from the list above it.
   :empty keeps it from reserving a gap while nothing is open. */
.detail:not(:empty){margin-top:16px}

/* A drafted answer is a little FORM (two inputs and two buttons), not a list
   row — flush rows read as one run-on block. */
.item.draft{padding:16px 15px}
.item.draft + .item.draft{border-top:1px solid var(--line)}

/* The drop zone's own button was 7px under its hint line. */
.dropin{gap:10px}

/* A field that follows a grid — the fallback/escalation boxes under the
   two-column block on Install — had no gap, because grid fields carry no
   bottom margin and the grid's own `gap` does not reach outside it. */
.grid + label.f, .grid + .f{margin-top:14px}

/* A touch more air under a field's label; 5px was tight once the control
   was something taller than an input (a row of colour swatches). */
label.f>span{margin-bottom:7px}

/* Drafted answers stack as their own small cards. They are forms — two
   inputs and two buttons each — so flush list rows read as one run-on block. */
.stack{display:flex;flex-direction:column;gap:12px}
.draft{border:1px solid var(--line);border-inline-start:3px solid var(--brand);border-radius:var(--r);background:var(--surface);padding:14px 15px}
.ttl + .list, .desc + .list{margin-top:10px}

/* ── device preview (Test screen) ────────────────────────────────────────
   The iframe renders at TRUE device width and is scaled down with a
   transform, so the page inside lays out exactly as it would on that device.
   Scaling the frame instead would just squeeze a desktop layout and show the
   owner something no visitor will ever see. */
.stage{
  display:flex; justify-content:center; padding:22px 16px;
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:var(--r-lg); overflow-x:auto;
}
.device{flex:0 0 auto;background:var(--surface);box-shadow:0 18px 46px -22px rgba(16,20,37,.5)}
.device .viewport{overflow:hidden;position:relative;background:#fff}
.device .screen{border:0;display:block;transform-origin:top left}

/* A browser window. */
.device.desktop{border:1px solid var(--line);border-radius:12px;overflow:hidden}
.device.desktop .chrome{
  display:flex; align-items:center; gap:6px; height:34px; padding:0 12px;
  background:var(--surface-2); border-bottom:1px solid var(--line);
}
.device.desktop .chrome i{width:9px;height:9px;border-radius:50%;background:var(--line)}
.device.desktop .chrome .addr{
  margin-inline-start:10px; flex:1; background:var(--surface); border:1px solid var(--line);
  border-radius:999px; font-size:11.5px; color:var(--ink-3); padding:3px 12px;
  text-align:center; max-width:280px;
}

/* A phone. */
.device.phone{border:9px solid #12172a;border-radius:34px;padding:0;position:relative;overflow:hidden}
.device.phone .viewport{border-radius:25px}
.device.phone .notch{
  position:absolute; top:0; inset-inline-start:50%; transform:translateX(-50%);
  width:110px; height:20px; background:#12172a; border-radius:0 0 12px 12px; z-index:2;
}
/* While the pass is being issued and the frame mounts. Given the stage's own
   height so switching tabs does not collapse the card and jump the page. */
/* Bot link: the code and its explanation sit side by side, and stack on a
   narrow screen rather than shrinking the code — a QR below a usable size is
   not a smaller QR, it is an unscannable one. */
.linkqr{display:flex;gap:22px;align-items:flex-start;margin-top:18px;flex-wrap:wrap}
.qr{flex:0 0 auto;background:#fff;padding:12px;border:1px solid var(--line);border-radius:var(--r-lg)}
.qr canvas{display:block;image-rendering:pixelated}
.qrnote{flex:1 1 260px;min-width:240px}
.qrnote h3{font-size:14.5px;margin:0 0 6px;letter-spacing:-.01em}
/* Step headings inside an install guide. */
.sub2{font-size:13px;font-weight:700;letter-spacing:.02em;text-transform:uppercase;color:var(--ink-3);margin:18px 0 8px}
.card .sub2:first-of-type{margin-top:14px}
/* A card that is telling the owner something is wrong, without shouting. */
.warncard{border-color:var(--warn);background:var(--warn-soft)}
.warncard .desc{color:var(--ink-2)}
.stageload{display:flex;align-items:center;justify-content:center;min-height:420px;width:100%}
.previewnote{margin-top:12px;max-width:70ch}
@media(max-width:820px){ .stage{padding:14px 10px} }

/* The bezel must sit OUTSIDE the viewport, not eat into it. With the default
   border-box the 9px phone bezel stole 18px from a 390px screen, so the chat
   page laid out at 390 and then had its right edge — including the Send
   button — clipped away. */
.device{box-sizing:content-box}
.device .viewport{box-sizing:content-box}

/* Country dial-code picker (sign-in).
   The panel opens UPWARD: the sign-in card is vertically centred, so a
   downward panel lands under the phone's on-screen keyboard exactly when the
   number field has focus. */
.cc{position:relative}
.cc-btn{display:flex;align-items:center;gap:7px;width:100%;padding:10px 10px;border:1px solid var(--line);border-radius:var(--r);background:var(--field);color:var(--ink);font:inherit;font-size:14px;cursor:pointer;transition:border-color .12s,box-shadow .12s}
.cc-btn:hover{border-color:var(--ink-3)}
.cc-btn:focus-visible{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.cc-btn[aria-expanded="true"]{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.cc-flag{font-size:16px;line-height:1}
.cc-code{font-variant-numeric:tabular-nums;font-weight:600}
.cc-caret{margin-inline-start:auto;width:0;height:0;border:4px solid transparent;border-top-color:var(--ink-3);margin-top:3px}
.cc-pop{position:absolute;bottom:calc(100% + 6px);inset-inline-start:0;z-index:40;width:290px;max-width:78vw;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh);overflow:hidden}
.cc-pop.down{bottom:auto;top:calc(100% + 6px)}
.cc-pop[hidden]{display:none}
.cc-searchwrap{padding:9px;border-bottom:1px solid var(--line)}
.cc-search{font-size:14px}
.cc-list{max-height:236px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.cc-item{display:flex;align-items:center;gap:9px;padding:9px 11px;cursor:pointer;font-size:14px;border-bottom:1px solid var(--line)}
.cc-item:last-child{border-bottom:none}
.cc-item:hover,.cc-item.on{background:var(--brand-soft)}
.cc-item .cc-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc-item .cc-dial{color:var(--ink-3);font-variant-numeric:tabular-nums;font-size:13px}
.cc-item.cc-custom .cc-name{font-weight:650;color:var(--brand)}
.cc-empty{padding:14px 12px;color:var(--ink-3);font-size:13px;text-align:center}
@media (prefers-reduced-motion:reduce){.cc-btn{transition:none}}

/* ── comboBox: the grouped, searchable picker ──────────────────────────────
   Reuses every .cc-* rule above (same popup, same flip, same rows) and adds
   only what a full-width, grouped, label-carrying field needs. */

/* Field wrapper for a control that is NOT a native input. Same look as
   label.f, on a div: a <label> binds to its first labelable descendant, which
   for this picker is the search box inside its own popup. Values match
   label.f exactly, so the two shapes are indistinguishable on screen. */
.f{display:block;margin-bottom:12px}
.f>span{display:block;font-size:13px;font-weight:600;margin-bottom:7px}
/* Only the picker's caption is clickable. `.f>span` also matches every
   label.f>span in the app (label.f carries class `f`), and `cursor` is not
   declared there — so an unscoped pointer here would cascade onto every
   field caption in the dashboard, each promising a click that does nothing. */
.f>span.cbcap{cursor:pointer}
.f>span em{font-weight:400;color:var(--ink-3);font-style:normal}
.grid .f{display:flex;flex-direction:column;height:100%;margin-bottom:0}
/* The grid aligns fields on a row by pushing the CONTROL to the bottom. The
   existing rule names input/textarea/select; .cc is a fourth kind of control
   and without this the picker floats up while the text field beside it sits
   on the baseline. */
.grid .f>.cc{margin-top:auto}
.policy .f{margin-bottom:0}
.f + .row, .f + .btn{margin-top:16px}

/* Full width, unlike the dial-code popup's fixed 290px column. */
.cc-pop.wide{width:100%;min-width:260px}
/* The button's label is text, not a flag + code, so it needs to grow and
   ellipsise — "Freelancer / independent professional" is a real row. */
.cc-btn .cc-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start}
.cc-btn .cc-name.cc-ph{color:var(--ink-3)}
/* Group headings. Sticky because the list scrolls 194 rows and a heading that
   scrolls away leaves you reading trade names with no idea which trade group
   you are in. */
.cc-group{position:sticky;top:0;z-index:1;background:var(--surface);padding:8px 11px 5px;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);border-bottom:1px solid var(--line)}
/* The row that is already chosen, so re-opening the picker shows you where
   you are instead of making you re-read the list. */
.cc-item.sel .cc-name{font-weight:650;color:var(--brand)}
.cc-item.cc-clear .cc-name{color:var(--ink-3)}

/* Plan tiles (Plan screen). Bottom-aligned buttons across three tiles of
   different feature-list lengths — without this the "Choose" buttons sit at
   three different heights and the row reads as broken. */
.grid.c3 > .stat{display:flex;flex-direction:column}
.grid.c3 > .stat > ul{flex:1;list-style:none}
.grid.c3 > .stat > ul li{position:relative;padding-inline-start:14px}
.grid.c3 > .stat > ul li::before{content:"";position:absolute;inset-inline-start:0;top:.62em;width:4px;height:4px;border-radius:50%;background:var(--brand)}
.grid.c3 > .stat > button{margin-top:auto;width:100%}

/* ── i18n ─────────────────────────────────────────────────────────────────
   The language switcher, and the few things logical properties cannot flip. */
.langselect{
  height:34px;padding:0 8px;border:1px solid var(--line);border-radius:8px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13px;cursor:pointer;
}
.langselect:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
/* Code, keys and QR payloads read left-to-right in every language. */
[dir="rtl"] pre,[dir="rtl"] code,[dir="rtl"] .mono{direction:ltr;text-align:start;unicode-bidi:isolate}
[dir="rtl"] input[type="tel"],[dir="rtl"] input[inputmode="numeric"]{direction:ltr;text-align:end}

/* ─────────────────────────────────────────────────────────────────────────
   Chats, Leads and the Welcome flow.

   Everything below dresses markup the three inbox views already emit. It is
   token-only on purpose: dark mode is the token block at the top of this
   file, so nothing here needs a second set of colours.
   ───────────────────────────────────────────────────────────────────────── */

/* Filter chips (All · Leads · Today · With files, and the lead statuses). */
.chips{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 14px}
.chips .chip{appearance:none;border:1px solid var(--line);background:var(--surface);
  color:var(--ink-2);border-radius:999px;padding:6px 13px;font:inherit;font-size:13px;
  font-weight:600;cursor:pointer;transition:background .12s,border-color .12s,color .12s}
.chips .chip:hover{border-color:var(--brand);color:var(--ink)}
.chips .chip.on{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
.chips .chip:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* A conversation row: the visitor's last words, kept to one line. */
.crow{align-items:flex-start;gap:11px;flex-wrap:nowrap}
.cprev{margin:2px 0 0;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;
  display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical}
.contactrow{gap:8px;align-items:center;flex-wrap:wrap}

/* What the visitor sent, inside their own bubble. */
.atts{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
/* One attachment: the thumbnail or file chip, with the AI's reading under it. */
.att{display:flex;flex-direction:column;gap:2px;max-width:220px;min-width:0}
.thumb{width:104px;height:104px;object-fit:cover;border-radius:10px;display:block;
  border:1px solid var(--line);background:var(--surface-2)}
.filechip{display:inline-flex;align-items:center;gap:7px;max-width:100%;padding:7px 11px;
  border:1px solid var(--line);border-radius:10px;background:var(--surface);
  color:var(--ink);text-decoration:none;font-size:13px;overflow-wrap:anywhere}
.filechip:hover{border-color:var(--brand)}
/* What the model made of a photo it could not answer from. */
.anote{margin:6px 0 0;font-style:italic}
/* A turn the welcome flow produced, not the AI. */
.flowtag{opacity:.75}

/* Details the visitor gave the welcome flow. */
.vcard{margin-top:14px;padding:12px 14px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface-2)}
.vcard h3{margin:0 0 8px}
.vgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px 18px}
.vrow{display:flex;flex-direction:column;gap:1px;min-width:0}
.vk{font-size:12px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.03em}
.vv{font-size:14px;color:var(--ink);font-weight:600;overflow-wrap:anywhere}

/* The lead panel on an open chat. */
.leadbox{margin-top:12px}
.leadrow{gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:10px}
.leadsel{height:38px;padding:0 10px;min-width:150px}

/* A generated summary — per chat, and the day's briefing. */
.summary,.daily{margin-top:16px}
.sumsec{margin-top:14px}
.sumsec:first-of-type{margin-top:6px}
.sumsec > h3{margin:0 0 6px}
.sumissue{margin:0;color:var(--ink-2);max-width:78ch}
.sumlist{margin:0;padding-inline-start:20px;color:var(--ink-2)}
.sumlist li{margin:3px 0}
.fup{margin:0 0 8px;padding:10px 12px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface-2);color:var(--ink);max-width:78ch}
/* The day's numbers, as a row of small tiles. */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:10px;margin:4px 0 6px}

@media (max-width:640px){
  .thumb{width:84px;height:84px}
  .vgrid{grid-template-columns:1fr}
  .leadsel{width:100%}
}

/* ─────────────────────────────────────────────────────────────────────────
   The welcome-flow editor.

   Steps are blocks INSIDE the steps card, not a second list floating under
   it, and a step's own options are compact toggles: a question step carries
   two of them, so the bordered full-width `label.switch` used at the top of
   the page turned a three-step flow into six boxes that read as broken cards.
   ───────────────────────────────────────────────────────────────────────── */

/* "How it runs" holds four decisions about the flow. Two are a plain yes/no
   and get `label.switch`; two carry a control instead of a checkbox, and used
   to be a bare `label.f` — which has no top margin, so each sat FLUSH against
   the bordered box above it and read as a caption belonging to that box
   rather than as the next setting. Same box for all four instead: the row is
   the unit, and what sits inside it is the only thing that differs. */
/* display:block is load-bearing, not tidiness: one of these rows is a <label>
   so that clicking its caption focuses the field, and a label is inline by
   default — margin-top does not apply to an inline box, so that row climbed
   11px INTO the switch above it. */
.frow{display:block;margin-top:12px;padding:12px 14px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface)}
.frow > .t{font-weight:600;font-size:14.5px;line-height:1.35}
.frow > .tiny{margin-top:3px}
/* A bigger step above the control than the 3px under the title: the control
   is a different thing, not the next line of the same sentence. */
.frow > .row,.frow > input,.frow > textarea,.frow > select{margin-top:10px}

.fsteps{display:flex;flex-direction:column;gap:12px;margin-top:14px}
.fstep{border:1px solid var(--line);border-radius:var(--r);background:var(--surface-2);overflow:hidden}
.fstep-head{display:flex;align-items:center;gap:10px;padding:9px 12px;
  background:var(--surface);border-bottom:1px solid var(--line)}
.fstep-head .grow{flex:1;min-width:0}
.fstep-head .t{font-weight:600;font-size:14.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The step's position, and the only number on the row — a counter, not a status. */
.fnum{flex:0 0 auto;width:24px;height:24px;border-radius:50%;background:var(--brand-soft);
  color:var(--brand);font-size:12.5px;font-weight:700;display:flex;align-items:center;justify-content:center}
.fctl{display:flex;gap:4px;flex:0 0 auto}
.fctl .btn{width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;line-height:1}
.fctl .btn:disabled{opacity:.35;cursor:not-allowed}
.fbody{padding:12px}
/* The last field in a step has the card's own padding under it already. */
.fbody > label.f:last-child,.fbody > .fgrid:last-child{margin-bottom:0}

/* Where the answer lands, and what it is called: one decision, one row. */
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:0 14px;align-items:start}
.fgrid > .fkey{min-width:0}

/* Compact per-step options. `title` carries the explanation the full-width
   switch would have spelled out on three lines, six times a page. */
.ftoggles{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:2px}
.ftoggle{display:inline-flex;align-items:center;gap:7px;cursor:pointer;
  font-size:13.5px;font-weight:550;color:var(--ink-2)}
.ftoggle:hover{color:var(--ink)}
.ftoggle input[type=checkbox]{margin:0}

.fadd{margin-top:16px;padding-top:14px;border-top:1px dashed var(--line)}
.fadd > p{margin:0 0 8px}

@media (max-width:640px){
  .fgrid{grid-template-columns:1fr}
  .ftoggles{gap:8px 14px}
  /* Tap targets, and a head that never wraps its controls onto the title. */
  .fctl .btn{width:38px;height:38px}
  .fstep-head{gap:8px;padding:8px 10px}
  .fbody{padding:11px}
}
