/* THE MAIN PORTAL'S STYLESHEET. Moved out of index.html's inline <style>
 * block on 2026-09-07 (a code-audit quick win, M3) so the browser can
 * cache it separately from the page and does not re-download it on every
 * load. Every rule below is byte-identical to what index.html carried;
 * nothing about how the portal looks changed. See ops.css for the same
 * pattern already in use for the ops-only skin. */
  :root{
    --paper:#FFFFFF;
    --paper-deep:#F3F9F6;
    --ink:#101614;
    --ink-soft:#3B4844;
    --ink-faint:#6B7A75;
    --accent:#0A5F4B;
    --accent-lift:#07493A;
    --accent-wash:#DCF0E9;
    --border:#BDD4CB;
    --border-soft:#E2EDE8;
    --tint:#F1F9F5;
    --warn:#8A3B12;
    --warn-wash:#FBEDE4;
    /* THE RESTYLE, 2026-08-31. Chapter Bands, treatment B, colourway 6 with
       the deep wash - chosen by Joe across four sheets in strategy/. The two
       new colours are the old washes mixed deeper, and the reason is measured:
       #DCF0E9 is 1.19:1 against white and #EDF7F3 is 1.09:1, so a tab drawn in
       either is not there on a phone at arm's length in daylight. */
    --tab-fill:#C6E6D8; --tab-name:#07493A; --tab-rule:#0A5F4B;
    --flag-fill:#F4DFD2; --flag-name:#8A3B12;
    /* The dark bar's greens, copied from the website so they are the same. */
    --dark:#06281F; --dark-soft:#0A3A2D; --on-dark:#E8F3EE;
    --shadow-1: 0 1px 2px rgba(16,22,20,0.07);
    --shadow-2: 0 8px 24px rgba(16,22,20,0.09);
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 18px;
    --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
    --ease: 200ms ease;
    --font-serif: "Iowan Old Style", Palatino, Georgia, serif;
    --font-sans: "Inter var", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  }
  *,*::before,*::after{ box-sizing:border-box; }
  html,body{ height:100%; }
  body{
    margin:0; font-family:var(--font-sans); font-size:17px; line-height:1.6;
    color:var(--ink); background:#FFFFFF; -webkit-font-smoothing:antialiased;
  }
  /* Inter inside the product. The serif is for the wordmark and the marketing
     site only — a serif heading reads as *document*, and the whole point of The
     Workbench is that this is not one. Design decision, 2026-08-28. */
  h1,h2,h3{ font-family:var(--font-sans); font-weight:650; margin:0; letter-spacing:-.01em; }
  p{ margin:0; }
  ul{ margin:0; padding:0; list-style:none; }
  button{ font-family:inherit; font-size:inherit; color:inherit; }
  input,textarea{ font-family:inherit; font-size:16px; color:var(--ink); }
  [hidden]{ display:none !important; }

  /* Hard 3px corners, uppercase, and NO transition and NO transform on any
     of them. The pill shape went with the redesign, 2026-08-28. */
  .btn{
    display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
    min-height:40px; padding:0 var(--sp-4); border-radius:3px; border:1px solid transparent;
    cursor:pointer; font-size:11px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  }
  .btn-primary{ background:var(--accent); color:#fff; }
  .btn-primary:hover{ background:var(--accent-lift); }
  .btn-secondary{ background:transparent; color:var(--accent); border-color:var(--border); }
  .btn-ghost{ background:transparent; color:var(--ink-soft); text-transform:none;
    letter-spacing:0; font-weight:600; font-size:13px; }
  .btn-sm{ min-height:34px; padding:0 var(--sp-3); }
  .btn[disabled]{ opacity:.55; cursor:default; }
  button:focus-visible, a:focus-visible, input:focus-visible, textarea:focus-visible{
    outline:2px solid var(--accent); outline-offset:2px; border-radius:6px;
  }

  /* ---------- Sign in ---------- */
  /* TIDIED 2026-09-06. The card sits on the deep paper tint rather than bare
     white, so it reads as a card instead of a box drawn on nothing; the real
     wordmark from the logo package replaces the typed serif one; the word
     "Sign in" appears once as the heading and once on the button, not three
     times; and the lost-password line names the address to write to. */
  .signin{ min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center;
    padding:var(--sp-5); background:var(--paper-deep); }
  .signin-card{
    width:100%; max-width:400px; background:var(--paper); border:1px solid var(--border);
    border-top:2px solid var(--tab-rule);
    border-radius:3px; box-shadow:var(--shadow-2); padding:var(--sp-7) var(--sp-6); text-align:center;
  }
  .signin-logo{ display:block; width:172px; height:auto; margin:0 auto; }
  .wordmark{ font-family:var(--font-serif); font-size:20px; }
  .portal-tag{ margin-top:var(--sp-3); font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--accent); }
  .signin h1{ margin-top:var(--sp-6); font-size:24px; }
  /* A button dressed as a link, for the lost-password line: it is an action
     on this page, not a place to go, so it is a button underneath. */
  .linkish{ background:none; border:0; padding:0; margin:0; font:inherit; color:var(--accent-lift);
    text-decoration:underline; cursor:pointer; }
  .linkish[disabled]{ opacity:.55; cursor:default; }
  .signin-foot{ margin-top:var(--sp-5); font-size:13px; color:var(--ink-faint); }
  .signin-foot a{ color:var(--ink-soft); text-decoration:none; }
  .signin-foot a:hover{ text-decoration:underline; }
  .field{ margin-top:var(--sp-5); text-align:left; }
  .field label{ display:block; font-size:13px; font-weight:600; color:var(--ink-soft); margin-bottom:var(--sp-2); }
  .field input, .field select{
    width:100%; min-height:48px; padding:0 var(--sp-4); border:1px solid var(--border);
    border-radius:3px; background:var(--paper);
  }
  /* A textarea in a field was never styled, so the note composer rendered at
     the browser's default 20 columns beside its own Save button. Long-standing,
     not caused by the restyle - just impossible to miss once the box around it
     went away. */
  .field textarea{
    width:100%; min-height:78px; padding:10px var(--sp-3); border:1px solid var(--border);
    border-radius:3px; background:var(--paper); line-height:1.5; resize:vertical;
  }
  /* The "Add a customer / Add an invoice" button row above those tables. */
  .add-row{ margin:var(--sp-4) 0 0; }
  .signin .btn{ width:100%; margin-top:var(--sp-4); }
  .signin-help{ margin-top:var(--sp-4); font-size:13px; color:var(--ink-soft); }
  .notice{ margin-top:var(--sp-4); padding:var(--sp-3) var(--sp-4); border-radius:var(--radius-sm);
    background:var(--tint); border:1px solid var(--border-soft); font-size:14px; color:var(--ink-soft); text-align:left; }
  .notice.bad{ background:var(--warn-wash); border-color:#EBD3C3; color:var(--warn); }
  .notice a{ color:var(--accent-lift); word-break:break-all; }
  /* The sign-in card, its fields and its button all have 3px corners; the
     notice inside it should not be the one rounded thing (2026-09-06). */
  .signin .notice{ border-radius:3px; }

  /* ---------- App ---------- */

  /* The dark bar across the top: the only large area of colour in the
     product. It gives the page a hard top edge, and the white below it reads
     as the work surface. */
  .topnav{ background:var(--dark); display:flex; align-items:center; gap:18px;
    padding:0 16px; height:48px; position:sticky; top:0; z-index:6; }
  .topnav .wm{ font-size:15px; color:#fff; flex:none; white-space:nowrap; }
  /* The Connections screen already owns the bare `.links` class (its card of
     connection rows), so the bar's row of links has to zero that out. */
  .topnav .links{ flex:1; display:flex; align-items:center; gap:2px; min-width:0;
    background:none; border:0; border-radius:0; padding:0; margin:0; box-shadow:none; }
  .topnav .links button{ display:flex; align-items:center; gap:6px; padding:6px 10px;
    border:0; border-radius:4px; background:none; color:var(--on-dark); font-size:12.5px;
    white-space:nowrap; opacity:.82; cursor:pointer; }
  .topnav .links button .g{ font-size:12px; opacity:.85; }
  .topnav .links button.on{ background:var(--dark-soft); color:#fff; font-weight:650; opacity:1;
    box-shadow:inset 0 -2px 0 var(--accent-wash); }
  .topnav .bdg{ background:var(--warn); color:#fff; border-radius:3px; font-size:10px;
    font-weight:800; padding:0 4px; line-height:15px; }
  .topnav .tools{ flex:none; display:flex; gap:4px; }
  .topnav .tool{ background:none; border:0; color:var(--on-dark); opacity:.8;
    padding:6px 8px; font-size:14px; cursor:pointer; }
  /* NOT A DESTINATION, AN ACTION. Texts stopped being a nav entry on
     2026-08-28: you want it at the moment a message arrives, from wherever
     you are, so it is on every screen instead of one. */
  .topnav .capture{ display:flex; align-items:center; gap:6px; cursor:pointer;
    border:1px solid rgba(232,243,238,.35); background:none; border-radius:3px;
    padding:4px 9px; font-size:11px; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:#fff; white-space:nowrap; }
  .topnav .capture:hover{ background:var(--dark-soft); }
  /* On a phone it is a full width bar under the header, because the phone is
     where the screenshot of the text actually is. */
  .capture-bar{ display:none; }
  @media (max-width:900px){
    .capture-bar{ display:flex; width:100%; align-items:center; justify-content:center;
      gap:8px; cursor:pointer; background:var(--accent-wash); color:var(--accent-lift);
      border:0; border-bottom:1px solid var(--border-soft); padding:9px 12px;
      font-size:11.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; }
    .topnav .capture span:last-child{ display:none; }
  }
  @media (max-width:900px){ .topnav .links{ display:none; } }

  /* ELEVEN DESTINATIONS IS MORE THAN THIS BAR WAS BUILT FOR.
     Bookkeeping made it eleven. Between the phone breakpoint and about 1330px
     the last tab ran underneath the Add from a text button: 48px of Agents was
     covered at 1280, and 240px at 940. Nothing looked broken, the tab just
     quietly stopped being clickable where it looked like it should be.

     Two parts, because one was not enough. Measured, not guessed.

     1. The row tightens between 901 and 1360, which alone fixed 1280 and up. */
  @media (min-width:901px) and (max-width:1360px){
    .topnav{ gap:10px; padding:0 12px; }
    .topnav .links button{ padding:6px 6px; gap:4px; }
    .topnav .links{ gap:0; }
  }
  /* 2. Below roughly 1200 no amount of tightening fits eleven tabs, so the row
        scrolls inside its own box instead of spilling over its neighbour. The
        overlap becomes structurally impossible at any width rather than fixed
        at the handful we happened to test. Every destination stays reachable,
        which is the part that actually matters, and the thin bar is what tells
        you there is more to the right.

        If all eleven should be visible at once on a small laptop, that is a
        design decision (a More menu, shorter labels, or moving Add from a text)
        and it belongs to whoever owns the shell, not to this bug fix. */
  .topnav .links{ overflow-x:auto; overflow-y:hidden; scrollbar-width:thin;
    scrollbar-color:rgba(232,243,238,.35) transparent; }
  .topnav .links::-webkit-scrollbar{ height:3px; }
  .topnav .links::-webkit-scrollbar-thumb{ background:rgba(232,243,238,.35); border-radius:3px; }
  .topnav .links::-webkit-scrollbar-track{ background:transparent; }

  /* One white strip under the nav, on every screen that has a date range at
     all. Its absence is information too: no strip means no time window on
     this screen. */
  .filters{ background:var(--paper); border-bottom:1px solid var(--border);
    display:flex; align-items:center; justify-content:space-between;
    padding:9px 18px; gap:12px; position:relative; }
  .filters .range{ display:flex; align-items:center; gap:8px; border:1px solid var(--border);
    border-radius:3px; padding:5px 11px; font-size:12.5px; color:var(--ink);
    background:var(--paper); cursor:pointer; }
  .filters .cal{ opacity:.55; }
  .filters .car{ color:var(--ink-faint); font-size:10px; }
  .filters .upd{ display:flex; align-items:center; gap:7px; font-size:11.5px; color:var(--ink-faint); }
  .filters .btn-r{ border:1px solid var(--border); border-radius:3px; padding:4px 8px;
    color:var(--accent); background:none; cursor:pointer; }
  .btn-r.busy{ opacity:.5; animation: refresh-spin 1s linear infinite; }
  @keyframes refresh-spin{ to{ transform: rotate(360deg); } }
  .range-menu{ position:absolute; top:calc(100% + 2px); left:18px; z-index:15;
    background:var(--paper); border:1px solid var(--border); border-radius:3px;
    box-shadow:0 6px 18px rgba(16,22,20,.12); min-width:170px; display:flex; flex-direction:column; }
  .range-menu button{ background:none; border:0; border-bottom:1px solid var(--border-soft);
    text-align:left; padding:8px 12px; font-size:12.5px; color:var(--ink); cursor:pointer; }
  .range-menu button:last-child{ border-bottom:0; }
  .range-menu button.on{ background:var(--accent-wash); font-weight:650; }
  @media (max-width:900px){ .filters{ padding:8px 12px; } .filters .upd span{ display:none; } .range-menu{ left:12px; } }

  /* The More sheet on a phone. NO ANIMATION: it is there or it is not. A
     sliding panel is exactly the kind of motion the design forbids. */
  .more-sheet{ position:fixed; inset:0; z-index:20; background:rgba(16,22,20,.4);
    display:flex; align-items:flex-end; }
  .more-panel{ width:100%; background:var(--paper); border-radius:10px 10px 0 0;
    padding:16px 16px calc(16px + env(safe-area-inset-bottom, 0px)); }
  .more-head{ font-size:9.5px; font-weight:800; letter-spacing:.14em;
    text-transform:uppercase; color:var(--ink-faint); margin-bottom:10px; }
  .more-links{ display:flex; flex-direction:column; }
  .more-links button{ display:flex; align-items:center; gap:10px; background:none;
    border:0; border-bottom:1px solid var(--border-soft); padding:13px 2px;
    font-size:15px; color:var(--ink); text-align:left; cursor:pointer; }
  .more-links button:last-child{ border-bottom:0; }

  /* The crew's view is untouched by the redesign: no dark bar, no filter
     strip. They keep the plain white header the owner no longer has. */
  .field-mode .topnav, .field-mode .filters{ display:none; }
  body:not(.field-mode) .topbar{ display:none; }

  .topbar{
    position:sticky; top:0; z-index:5; background:var(--paper); border-bottom:1px solid var(--border-soft);
    padding:var(--sp-3) var(--sp-4); display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
  }
  .topbar .who{ min-width:0; }
  .topbar .biz{ font-size:17px; font-weight:650; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .topbar .sub{ font-size:12px; color:var(--ink-soft); }
  .topbar .btn{ flex:none; white-space:nowrap; }

  .sample-banner{
    background:var(--warn-wash); color:var(--warn); border-bottom:1px solid #EBD3C3;
    padding:var(--sp-2) var(--sp-4); font-size:13px; text-align:center; font-weight:600;
  }

  .group{ margin-top:var(--sp-5); }
  /* The stage picker on the Jobs screen. Six stages can outgrow a phone, so
     the row scrolls sideways inside itself rather than widening the page. */
  .stage-row{ margin:var(--sp-3) 0 var(--sp-4); overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .stage-row .seg{ width:max-content; }

  /* THE ROW — the component the whole product is made of. A 3px status bar
     down the left edge says whether it needs you (flag), has money on it
     (green), or neither (grey). Hard corners, no shadow; rows are separated
     by a 5px gap, not by depth. The transition below is one of the two
     movements the design allows: a row leaving the To Do List. */
  .row{
    background:var(--paper); border:1px solid var(--border-soft); border-left:3px solid var(--border);
    border-radius:3px; margin-bottom:5px; overflow:hidden;
    transition: opacity var(--ease), transform var(--ease);
  }
  .row.going{ opacity:0; transform:translateX(28px); }
  .row.hot{ border-left-color:var(--warn); }
  .row.warm{ border-left-color:var(--accent); }
  .row-face{ display:flex; align-items:center; gap:10px; padding:9px 11px; width:100%;
    background:none; border:0; cursor:pointer; text-align:left; }
  .row .t{ flex:1; min-width:0; }
  .row .n{ font-size:14px; font-weight:650; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .row .m{ font-size:10.5px; font-weight:600; letter-spacing:.05em; text-transform:uppercase;
    color:var(--ink-faint); margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .row .amt{ flex:none; font-size:13.5px; font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; }
  /* A budget said as a ceiling, quieter than a price they agreed to. */
  .row .amt.ceiling{ font-weight:600; color:var(--ink-soft); }
  .row .go{ flex:none; background:var(--accent); color:#fff; border-radius:3px; font-size:10.5px;
    font-weight:700; letter-spacing:.05em; padding:6px 11px; text-transform:uppercase; }
  .row .go.ghost{ background:transparent; color:var(--accent); border:1px solid var(--border); }
  .row .chev{ flex:none; color:var(--border); font-size:15px; padding-left:2px; }
  /* On a phone the button matters more than the figure, and both will not fit. */
  @media (max-width:900px){ .row .amt{ display:none; } .row .amt.keep{ display:block; } }

  /* The Dashboard's preview of a row: same face, a chevron where the button
     would be, and it can do nothing but open the To Do List. */
  .row.preview{ display:flex; align-items:center; gap:10px; padding:9px 11px; cursor:pointer; }

  /* Spending as labelled bars, sized against the biggest line. */
  /* ---- Bookkeeping: the three bands ported from the demo, 2026-09-01 ----
     The demo page (deliverables/demos/bookkeeping-demo.html) is the drawing;
     these are the same three blocks built for real. Class names and values are
     the demo's, so the two cannot drift apart by eye. */

  /* The in-vs-out bars. Two bars per bucket, in then out. */
  .bk-bars{ display:flex; align-items:flex-end; gap:4px; height:118px;
    border-bottom:1px solid var(--border-soft); }
  .bk-bars .b{ flex:1; min-width:0; display:flex; align-items:flex-end; gap:2px; height:100%; }
  .bk-bars .b i{ flex:1; display:block; background:var(--accent);
    border-radius:1px 1px 0 0; min-height:2px; }
  .bk-bars .b i.out{ background:var(--border); }
  .bk-axis{ display:flex; gap:4px; margin-top:5px; }
  .bk-axis span{ flex:1; min-width:0; text-align:center; font-size:9px; font-weight:700;
    letter-spacing:.04em; text-transform:uppercase; color:var(--ink-faint); white-space:nowrap; }
  .bk-legend{ display:flex; align-items:center; gap:18px; margin-top:12px; padding-top:11px;
    border-top:1px solid var(--border-soft); flex-wrap:wrap; }
  .bk-legend .k{ display:flex; align-items:center; gap:6px; font-size:10.5px; font-weight:700;
    letter-spacing:.05em; text-transform:uppercase; color:var(--ink-faint); }
  .bk-legend .k i{ width:9px; height:9px; border-radius:2px; background:var(--accent); display:block; }
  .bk-legend .k i.out{ background:var(--border); }
  .bk-legend .k b{ color:var(--ink); font-variant-numeric:tabular-nums; font-size:12.5px; }

  /* Every way it gets here: two columns, out then in. */
  .ways{ display:flex; gap:30px; flex-wrap:wrap; }
  .ways .col{ flex:1 1 320px; min-width:0; }
  .ways .ct{ font-size:9.5px; font-weight:800; letter-spacing:.11em; text-transform:uppercase;
    color:var(--accent); padding-bottom:7px; border-bottom:1px solid var(--border-soft); }
  .way{ display:flex; gap:11px; padding:9px 0; border-bottom:1px solid var(--border-soft); }
  .way:last-child{ border-bottom:0; }
  .way .ic{ flex:none; width:26px; height:26px; border-radius:3px; background:var(--tint);
    color:var(--accent-lift); display:flex; align-items:center; justify-content:center; }
  .way .ic svg{ width:14px; height:14px; }
  .way .n{ display:block; font-size:13px; font-weight:650; color:var(--ink); }
  .way .l{ display:block; font-size:12px; color:var(--ink-faint); margin-top:1px; }

  /* Your card. */
  .connect{ background:var(--paper); border:1px solid var(--border); border-radius:3px;
    padding:13px 16px; display:flex; align-items:center; gap:13px; flex-wrap:wrap; }
  .connect.on{ border-color:var(--accent); border-left-width:3px; }
  .connect .spacer{ flex:1; }
  .conn-ic{ flex:none; width:32px; height:32px; border-radius:3px; background:var(--accent-wash);
    color:var(--accent-lift); display:flex; align-items:center; justify-content:center; }
  .conn-ic svg{ width:16px; height:16px; }
  .conn-ic.off{ background:var(--tint); }
  .conn-t{ flex:1 1 340px; min-width:0; }
  .conn-t b{ display:block; font-size:14.5px; font-weight:650; color:var(--ink); }
  .conn-s{ display:block; font-size:12px; color:var(--ink-faint); margin-top:1px; }

  .spend{ background:var(--paper); padding:2px 0 0; }
  .sline{ display:flex; align-items:center; gap:12px; padding:6px 0; }
  .sline .k{ flex:0 0 128px; font-size:10.5px; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:var(--ink-faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .sline .track{ flex:1; height:7px; background:var(--border-soft); border-radius:2px; overflow:hidden; }
  .sline .track i{ display:block; height:100%; background:var(--accent); }
  .sline .v{ flex:none; font-size:12.5px; font-weight:700; color:var(--ink);
    font-variant-numeric:tabular-nums; min-width:58px; text-align:right; }

  /* The screens not yet on the new row (Jobs, What's coming, the crew's own
     list) still build .row-main / .row-text. */
  .row-main{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4); }
  .row-text{ flex:1; min-width:0; text-align:left; background:none; border:0; padding:0; cursor:pointer; }
  .row-text .h{ font-weight:600; }
  .row-text .d{ font-size:14px; color:var(--ink-soft); }
  .row-open{ border-top:1px solid var(--border-soft); padding:var(--sp-4); background:var(--tint); }
  /* Says what the thing below it IS, before the owner reads a word of it —
     Fritz, 2026-08-27. Without it a ready-written letter can read as one the
     customer already sent. */
  .row-open .draft-label{ margin-bottom:var(--sp-3); }
  .row-open .draft-label .what{
    display:inline-block; font-family:var(--font-sans); font-size:11px;
    font-weight:700; letter-spacing:.06em; text-transform:uppercase;
    color:var(--accent); background:var(--accent-wash);
    border-radius:3px; padding:3px 9px;
  }
  .row-open .draft-label .how{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-2); }
  .row-open .subject{ font-weight:600; margin-bottom:var(--sp-2); }
  .row-open textarea{
    width:100%; min-height:190px; padding:var(--sp-3); border:1px solid var(--border);
    border-radius:3px; background:var(--paper); line-height:1.55; resize:vertical;
  }
  .row-open .actions{ display:flex; flex-wrap:wrap; gap:var(--sp-2); margin-top:var(--sp-3); }

  .empty{
    background:var(--paper); border:1px solid var(--border-soft); border-radius:3px;
    padding:var(--sp-7) var(--sp-5); text-align:center;
  }
  .empty h2{ font-size:22px; }
  .empty p{ margin-top:var(--sp-2); color:var(--ink-soft); }

  @media (max-width:560px){
    .wrap{ padding-left:var(--sp-3); padding-right:var(--sp-3); }
  }

  /* What is linked, and what each link can actually do. Deliberately visible
     rather than hidden in a settings screen: a mailbox that can send but not
     read is the normal free state on Google, and the owner has to understand
     what the portal can and cannot see on their behalf. */
  /* Connection rows sit in a chapter now: the tab and the rule are the only
     edges, and the rows are held apart by their own hairlines. */
  .links{
    background:var(--paper); padding:0; margin-bottom:var(--sp-4);
  }
  .link-row{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-2) 0; flex-wrap:wrap; }
  .link-row + .link-row{ border-top:1px solid var(--border-soft); }
  .link-row .what{ flex:1; min-width:140px; }
  .link-row .what b{ display:block; }
  .link-row .what span{ font-size:13px; color:var(--ink-soft); }
  .dot{ width:9px; height:9px; border-radius:999px; flex:none; background:var(--border); }
  .dot.on{ background:var(--accent); }
  .dot.part{ background:#C08A2E; }
  .link-note{
    margin-top:var(--sp-2); font-size:13px; color:var(--ink-soft);
    background:var(--tint); border-radius:var(--radius-sm); padding:var(--sp-2) var(--sp-3);
  }
  .picker{ margin-top:var(--sp-3); border-top:1px solid var(--border-soft); padding-top:var(--sp-3); }
  .picker h3{ font-size:16px; }
  .choice{
    display:block; width:100%; text-align:left; margin-top:var(--sp-2); cursor:pointer;
    background:var(--paper); border:1px solid var(--border); border-radius:var(--radius-md);
    padding:var(--sp-3) var(--sp-4);
  }
  .choice:hover{ border-color:var(--accent); background:var(--tint); }
  .choice b{ display:block; }
  .choice span{ display:block; font-size:13px; color:var(--ink-soft); margin-top:2px; }
  .picker .field{ margin-top:var(--sp-3); }
  .picker .caps{ margin-top:var(--sp-3); font-size:14px; }
  .picker .caps label{ display:flex; gap:var(--sp-2); align-items:flex-start; margin-top:var(--sp-2); }
  .picker .caps input{ margin-top:5px; }
  .picker .caps span small{ display:block; color:var(--ink-soft); }

  .kind{
    flex:none; font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
    color:var(--accent-lift); background:var(--accent-wash); border-radius:999px; padding:1px 8px;
  }
  .kind.mail{ color:#5B3C8A; background:#EDE6F7; }
  /* A quote is neither money owed nor an email. Its own colour, so a glance
     down the list tells you which of the three you are looking at. */
  .kind.quote{ color:#1F5E63; background:#E2F1F1; }
  /* A repeating entry on the What's coming page. Its own colour again, so the
     three kinds on that list are told apart at a glance the same way. */
  .kind.plan{ color:#6B4A15; background:#F6EEDF; }
  .row-open .drafted{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-2); }

  .toast{
    position:fixed; left:50%; bottom:20px; transform:translateX(-50%);
    background:var(--ink); color:#fff; padding:var(--sp-3) var(--sp-5); border-radius:999px;
    box-shadow:var(--shadow-2); font-size:15px; z-index:20; max-width:92vw; text-align:center;
  }
  .toast-undo{
    margin-left:var(--sp-3); background:transparent; border:0; padding:0;
    color:#fff; font:inherit; font-weight:700; text-decoration:underline; cursor:pointer;
  }
  /* A REFUSAL LOOKS DIFFERENT FROM A CONFIRMATION. Added 2026-08-28 with the
     optimistic taps: a change that has been put back because the server said
     no must not arrive wearing the same clothes as "Saved." Same warm red the
     notices already use, so nothing new is introduced to the palette. */
  .toast.toast-bad{ background:var(--warn); }
  .foot{ margin-top:var(--sp-6); text-align:center; font-size:12px; color:var(--ink-soft); }

  /* "Bad Debt" sits next to "Not now" and must not look like the same weight
     of decision, but it is not destructive either — it is reversible all day.
     Warm, not red. */
  .btn-ghost.danger{ color:var(--warn); }

  /* What the owner already dealt with today. Folded shut, quiet, and never
     competing with the list of things that still need them. */
  .dealt{ margin-top:var(--sp-6); border-top:1px solid var(--border-soft); padding-top:var(--sp-3); }
  .dealt > summary{
    cursor:pointer; font-size:13px; color:var(--ink-soft); list-style:none;
    padding:var(--sp-2) 0; user-select:none;
  }
  .dealt > summary::-webkit-details-marker{ display:none; }
  .dealt > summary::before{ content:'\25B8'; display:inline-block; width:14px; }
  .dealt[open] > summary::before{ content:'\25BE'; }
  .dealt-row{
    display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-2) 0;
    border-top:1px solid var(--border-soft); font-size:14px;
  }
  .dealt-row > div:first-child{ flex:1; min-width:0; }
  .dealt-who{ font-weight:600; }
  .dealt-detail{ font-size:12px; color:var(--ink-soft); }
  .dealt-what{ font-size:12px; color:var(--ink-soft); white-space:nowrap; }
  .dealt-row.dead .dealt-what{ color:var(--warn); }
  .dealt-locked{ display:inline-block; min-width:52px; }

  /* ================= The shell: sidebar, screens, bottom bar =================
     The portal stopped being one scrolling page here. It has sections now, and
     the sidebar is the map of them. Below 900px the sidebar is replaced by a
     bottom bar, because on a phone a left rail either eats a third of the
     screen or hides behind a hamburger nobody presses. */

  .shell{ display:flex; min-height:100vh; align-items:flex-start; }

  .main{ flex:1; min-width:0; }
  .screen{ max-width:1100px; margin:0 auto; padding:var(--sp-4) var(--sp-4) var(--sp-7); }

  .page-head{ margin-bottom:var(--sp-4); }
  .ptitle{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3); }
  .ptitle h1{ font-size:20px; font-weight:650; color:var(--ink); letter-spacing:-.01em; }
  .psub{ margin-top:4px; font-size:12.5px; color:var(--ink-faint); max-width:74ch; }

  /* --- the tab: a chapter's name in a filled shape, sitting on a rule that
     runs off it. THE MARKUP IS UNCHANGED from the old band - band() in app.js
     has always emitted [.lbl][.n][.ln][action] in that order, so filling the
     first two and letting the line flex turns what was already there into a
     tab with the count inside the name. No render code moved for this. */
  .band{ display:flex; align-items:flex-end; gap:0; margin:20px 0 0; }
  .band .lbl{ font-size:9.5px; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
    background:var(--tab-fill); color:var(--tab-name); padding:5px 11px; border-radius:3px 3px 0 0; }
  /* The count is part of the name, so it shares the name's shape rather than
     starting a second one: no left padding, and only the right corner is cut. */
  .band .n{ font-size:9.5px; font-weight:800; letter-spacing:.06em;
    background:var(--tab-fill); color:var(--tab-name); align-self:stretch;
    display:flex; align-items:center; padding:0 11px 0 0; border-radius:0 3px 0 0; }
  .band .ln{ flex:1; height:2px; background:var(--tab-rule); }
  /* An action tabs itself on the far end, so a band that is a message rather
     than a heading - "Finished work is waiting to be billed - Bill it on the
     To Do List" - reads as ONE object with nothing hanging under it. */
  .band .more{ font-size:10px; font-weight:700; letter-spacing:.04em;
    background:var(--tab-fill); color:var(--tab-name); align-self:stretch;
    display:flex; align-items:center; padding:0 11px; border-radius:3px 3px 0 0;
    border:0; cursor:pointer; }
  /* A period picker on a chapter's tab is choosing WHAT YOU READ, not
     operating the business, so it belongs on the tab and looks like it. */
  .band .range-pick{ align-self:stretch; background:var(--tab-fill); color:var(--tab-name);
    border:0; border-radius:3px 3px 0 0; padding:0 9px; font-size:10px; font-weight:700;
    letter-spacing:.04em; cursor:pointer; }
  .band .range-pick:hover{ border-color:transparent; color:var(--tab-name); }
  .band.flagged{ --tab-fill:var(--flag-fill); --tab-name:var(--flag-name); --tab-rule:var(--flag-name); }

  /* WHAT A CHAPTER'S CONTENT SITS IN. No border, ever: the tab and the rule
     are its only edges. This is one half of the rule the restyle rests on -
     a tab marks something you READ, a hairline marks something you USE - and
     .card below is the other half. Nothing has both. */
  .under{ background:var(--paper); padding:11px 0 0; }
  .under.rows{ padding:6px 0 0; }
  .under.tight{ padding:8px 0 0; }
  /* A table in a chapter has no box, so its own header row carries the weight
     the box used to: the same hairline it always had, one step darker. */
  .under table.data th{ border-bottom:1px solid var(--border); }

  /* The Marketing screen's verdict word and its day-by-day spend bars. One
     word per ad set — WORKING, THIN, BURNING — and a chart with no library
     behind it, because thirty rectangles do not need one. */
  .row .n.wrapline, .row .m.wrapline{ white-space:normal; overflow:visible; text-overflow:clip; }
  .verdict{ font-size:10px; font-weight:800; letter-spacing:.08em; padding:3px 7px; border-radius:3px; white-space:nowrap; }
  .verdict.working{ background:var(--accent-wash); color:var(--accent-lift); }
  .verdict.thin{ background:#F0EFE4; color:#6B6430; }
  .verdict.burning{ background:var(--warn-wash); color:var(--warn); }
  /* Same as the trend chart and the spending bars: a chart is content inside
     a chapter, and a chapter is held by its tab, not by a box. */
  .mkt-chart{
    background:var(--paper); padding:12px 0 8px;
    display:flex; align-items:flex-end; gap:3px; height:110px;
  }
  .mkt-chart i{ flex:1; background:var(--accent-wash); border-radius:2px 2px 0 0; display:block; min-width:2px; }
  .mkt-chart i:hover{ background:var(--accent); }

  /* --- ganged tiles: numbers that belong together share one card --- */
  /* No outer box: the gang is content inside a chapter, and a chapter is held
     by its tab and its rule. The hairlines BETWEEN the figures stay - they are
     what stops four numbers reading as one sentence. */
  .gang{ display:flex; background:var(--paper); }
  .gang .t{ flex:1; padding:12px 14px; border-left:1px solid var(--border-soft); min-width:0; }
  .gang .t:first-child{ border-left:0; }
  .gang .k{ display:flex; align-items:center; gap:5px; font-size:9.5px; font-weight:700;
    letter-spacing:.09em; text-transform:uppercase; color:var(--ink-faint); white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis; }
  .gang .v{ margin-top:5px; font-size:23px; font-weight:700; color:var(--ink);
    font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
  .gang .t.flag .v{ color:var(--warn); }
  .gang .t.flag .k{ color:var(--warn); }
  /* Text facts (a customer's contact card) read at body size, not figure size. */
  .gang .t.small .v{ font-size:14px; font-weight:600; letter-spacing:0; font-variant-numeric:normal;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .gang .t.small .v a{ color:var(--accent-lift); text-decoration:none; }
  .gang .t.small .v a:hover{ text-decoration:underline; }
  .info{ flex:none; width:12px; height:12px; border-radius:50%; border:1px solid var(--border);
    color:var(--ink-faint); font-size:8px; font-weight:700; display:inline-flex;
    align-items:center; justify-content:center; font-style:italic; line-height:1;
    background:none; padding:0; cursor:pointer; }
  .info-note{ margin-top:8px; font-size:12px; line-height:1.45; color:var(--ink-soft);
    background:var(--tint); border:1px solid var(--border-soft); border-radius:3px; padding:8px 10px;
    white-space:normal; text-transform:none; letter-spacing:0; font-weight:400; }
  @media (max-width:900px){
    .gang{ flex-wrap:wrap; }
    .gang .t{ flex:1 1 50%; min-width:calc(50% - 1px); border-left:0; border-top:1px solid var(--border-soft); }
    .gang .t:nth-child(1), .gang .t:nth-child(2){ border-top:0; }
    .gang .t:nth-child(even){ border-left:1px solid var(--border-soft); }
    .gang .v{ font-size:20px; }
  }

  /* --- the segmented control --- */
  .seg{ display:flex; border:1px solid var(--border); border-radius:3px; overflow:hidden; }
  .seg button{ background:none; border:0; cursor:pointer; font-size:10.5px; font-weight:700;
    letter-spacing:.05em; text-transform:uppercase; padding:5px 11px; color:var(--ink-soft); }
  .seg button.on{ background:var(--accent); color:#fff; }
  /* One big plus beside the title, instead of "Add a quote" on its own line
     pushing the list down the page. Fritz, 2026-08-27. */
  .add-plus{
    flex:none; width:44px; height:44px; border-radius:999px;
    border:1px solid var(--accent); background:var(--accent); color:#fff;
    font-size:26px; line-height:1; font-weight:400;
    display:inline-flex; align-items:center; justify-content:center;
    cursor:pointer;
  }
  .add-plus:hover{ background:var(--accent-lift); }
  .add-plus.is-open{ background:var(--paper); color:var(--accent); }

  .tabbar{ display:none; }

  @media (max-width:900px){
    .screen{ padding:var(--sp-4) var(--sp-3) 96px; }
    .tabbar{
      display:flex; position:fixed; left:0; right:0; bottom:0; z-index:12;
      background:var(--paper); border-top:1px solid var(--border-soft);
      padding:6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
      box-shadow:0 -2px 12px rgba(16,22,20,.06);
    }
    .tab{
      flex:1; min-width:0; background:none; border:0; cursor:pointer; padding:6px 1px;
      display:flex; flex-direction:column; align-items:center; gap:2px;
      color:var(--ink-soft); font-size:10.5px; line-height:1.2; border-radius:var(--radius-sm);
      overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    }
    .tab span:last-child{ max-width:100%; overflow:hidden; text-overflow:ellipsis; }
    /* SEVEN TABS, NOT SIX, since What's coming was added on 2026-08-25. At a
       375px phone that left "Customers" reading "Custom…", and a menu that
       cannot say its own words is worse than a slightly smaller one. Half a
       point of type buys the whole label back on every phone in use. */
    @media (max-width:430px){
      .tab{ font-size:9.5px; letter-spacing:-.01em; }
    }
    .tab .glyph{ font-size:17px; line-height:1; }
    .tab.on{ color:var(--accent-lift); font-weight:700; }
    .tab .dotcount{
      position:absolute; transform:translate(14px,-4px);
      background:var(--warn); color:#fff; font-size:10px; font-weight:700;
      border-radius:999px; padding:0 5px; min-width:16px; text-align:center;
    }
  }

  /* ---------- Cards, tiles and tables, the furniture every screen uses ---------- */

  /* A PANEL: a thing you OPERATE - a form, a switch, a self-contained block
     with its own controls. It keeps a hairline and gets no tab, the opposite
     of .under above. Deliberately still called .card: 14 sites rely on it, and
     it means a chapter conversion that gets missed renders as a tidy box on
     white rather than as something broken. */
  .card{
    background:var(--paper); border:1px solid var(--border-soft); border-radius:4px;
    padding:var(--sp-4) var(--sp-4); margin-bottom:var(--sp-4);
  }
  .card > h2{ font-size:19px; }
  .card .card-sub{ font-size:13px; color:var(--ink-soft); margin-top:2px; }
  .under > .card-sub{ font-size:12px; color:var(--ink-faint); margin:-3px 0 9px; }
  .card-head{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-3); flex-wrap:wrap; margin-bottom:var(--sp-4); }

  .tiles{ display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)); gap:var(--sp-3); margin-bottom:var(--sp-4); }
  .tile{
    background:var(--paper); border:1px solid var(--border-soft); border-radius:3px;
    padding:var(--sp-4);
  }
  .tile .k{ font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--ink-soft); }
  .tile .v{ font-size:26px; font-weight:700; letter-spacing:-.02em; margin-top:var(--sp-2); font-variant-numeric:tabular-nums; line-height:1.1; }
  .tile .n{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-1); }
  .tile.hot .v{ color:var(--warn); }

  /* Bookkeeping. A spending line opens for editing; money in has no expenses
     row behind it, so it does not. */
  .line-open{ cursor:pointer; }
  .line-open:hover td{ background:var(--tint); }
  .book-say textarea{ width:100%; min-height:52px; }
  .tile.good .v{ color:var(--accent); }

  /* The three money lines that sit under the greeting. Labels are a fixed
     width and the figures are tabular, so the dollar signs stack in a column
     and the eye can compare the three heights without reading them. */
  .tiles-when{ margin:calc(-1 * var(--sp-2)) 0 var(--sp-4); font-size:12px; color:var(--ink-soft); }
  /* The period the tiles are read over: the dropdown, and the dates it works
     out to, side by side above the figures. Fritz, 2026-08-27. */
  .period{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; margin-bottom:var(--sp-3); }
  .period-dates{ font-size:13px; color:var(--ink-soft); }

  /* ---------- TEXTS SCREEN ----------
     Every class from here starts .shot- or .sf- and appears on that screen
     only. Fritz, 2026-08-27. */

  /* TWO WAYS IN, SIDE BY SIDE. The microphone used to float underneath the
     drop box, which read as an afterthought rather than an equal choice. */
  .shot-start{
    display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
    gap:var(--sp-4);
    border:1px solid var(--border-soft); border-radius:var(--radius-lg);
    background:var(--paper); box-shadow:var(--shadow-1);
    padding:var(--sp-5);
  }
  .shot-start.is-busy{ opacity:.6; pointer-events:none; }
  /* A hairline through the word, so the two halves read as one choice. */
  .shot-or{
    position:relative; align-self:stretch; min-height:150px;
    display:flex; align-items:center; justify-content:center;
    font-size:13px; color:var(--ink-soft);
  }
  .shot-or::before, .shot-or::after{
    content:''; position:absolute; left:50%; width:1px; background:var(--border-soft);
  }
  .shot-or::before{ top:0; height:calc(50% - 14px); }
  .shot-or::after{ bottom:0; height:calc(50% - 14px); }

  /* Sized for a thumb in a truck, not a mouse. */
  .shot-drop{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:var(--sp-3); cursor:pointer; min-height:150px;
    border:2px dashed var(--border); border-radius:var(--radius-lg);
    background:var(--tint); padding:var(--sp-5) var(--sp-4);
    text-align:center;
    -webkit-tap-highlight-color:transparent;
  }
  .shot-drop:hover{ border-color:var(--accent); background:var(--accent-wash); }
  .shot-drop input[type="file"]{
    position:absolute; width:1px; height:1px; opacity:0; pointer-events:none;
  }
  .shot-plus{
    width:60px; height:60px; flex:none;
    border-radius:999px; background:var(--accent); color:#fff;
    font-size:34px; line-height:1;
    display:flex; align-items:center; justify-content:center;
  }
  .shot-words{ font-size:16px; font-weight:600; color:var(--ink); }
  /* Busy is said in words ("Reading it…") and a dimmed panel — no pulse.
     Loading is a plain word, not a moving thing. Motion audit, 2026-08-28. */
  .shot-start.is-busy .shot-plus{ opacity:.55; }

  /* MIRRORS .shot-drop exactly. When one side had a box and the other had a
     floating button, the spoken option read as an afterthought. */
  .shot-say{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:var(--sp-3); min-height:150px;
    border:2px dashed var(--border); border-radius:var(--radius-lg);
    background:var(--tint); padding:var(--sp-5) var(--sp-4);
  }
  .shot-say:hover{ border-color:var(--accent); background:var(--accent-wash); }
  /* The button inside it is the circle, matching the plus opposite. */
  .shot-say .mic-btn{
    flex-direction:column; gap:var(--sp-3);
    border:none; background:none; padding:0; min-height:0;
  }
  .shot-say .mic-btn:hover{ border-color:transparent; }
  .shot-say .mic-icon{
    width:60px; height:60px; flex:none;
    border-radius:999px; background:var(--accent); color:#fff;
    font-size:26px; line-height:1;
    display:flex; align-items:center; justify-content:center;
  }
  .shot-say .mic-words{ font-size:16px; font-weight:600; color:var(--ink); }
  /* Listening: the circle goes amber and pulses, and the panel says so too. */
  .shot-say .mic-btn.is-listening .mic-icon{ background:var(--warn); }
  .shot-say .mic-btn.is-listening .mic-words{ color:var(--warn); }

  .shot-error{
    margin-top:var(--sp-4); padding:var(--sp-3) var(--sp-4);
    border-left:3px solid var(--warn); background:var(--warn-wash);
    color:var(--warn); font-size:14px; border-radius:0 8px 8px 0;
  }
  .shot-fineprint{
    margin-top:var(--sp-4); font-size:13px; color:var(--ink-soft);
    text-align:center; max-width:54ch; margin-inline:auto;
  }

  /* On a phone the two ways stack, and the rule turns on its side. */
  @media (max-width:700px){
    .shot-start{ grid-template-columns:1fr; padding:var(--sp-4); gap:var(--sp-3); }
    .shot-or{ align-self:auto; min-height:0; height:20px; }
    .shot-or::before, .shot-or::after{
      top:50%; height:1px; width:calc(50% - 22px); left:auto;
    }
    .shot-or::before{ left:0; }
    .shot-or::after{ right:0; }
    .shot-say, .shot-drop{ min-height:0; }
  }

  /* ---------- WHAT IT FOUND ----------
     The decision first, then the detail. This was five identical full-width
     boxes stacked down the page, so nothing said which of them mattered. */
  .shot-found{ max-width:760px; }
  .sf-head{ display:flex; flex-wrap:wrap; gap:var(--sp-2); margin-bottom:var(--sp-3); }
  .sf-tag{
    display:inline-flex; align-items:center;
    font-family:var(--font-sans); font-size:11px; font-weight:700;
    letter-spacing:.06em; text-transform:uppercase;
    border-radius:999px; padding:4px 10px;
    background:var(--paper-deep); color:var(--ink-soft);
  }
  .sf-tag.lead{ background:var(--accent-wash); color:var(--accent); }
  .sf-tag.hot{ background:var(--warn-wash); color:var(--warn); }
  .sf-tag.known{
    background:var(--tint); color:var(--ink);
    text-transform:none; letter-spacing:0; font-size:12px; font-weight:600;
  }
  .sf-summary{ font-size:21px; line-height:1.3; margin-bottom:var(--sp-4); }
  .sf-standin{
    font-size:12px; color:var(--ink-soft); margin-bottom:var(--sp-4);
    padding:var(--sp-2) var(--sp-3); background:var(--paper-deep); border-radius:8px;
  }

  /* Short answers three across on a laptop, stacked on a phone. The address
     gets the room: it is the longest and the most used. */
  .sf-row{ display:grid; grid-template-columns:2fr 1fr 1fr; gap:var(--sp-3); }
  @media (max-width:700px){ .sf-row{ grid-template-columns:1fr; } }

  /* A gap is marked ON the box it belongs to. A note under all of them
     arrives after the owner has already stopped looking. */
  .sf-gap{
    margin-left:var(--sp-2); font-family:var(--font-sans); font-size:11px;
    font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--warn);
  }
  .field input.is-gap{
    border-style:dashed; border-color:var(--warn); background:var(--warn-wash);
  }
  .sf-note{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-3); }
  .sf-transcript{
    font-size:13px; color:var(--ink-soft); white-space:pre-wrap;
    margin-top:var(--sp-2); padding:var(--sp-3);
    background:var(--tint); border-radius:8px;
  }
  .sf-actions{ margin-top:var(--sp-5); }

  /* THE MICROPHONE. Sized for a thumb, and it says out loud when it is
     listening — a microphone whose state you have to guess is one people stop
     trusting. Fritz, 2026-08-27. */
  .mic-row{ margin-top:var(--sp-3); text-align:center; }
  .mic-btn{
    display:inline-flex; align-items:center; gap:var(--sp-2);
    min-height:48px; padding:0 var(--sp-4);
    border:1px solid var(--border); border-radius:999px;
    background:var(--paper); color:var(--ink);
    font-family:var(--font-sans); font-size:15px; font-weight:600;
    cursor:pointer;
    max-width:100%;
  }
  .mic-btn:hover{ border-color:var(--accent); }
  .mic-icon{ font-size:19px; line-height:1; flex:none; display:flex; align-items:center; }
  /* The glyph is an SVG sized in em, so it follows the font-size above and
     the 26px one on the Texts screen. display:block kills the baseline gap. */
  .mic-icon svg{ display:block; }
  .mic-words{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* Listening is said in amber and in words — no pulse. Motion audit. */
  .mic-btn.is-listening{ border-color:var(--warn); background:var(--warn-wash); color:var(--warn); }

  .shot-transcript{ margin-top:var(--sp-4); }
  .shot-transcript summary{ cursor:pointer; font-size:13px; color:var(--ink-soft); }

  /* A tile you can press to see what it is made of. */
  .tile.tile-open{ cursor:pointer; }
  .tile.tile-open:hover{ border-color:var(--accent); }
  .tile.tile-open.is-open{ border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-wash); }

  /* The two figures the bars add up to, for the period on show. */
  .chart-sum{
    display:flex; flex-wrap:wrap; gap:var(--sp-5);
    margin:calc(-1 * var(--sp-2)) 0 var(--sp-4);
    padding-bottom:var(--sp-3); border-bottom:1px solid var(--border-soft);
  }
  .chart-sum .cs-k{ font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }
  .chart-sum .cs-v{ font-size:19px; font-weight:700; font-variant-numeric:tabular-nums; margin-top:2px; }
  .chart-sum .cs-v.in{ color:var(--accent); }
  .chart-sum .cs-v.good{ color:var(--accent); }
  .chart-sum .cs-v.bad{ color:var(--warn); }
  .chart-sum .cs-v.strong{ font-weight:700; }

  /* THE HOVER LABEL ON THE CHART. Parks above the taller bar in the column,
     never follows the mouse. Fritz, 2026-08-27. */
  .chart-wrap{ position:relative; }
  .chart-tip{
    position:absolute; z-index:3; pointer-events:none;
    background:var(--ink); color:#fff; border-radius:8px;
    padding:7px 10px; font-family:var(--font-sans); font-size:12px;
    line-height:1.35; white-space:nowrap;
    box-shadow:0 6px 18px rgba(0,0,0,.18);
  }
  .chart-tip .tip-when{ font-weight:700; margin-bottom:3px; }
  .chart-tip .tip-row{ display:flex; align-items:center; gap:6px; }
  .chart-tip .tip-sw{ width:8px; height:8px; border-radius:2px; flex:none; }
  .chart-tip .tip-sw.in{ background:var(--accent-wash); }
  .chart-tip .tip-sw.out{ background:var(--border); }
  .chart-tip .tip-k{ opacity:.75; }
  .chart-tip .tip-v{ margin-left:auto; font-weight:700; }

  /* What one clicked bar, or one pressed tile, was made of. */
  .day-detail{
    margin-top:var(--sp-4); padding:var(--sp-4);
    border:1px solid var(--border-soft); border-radius:12px; background:var(--tint);
  }
  .dd-head{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3); margin-bottom:var(--sp-3); }
  .dd-head h3{ font-size:16px; }
  .dd-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:var(--sp-4); }
  .dd-title{ font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); margin-bottom:var(--sp-2); }
  .dd-line{ display:flex; gap:var(--sp-3); padding:5px 0; border-bottom:1px solid var(--border-soft); font-size:14px; }
  .dd-line:last-child{ border-bottom:none; }
  .dd-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .dd-amt{ margin-left:auto; font-weight:600; white-space:nowrap; }
  .dd-total{ font-weight:700; border-top:1px solid var(--border); margin-top:4px; }

  /* The Choose row. Wraps to one field per line on a phone. */
  .custom-range{
    display:flex; align-items:flex-end; gap:var(--sp-3); flex-wrap:wrap;
    margin:calc(-1 * var(--sp-2)) 0 var(--sp-4);
    padding:var(--sp-3); border:1px solid var(--border-soft);
    border-radius:10px; background:var(--tint);
  }
  .custom-range .cr-field{ display:flex; flex-direction:column; gap:4px; }
  .custom-range .cr-label{ font-size:12px; color:var(--ink-soft); }
  .custom-range input[type="date"]{
    font-size:15px; padding:6px 8px; border:1px solid var(--border);
    border-radius:8px; background:var(--paper); color:var(--ink);
  }

  /* The period dropdown on a card head. Small and quiet on purpose — it
     changes what the card shows, it is not itself a thing to look at. */
  .range-pick{
    flex:none; font-size:13px; color:var(--ink-soft); background:var(--paper);
    border:1px solid var(--border); border-radius:var(--radius-sm);
    padding:4px 8px; cursor:pointer;
  }
  .range-pick:hover{ border-color:var(--accent); color:var(--ink); }

  /* The armed state of Approve and send: same button, louder words. */
  .btn.armed{ background:var(--warn); border-color:var(--warn); }

  /* Housekeeping that has to be findable but should not compete with the
     things an owner came here to do. Quieter frame, smaller type, no shadow. */
  .card.quiet{
    background:transparent; box-shadow:none; border-style:dashed;
    padding:var(--sp-4); margin-top:var(--sp-5);
  }
  /* Descendant selectors, not child: the heading of a card lives inside
     .card-head, so `.card.quiet > h2` matches nothing at all. */
  .card.quiet h2{ font-family:var(--font-sans); font-size:14px; font-weight:700;
                  letter-spacing:.04em; color:var(--ink-soft); }
  .card.quiet .card-head{ margin-bottom:var(--sp-2); }
  .card.quiet .card-sub{ font-size:12px; }
  .card.quiet .field{ margin-top:var(--sp-3); }
  .card.quiet .field label{ font-size:12px; font-weight:500; }
  .card.quiet .field input{ max-width:300px; }
  .card.quiet .muted-note{ font-size:12px; }
  .card.quiet .btn{ font-size:13px; }

  /* A table that survives a phone. Below 700px it is scrolled sideways inside
     its own box rather than being allowed to widen the page, which is the
     failure that makes every other column unreadable. */
  .table-wrap{ overflow-x:auto; -webkit-overflow-scrolling:touch; margin:0 calc(-1 * var(--sp-4)); padding:0 var(--sp-4); }
  /* Those negative margins exist to bleed a table to the edges of the PANEL
     that used to hold it. A chapter has no padding to bleed past, so inside
     one they just hang the table 16px left of everything above it. */
  .under > .table-wrap{ margin:0; padding:0; }
  table.data{ width:100%; border-collapse:collapse; font-size:13.5px; min-width:520px; }
  table.data th{
    text-align:left; font-size:9.5px; font-weight:800; letter-spacing:.11em; text-transform:uppercase;
    color:var(--ink-faint); padding:9px var(--sp-3); border-bottom:1px solid var(--border); white-space:nowrap;
  }
  table.data th.sortable{ cursor:pointer; user-select:none; }
  table.data th .sort-arrow{ margin-left:4px; letter-spacing:0; font-size:8px; }
  table.data td{ padding:11px var(--sp-3); border-bottom:1px solid var(--border-soft); vertical-align:middle; }
  table.data tbody tr:last-child td{ border-bottom:0; }
  table.data tbody tr.clickable{ cursor:pointer; }
  table.data tbody tr.clickable:hover{ background:var(--tint); }
  table.data .num{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
  /* A button in a table cell keeps its words on one line. Without this the
     quotes table wrapped "They said yes" onto three lines and the row grew to
     match, which made a tidy list look broken. */
  table.data td .btn{ white-space:nowrap; }
  table.data td.act{ text-align:right; white-space:nowrap; }
  table.data td.nowrap{ white-space:nowrap; }
  table.data .strong{ font-weight:600; }
  table.data .linky{ color:var(--accent-lift); font-weight:600; }
  table.data .muted{ color:var(--ink-soft); font-size:14px; }

  .tag{
    display:inline-block; font-size:10px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
    border-radius:3px; padding:2px 8px; white-space:nowrap;
    background:var(--accent-wash); color:var(--accent-lift);
  }
  .tag.late{ background:var(--warn-wash); color:var(--warn); }
  .tag.paid{ background:#E8F1EC; color:#3B6B55; }
  /* Open is neither good news nor bad — neutral grey, so colour keeps its
     one meaning per the design: green = good, flag = late. */
  .tag.open{ background:#ECECEA; color:#6B6B66; }
  /* Finished business, neither good news nor bad. Grey, so a turned down quote
     does not sit on the page looking like something that needs attention. */
  .tag.done{ background:#ECECEA; color:#6B6B66; }

  /* THE TEMPERATURE PILL. It is the control as well as the label: tapping it
     is how the owner overrides the portal's guess, so the colour lands
     exactly where the thing you press is. */
  .temp{
    display:inline-flex; align-items:center; gap:5px; flex:none;
    font-size:10px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
    border-radius:3px; padding:2px 8px; white-space:nowrap; cursor:pointer;
    background:#ECECEA; color:#6B6B66; border:0;
  }
  .temp .car{ font-size:8px; opacity:.7; }
  /* The owner set this one by hand. A hairline, not a colour: the colour is
     already saying the temperature. */
  .temp.byyou{ box-shadow:inset 0 0 0 1px #C9C9C5; }

  /* THE HEAT RAMP. Joe picked colour over words, 2026-08-28.
     IT LIVES ON THE PILL AND THE BAND HEADING AND NOWHERE ELSE. The row's
     left edge already means something everywhere in this product — green when
     there is money on it, --warn when it needs you today, grey when it is
     neither — and the "hot" orange is close enough to the "late" orange that
     nobody would notice they had been swapped. The edge is also the only mark
     that survives being glanced at from across a truck cab, which makes it
     the worst one to overload. Do not put heat on .row. */
  .temp.h-fire{ background:#FBE3DC; color:#B3300F; }
  .temp.h-hot { background:#FBEDE4; color:#A0501B; }
  .temp.h-warm{ background:#F6EEDF; color:#6B4A15; }
  .temp.h-cold{ background:#E4EDF3; color:#3D6580; }
  .temp.h-ice { background:#EDF3F7; color:#5B7E96; }
  .band.b-fire .lbl, .band.b-fire .n{ color:#B3300F; }
  .band.b-hot  .lbl, .band.b-hot  .n{ color:#A0501B; }
  .band.b-warm .lbl, .band.b-warm .n{ color:#6B4A15; }
  .band.b-cold .lbl, .band.b-cold .n{ color:#3D6580; }
  .band.b-ice  .lbl, .band.b-ice  .n{ color:#5B7E96; }

  /* ---------- Charts, drawn as plain SVG ----------
     No chart library, on purpose: the portal makes no network calls to anyone
     but itself, and pulling one in would be the first exception to that. */
  .chart{ width:100%; height:auto; display:block; overflow:visible; }
  /* THE CHART SCROLLS INSIDE ITS OWN BOX, IT NEVER WIDENS THE PAGE.
     Eighteen months needs about 600px to stay legible, and on a 375px phone
     the bare SVG pushed the document to 649px wide: every screen gained a
     sideways scrollbar and the bottom bar was cut in half. The negative margin
     lets the scrolling area run to the edges of the card, which is what makes
     it obvious there is more to see. */
  .chart-scroll{
    overflow-x:auto; -webkit-overflow-scrolling:touch;
    margin:0 calc(-1 * var(--sp-5)); padding:0 var(--sp-5);
  }
  .axis-line{ stroke:var(--border-soft); stroke-width:1; }
  .axis-text{ fill:#6C7B76; font-size:10px; font-family:var(--font-sans); }
  .chart-legend{ display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-4); margin-top:var(--sp-4); font-size:14px; }
  .chart-legend .item{ display:flex; align-items:center; gap:var(--sp-2); }
  .swatch{ width:11px; height:11px; border-radius:3px; flex:none; }
  .legend-amt{ color:var(--ink-soft); font-variant-numeric:tabular-nums; }

  .split{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4); }
  @media (max-width:860px){ .split{ grid-template-columns:1fr; } }

  /* A labelled horizontal bar, used for aging and for spend. Reads correctly
     at any width and needs no axis to be understood. */
  .meter{ margin-top:var(--sp-3); }
  .meter .meter-row + .meter-row{ margin-top:var(--sp-3); }
  .meter-top{ display:flex; justify-content:space-between; gap:var(--sp-3); font-size:14px; margin-bottom:5px; }
  .meter-top .amt{ font-variant-numeric:tabular-nums; font-weight:600; flex:none; }
  .meter-track{ height:9px; border-radius:999px; background:var(--border-soft); overflow:hidden; }
  .meter-fill{ height:100%; border-radius:999px; background:var(--accent); }
  .meter-fill.hot{ background:var(--warn); }

  /* ---------- The customer record ---------- */
  .backlink{
    background:none; border:0; cursor:pointer; color:var(--accent-lift); font-weight:600;
    padding:0; margin-bottom:var(--sp-3); font-size:15px;
  }
  .backlink:hover{ text-decoration:underline; }

  .search{
    width:100%; max-width:320px; min-height:44px; padding:0 var(--sp-4);
    border:1px solid var(--border); border-radius:999px; background:var(--paper);
    /* Sits evenly between the top of the card and the table's first heading
       row, rather than crowding the top edge. Fritz, 2026-08-27. */
    margin-bottom:var(--sp-4);
  }

  .muted-note{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-3); }

  /* On a phone a table is not a table: the same records render as row cards
     and the CSS swaps which of the two shows. */
  .phone-cards{ display:none; }
  .row-actions{ padding:0 11px 9px; }
  @media (max-width:900px){
    .table-wrap{ display:none; }
    .phone-cards{ display:block; }
  }

  /* A crew member gets NONE of the owner's furniture: no sidebar, no tabs,
     no menu at all. Their whole portal is the job list, because a tool for a
     tech in a truck earns its keep by having nothing to learn. The screen is
     also held to a phone's width even on a computer, so it is the same app
     everywhere. */
  .field-mode .screen{ max-width:560px; }

  /* ---------- The field app: what a crew member sees on their phone ----------
     And the report block the owner sees on a job. The crew's screens reuse the
     same furniture as everything else; these are only the pieces that do not
     exist anywhere else. */
  .field-who{
    display:inline-block; margin-left:8px; font-size:11px; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; border-radius:999px; padding:2px 9px; white-space:nowrap;
    background:var(--accent-wash); color:var(--accent-lift); vertical-align:middle;
  }
  .field-who.mine{ background:#E8F1EC; color:#3B6B55; }
  .field-notes{
    width:100%; min-height:130px; padding:var(--sp-3); border:1px solid var(--border);
    border-radius:var(--radius-lg); background:var(--paper); font:inherit; resize:vertical; margin-top:var(--sp-3);
  }
  .field-save-state{ font-size:13px; color:var(--ink-soft); margin-top:var(--sp-2); min-height:18px; }
  /* The one tap that ends anything earns the biggest target on the screen. */
  .field-finish{ width:100%; min-height:52px; font-size:17px; margin-top:var(--sp-4); }
  .field-photos{ display:flex; flex-wrap:wrap; gap:var(--sp-3); margin-top:var(--sp-3); }
  .field-photos .thumb{ width:96px; }
  .field-photos img{
    width:96px; height:96px; object-fit:cover; border-radius:10px;
    border:1px solid var(--border-soft); display:block; cursor:pointer;
  }
  .field-photos .thumb.pending img{ opacity:.45; }
  .field-photos .thumb .state{ font-size:11px; color:var(--ink-soft); margin-top:2px; }
  .report-block{ margin-top:var(--sp-4); padding:var(--sp-4); background:var(--tint); border-radius:var(--radius-lg); }
  .report-block h4{
    font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-soft);
  }
  .report-block p{ margin-top:var(--sp-2); white-space:pre-wrap; }
  .report-parts{ margin:var(--sp-2) 0 0 18px; }
  .crew-person{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3); flex-wrap:wrap; padding:var(--sp-3) 0; border-top:1px solid var(--border-soft); }
  .crew-person:first-of-type{ border-top:0; }
  /* Was unsized, so it inherited the body's 17px and read as a heading beside
     12px labels. Long-standing; only obvious once the card around it went. */
  .crew-person .cp-who .n{ font-size:14px; font-weight:650; }
  .crew-person .cp-who .e{ font-size:13px; color:var(--ink-soft); }
  .crew-person .cp-acts{ display:flex; gap:var(--sp-2); flex-wrap:wrap; }
  /* The permissions editor: one person's checkboxes, opened from their row. */
  .perm-editor{ padding:var(--sp-3); margin:0 0 var(--sp-3); border:1px solid var(--border-soft); border-radius:10px; background:var(--paper); }
  .perm-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:var(--sp-2); }
  .perm-choice{ display:flex; align-items:flex-start; gap:8px; font-size:14px; cursor:pointer; }
  .perm-choice input{ margin-top:3px; accent-color:var(--accent, #2f6f4f); }
  .perm-switch{ margin-top:var(--sp-3); padding-top:var(--sp-3); border-top:1px solid var(--border-soft); }
  .perm-editor .actions{ margin-top:var(--sp-3); }
  select.search{ min-height:44px; padding:0 var(--sp-3); border:1px solid var(--border); border-radius:10px; background:var(--paper); font-size:14px; }
  .invite-link{ display:flex; gap:var(--sp-2); margin-top:var(--sp-3); }
  .invite-link input{
    flex:1; min-height:44px; padding:0 var(--sp-3); border:1px solid var(--border);
    border-radius:10px; background:var(--paper); font-size:13px;
  }

  /* ---------- The Agents tab ----------
     A rack of cards, and one agent open as a chat. The only motion is the
     thinking dots, which exist so the screen is never dead while an answer
     is being written — Joe's decision 2026-08-31, alongside the no-motion
     rule everywhere else. */
  .agent-rack{ display:grid; grid-template-columns:repeat(auto-fill,minmax(250px,1fr)); gap:var(--sp-3); }
  .agent-card{
    display:flex; align-items:center; gap:var(--sp-3); text-align:left;
    background:var(--paper); border:1px solid var(--border-soft); border-radius:3px;
    padding:var(--sp-4); font:inherit; color:var(--ink); width:100%;
  }
  .agent-card .t{ flex:1; min-width:0; }
  .agent-card .n{ font-weight:650; }
  .agent-card .m{ font-size:13px; color:var(--ink-faint); margin-top:2px; }
  .agent-card.ready{ cursor:pointer; }
  .agent-card.ready:hover{ border-color:var(--border); box-shadow:var(--shadow-1); }
  .agent-card .chev{ flex:none; color:var(--border); font-size:15px; }
  .agent-card .tag{ flex:none; }
  .agent-mark{
    flex:none; display:flex; align-items:center; justify-content:center;
    background:var(--accent-wash); color:var(--accent); border-radius:50%;
  }
  .agent-mark svg{ width:58%; height:58%; }
  .agent-initials{ font-weight:650; font-size:15px; }
  .agent-head{ display:flex; align-items:center; gap:var(--sp-3); margin:var(--sp-3) 0 var(--sp-4); }
  .agent-head h1{ font-size:20px; font-weight:650; }
  .agent-head .psub{ margin-top:2px; }

  .chat{ display:flex; flex-direction:column; }
  .chat-log{
    display:flex; flex-direction:column; gap:var(--sp-3);
    padding:var(--sp-4); min-height:220px; max-height:60vh; overflow-y:auto;
  }
  .chat-hint{ color:var(--ink-faint); font-size:14px; }
  /* The sales desk's "Looking at" picker. It sits ABOVE the log rather than
     beside the input, because it is the state of the conversation and not
     part of one question: what it is set to changes what every question
     after it means. */
  .desk-pick{
    display:flex; align-items:center; gap:var(--sp-2);
    padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--border-soft);
    font-size:13.5px; color:var(--ink-faint);
  }
  .desk-pick select{
    border:1px solid var(--border); border-radius:3px; padding:6px 8px;
    background:var(--paper); color:var(--ink); font-size:13.5px; max-width:100%;
  }
  .turn{ display:flex; flex-direction:column; max-width:85%; }
  .turn.me{ align-self:flex-end; align-items:flex-end; }
  .turn.them{ align-self:flex-start; align-items:flex-start; }
  .bubble{ padding:10px 14px; border-radius:12px; white-space:pre-wrap; line-height:1.5; }
  .turn.me .bubble{ background:var(--accent); color:#fff; border-bottom-right-radius:3px; }
  .turn.them .bubble{ background:var(--paper-deep); border:1px solid var(--border-soft); border-bottom-left-radius:3px; }
  .cite{
    margin-top:var(--sp-1); font-size:12.5px; color:var(--ink-faint);
    border-left:2px solid var(--accent-wash); padding-left:var(--sp-2); max-width:60ch;
  }
  .cite.bad{ color:var(--warn); border-left-color:var(--warn-wash); }
  .chat-ask{ display:flex; gap:var(--sp-2); padding:var(--sp-3) var(--sp-4); border-top:1px solid var(--border-soft); }
  .chat-ask input{
    flex:1; min-width:0; border:1px solid var(--border); border-radius:3px;
    padding:10px 12px; background:var(--paper);
  }
  /* The notes card under the sales desk's chat (phase 6). */
  .desk-today{ margin-bottom:var(--sp-4); }
  .desk-today h2{ font-size:15px; font-weight:650; margin-bottom:var(--sp-3); }
  .desk-today-list{ display:flex; flex-direction:column; gap:6px; margin-bottom:var(--sp-3); }
  .desk-today-row{
    display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 12px; width:100%; text-align:left;
    background:var(--paper-deep); border:1px solid var(--border-soft); border-radius:10px;
    padding:10px 14px; cursor:pointer; font:inherit; color:inherit;
  }
  .desk-today-row:hover{ border-color:var(--accent); }
  .desk-today-row .n{ font-weight:650; }
  .desk-today-row .w{ color:var(--ink-faint); font-size:13.5px; }
  .desk-notes{ margin-top:var(--sp-4); }
  .desk-notes h2{ font-size:15px; font-weight:650; margin-bottom:var(--sp-3); }
  .desk-notes-list{ display:flex; flex-direction:column; gap:var(--sp-3); margin-bottom:var(--sp-4); }
  .desk-note{ border-left:2px solid var(--accent-wash); padding-left:var(--sp-3); }
  .desk-note .t{ font-weight:650; font-size:14px; }
  .desk-note .b{ white-space:pre-wrap; line-height:1.5; font-size:14px; margin-top:2px; }
  .desk-note .m{ font-size:12.5px; color:var(--ink-faint); margin-top:4px; }
  .desk-note-form textarea{
    width:100%; min-height:72px; border:1px solid var(--border); border-radius:3px;
    padding:10px 12px; background:var(--paper); font:inherit; resize:vertical;
  }
  .desk-note-form .row2{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
    margin-top:var(--sp-2); font-size:13.5px; color:var(--ink-faint);
  }
  .desk-note-form label{ display:flex; align-items:center; gap:6px; cursor:pointer; }
  .bubble.thinking{ display:flex; gap:5px; padding:14px; }
  .bubble.thinking .dot{
    width:6px; height:6px; border-radius:50%; background:var(--ink-faint);
    animation:agent-think 1.2s infinite;
  }
  .bubble.thinking .dot:nth-child(2){ animation-delay:.2s; }
  .bubble.thinking .dot:nth-child(3){ animation-delay:.4s; }
  .bubble.thinking .looking{ font-size:13px; color:var(--ink-faint); margin-left:6px; align-self:center; }
  @keyframes agent-think{ 0%,60%,100%{ opacity:.25; } 30%{ opacity:1; } }
