
  :root{
    /* The UA's own furniture — caret, scrollbars, autofill, the file picker — follows this.
       Without it Chrome paints form controls in its light theme inside a near-black page. */
    color-scheme:dark;
    --ink:#03080a; --cloud:#fcfcfe;
    --accent:#4FC4B2; --accent-deep:#46AD9D; --accent-ink:#2C6159;
    --dbg:hsl(224 7% 4%);
    /* The reply surface. Bubble copy sits on this, so contrast is measured against it. */
    --dpanel:hsl(240 8% 6.6%);
    --dtext:#EAF1F4;              /* reply copy            16.1:1 on --dpanel */
    --dsub:rgba(234,242,245,.82); /* secondary sentences    8.9:1 */
    --dlabel:rgba(234,242,245,.62);/* mono labels, stamps    5.6:1 */
    --dline:hsla(198 22% 78% / .14);
    --dline-soft:hsla(198 22% 78% / .085);
    --dsurf:hsla(196 34% 72% / .055);
    --dsurf-hi:hsla(196 34% 72% / .09);
    --glow:rgba(79,196,178,.13);
    --mono:'Geist Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
    --sans:'Plus Jakarta Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
    --r:8px; --r-card:14px;
    /* ---------- the conversation surface: a message app, not a thread of documents ----------
       iMessage's dark appearance, because that is the shape a person already knows how to read
       at a glance: your own words on the right with a tail corner in the brand's seafoam, the
       agent's on the left in near-black grey, consecutive messages from one side pressed
       together with the name and clock printed once for the group.
       The outgoing bubble IS the site accent (#4FC4B2) — the same seafoam as the mark, the
       controls and the focus ring — so "mine" reads in the owner's own colour instead of a
       borrowed system blue. Bright seafoam cannot carry white text: white on it measures
       2.1:1. The ink is therefore dark teal (7.8:1 on the seafoam), and swapping these two
       tokens is the whole of changing the outgoing colour. */
    --imsg-out:var(--accent); --imsg-out-ink:#06231F;
    --imsg-in:#26262B; --imsg-in-ink:#F2F2F7; --r-imsg:19px;
    /* The brand mark as a backdrop. This is NOT an SVG <text> any more: an SVG loaded as an
       image cannot fetch a webfont, so the browser drew the letter in its own fallback face
       while the dot stayed pinned at coordinates written for Plus Jakarta's metrics — the
       dot landed on the stem (measured: 15-27px of overlap in DejaVu). These are the real
       Plus Jakarta Sans 800 outlines, dot included, baked into path data by tools/mark.py. */
    --wmark:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20869%20769%27%3E%3Cg%20transform%3D%27translate%28-33.0%20757.0%29%20scale%281%20-1%29%27%3E%3Cpath%20d%3D%27M312.0%20-12Q232.0%20-12%20169.0%2026.0Q106.0%2064%2069.5%20129.0Q33.0%20194%2033.0%20273Q33.0%20353%2070.0%20417.5Q107.0%20482%20170.0%20520.0Q233.0%20558%20311.0%20558Q371.0%20558%20417.5%20535.5Q464.0%20513%20491.0%20472L468.0%20442V757H618.0V0H478.0V100L492.0%2069Q464.0%2029%20416.0%208.5Q368.0%20-12%20312.0%20-12ZM330.0%20123Q371.0%20123%20402.0%20142.0Q433.0%20161%20450.5%20195.0Q468.0%20229%20468.0%20273Q468.0%20317%20450.5%20351.0Q433.0%20385%20402.0%20404.0Q371.0%20423%20330.0%20423Q289.0%20423%20256.5%20403.5Q224.0%20384%20206.0%20350.5Q188.0%20317%20188.0%20273Q188.0%20229%20206.0%20195.0Q224.0%20161%20256.5%20142.0Q289.0%20123%20330.0%20123Z%27%20fill%3D%27%23fcfcfe%27%20fill-opacity%3D%27.085%27%2F%3E%3Cpath%20d%3D%27M752.0%200V150H902.0V0Z%27%20fill%3D%27%234FC4B2%27%20fill-opacity%3D%27.34%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  }
  *{box-sizing:border-box}
  html{height:100%}
  body{
    margin:0; height:100%; background:var(--ink); color:var(--dtext);
    font-family:var(--sans); font-weight:450; font-size:16px; line-height:1.6;
    -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
    -webkit-tap-highlight-color:transparent;
    overflow:hidden;
  }
  .mono{font-family:var(--mono);font-weight:400}
  :focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
  ::selection{background:rgba(79,196,178,.28)}
  .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  *{scrollbar-width:thin;scrollbar-color:hsla(198 22% 78% / .22) transparent}
  *::-webkit-scrollbar{width:10px;height:10px}
  *::-webkit-scrollbar-thumb{background:hsla(198 22% 78% / .18);border-radius:99px;border:3px solid transparent;background-clip:content-box}
  *::-webkit-scrollbar-thumb:hover{background:hsla(198 22% 78% / .3);background-clip:content-box}
  *::-webkit-scrollbar-track{background:transparent}

  /* ---------- backdrop: the brand, very quiet ---------- */
  .backdrop{
    position:fixed; inset:0; z-index:0; pointer-events:none;
    background-image:
      radial-gradient(115% 80% at 82% -16%, var(--glow), rgba(79,196,178,0) 64%),
      radial-gradient(90% 65% at 0% 106%, rgba(79,196,178,.055), rgba(79,196,178,0) 62%);
    background-repeat:no-repeat;
  }
  /* The mark itself is its own layer. Owner direction 2026-09-17: the lockup stands in the
     middle of the window — the letter in cloud white, the period in accent seafoam — instead
     of a seafoam silhouette cropped into the corner. Centred, nothing has to be faded out to
     keep it from competing with the conversation, so there is no mask any more. */
  .backdrop::after{
    content:""; position:absolute; inset:0;
    background:var(--wmark) no-repeat center center;
    background-size:min(40vw,420px) auto;
    opacity:.9;
  }
  @media (max-width:760px){
    .backdrop::after{
      background-size:min(62vw,270px) auto;
      opacity:.7;
    }
  }

  /* ---------- app frame ----------
     One identity block, two layouts: a top bar on a phone, a rail on a desktop.
     No duplicated ids, and the phone layout is the one a shop owner actually uses. */
  .app{
    position:relative; z-index:1; height:100dvh;
    display:grid; grid-template-columns:1fr;
    grid-template-rows:auto 1fr auto;
    grid-template-areas:"side" "thread" "composer";
  }

  /* ---------- the mark: lowercase d, accent dot ---------- */
  .mark{
    display:inline-flex; align-items:baseline; flex:0 0 auto;
    font-weight:800; font-size:26px; line-height:1; letter-spacing:-.05em; color:var(--cloud);
  }
  .mark i{font-style:normal;color:var(--accent);text-shadow:0 0 14px rgba(79,196,178,.55)}
  .mark.sm{font-size:22px}

  .side{
    grid-area:side; display:flex; align-items:center; gap:12px;
    padding:12px 16px; padding-top:max(12px,env(safe-area-inset-top));
    border-bottom:1px solid var(--dline);
    background:rgba(3,8,10,.74); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  }
  .brand{display:flex;align-items:center;gap:11px;flex:0 0 auto}
  .brandtext{display:none}
  .brandtext b em{font-style:normal;color:var(--accent)}

  .identity{min-width:0;flex:1 1 auto}
  .biz{
    display:block; font-weight:700; font-size:15px; letter-spacing:-.015em; color:var(--cloud);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  /* Narrow on purpose: a bare `.agent` rule here also matches every agent message
     turn (`class="turn agent"`) and rendered replies as tiny faint labels. */
  .trialpill{display:block;margin-top:4px;color:var(--accent);font:10px var(--mono);letter-spacing:.04em;white-space:nowrap}
  .agent-name{
    display:block; font-family:var(--mono); font-size:10.5px; letter-spacing:.12em;
    text-transform:uppercase; color:var(--dlabel); margin-top:4px;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }

  .status{
    flex:0 0 auto; display:flex; align-items:center; gap:7px;
    padding:6px 11px 6px 10px; border:1px solid var(--dline); border-radius:999px; background:var(--dsurf);
    font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--dlabel);
  }
  .identity .status{padding:2px 0;border:0;background:transparent;letter-spacing:.08em}
  .heartbeat{display:block;margin-top:2px;color:var(--dsub);font:10px var(--mono);white-space:nowrap}
  .dot{width:7px;height:7px;border-radius:50%;background:hsl(0 0% 42%);flex:0 0 auto}
  .dot.up{background:var(--accent)}
  .dot.down{background:hsl(38 60% 56%)}

  .sidefoot{display:none}
  .railhead{display:none}
  /* The Conversation group is one control and one panel. The control is a 44px icon in the phone's
     search row and a labelled row in the rail; the panel holds the New conversation action and the
     way back through the earlier windows. It is a popover, not a rail list, because the rail is a
     header on a phone: a list that grows with the number of conversations grows the top of the
     screen with it (measured 2026-09-30 at 390px: the header was 162px against a 116px budget). */
  .conversation-tools{display:flex;flex:1 1 100%;flex-direction:column;gap:8px;min-width:0;position:relative}
  .conversation-tools[hidden],.conversation-note[hidden],.conversation-panel[hidden]{display:none}
  .conversation-menu{display:flex;align-items:center;gap:9px;width:100%;min-height:44px;padding:9px 12px;border:1px solid transparent;border-radius:var(--r-card);background:transparent;color:var(--dsub);font:11px var(--mono);letter-spacing:.04em;cursor:pointer;transition:border-color .16s ease,color .16s ease,background .16s ease}
  .conversation-menu svg{width:17px;height:17px;flex:0 0 auto}
  .conversation-menu-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .conversation-menu:hover,.conversation-menu[aria-expanded="true"]{border-color:var(--dline);background:var(--dsurf);color:var(--dtext)}
  .conversation-panel{position:absolute;z-index:6;top:calc(100% + 8px);left:0;right:auto;width:min(380px,calc(100vw - 32px));max-height:min(60vh,calc(100vh - 200px));overflow:auto;padding:16px;border:1px solid var(--dline);border-radius:var(--r-card);background:rgba(10,18,21,.99);box-shadow:0 20px 60px rgba(0,0,0,.35)}
  .conversation-panel:focus{outline:none}
  .conversation-panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}
  .conversation-panel-heading{min-width:0}
  .conversation-panel-head h2{margin:0;color:var(--cloud);font-size:15px}
  .conversation-panel-count{margin:3px 0 0;color:var(--dlabel);font:10px var(--mono)}
  .conversation-panel-close{min-width:44px;min-height:44px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:18px/1 var(--mono);cursor:pointer}
  .conversation-panel-close:hover{border-color:var(--dline-strong);color:var(--dtext)}
  .conversation-panel .new-conversation{margin-bottom:10px}
  .new-conversation{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:9px 12px;border:1px solid var(--dline);border-radius:var(--r-card);background:transparent;color:var(--dsub);font:11px var(--mono);letter-spacing:.04em;cursor:pointer;transition:border-color .16s ease,color .16s ease,background .16s ease}
  .new-conversation svg{width:17px;height:17px;flex:0 0 auto}
  .new-conversation:hover{border-color:var(--dline-strong);background:var(--dsurf);color:var(--dtext)}
  .new-conversation[disabled]{opacity:.55;cursor:default}
  .conversation-note{margin:0 0 10px;padding:8px 10px;border:1px solid var(--dline);border-radius:8px;color:var(--dlabel);font:10px/1.45 var(--mono)}
  .conversation-list{display:grid;gap:2px;min-width:0}
  .conversation-row{display:block;padding:7px 8px;border:1px solid transparent;border-radius:8px;color:var(--dsub);text-decoration:none;min-width:0}
  .conversation-row:hover,.conversation-row.is-reading{border-color:var(--dline);background:var(--dsurf);color:var(--dtext)}
  .conversation-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.35}
  .conversation-meta{display:block;margin-top:3px;color:var(--dlabel);font:9px/1.35 var(--mono);letter-spacing:.02em}

  /* ---------- thread ---------- */
  main.thread{
    grid-area:thread; min-height:0; overflow-y:auto; overscroll-behavior:contain;
    padding:22px 16px 12px; scroll-padding-bottom:16px;
  }
  .wrap{max-width:768px;width:100%;margin:0 auto;display:flex;flex-direction:column;gap:22px}

  .turn{display:flex;flex-direction:column;max-width:84%;gap:5px}
  .turn.me{align-self:flex-end;align-items:flex-end}
  /* stretch, not flex-start: with flex-start the reply column takes its content width, so a
     single long unbreakable line (a SQL block) dragged the bubble 600px wide on a 390px
     phone instead of scrolling inside its own box. */
  .turn.agent{align-self:flex-start;align-items:stretch;width:100%;max-width:100%}

  /* ---------- grouping ----------
     Consecutive messages from ONE side read as one block: 4px instead of 22px, and the name
     and the clock printed once for the group rather than once per line. This is most of what
     makes a message app scannable, and it is why the wrap gap is 22px and reduced here — a
     day line between two turns breaks the grouping, which is exactly right. */
  .wrap > .turn.me + .turn.me,
  .wrap > .turn.agent + .turn.agent{margin-top:-18px}
  .wrap > .turn.me + .turn.me .meta,
  .wrap > .turn.agent + .turn.agent .meta{display:none}

  .meta{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:11px;color:var(--dlabel)}
  .turn.me .meta{flex-direction:row-reverse}
  .meta .who{
    font-family:var(--mono); font-size:10.5px; letter-spacing:.12em;
    text-transform:uppercase; color:var(--dlabel);
  }
  /* Your own messages carry no name: you know who you are, and the label is the thing that
     made both sides of this thread look alike at a glance. */
  .turn.me .who{display:none}
  .turn.agent .meta{padding-left:2px}
  .stamp{font-family:var(--mono);font-size:10.5px;color:var(--dlabel)}
  .stamp::before{content:"·";margin-right:9px;opacity:.55}
  .turn.me .stamp::before{content:"";margin:0}
  .copy{
    appearance:none; background:none; border:0; padding:0; cursor:pointer;
    font-family:var(--mono); font-size:10px; letter-spacing:.11em; text-transform:uppercase;
    color:var(--dlabel); opacity:0; transition:opacity .15s ease,color .15s ease;
  }
  .turn:hover .copy,.copy:focus-visible{opacity:1}
  @media (hover:none){.copy{opacity:.65}}
  .copy:hover{color:var(--accent)}

  .line{display:flex;gap:12px;align-items:flex-start;min-width:0}
  .turn.me .line{justify-content:flex-end}
  /* A one-to-one conversation has no avatars. The brand mark is in the rail, and a 26px square
     beside every reply is what made this read as a document viewer. */
  .avatar{display:none}
  .avatar i{font-style:normal;color:var(--accent)}

  /* ---------- the bubbles ---------- */
  .bubble{
    min-width:0; padding:9px 14px; border-radius:var(--r-imsg);
    overflow-wrap:anywhere; font-size:16px; line-height:1.68;
  }
  .turn.me .bubble{
    background:var(--imsg-out); color:var(--imsg-out-ink); font-weight:500;
    border-bottom-right-radius:6px; white-space:pre-wrap;
    /* Tight, because nothing long is ever typed here — and the prose leading the reply column
       is measured and gate-checked, so it is not the place to borrow iMessage's rhythm. */
    line-height:1.45;
  }
  /* The tail corner belongs to the LAST bubble of a group, and the first one gets its full
     radius at the top — that is the iMessage stacking, and it is what makes two messages from
     the same side look like one utterance. */
  .turn.me + .turn.me .bubble{border-bottom-right-radius:var(--r-imsg)}
  .turn.me:has(+ .turn.me) .bubble{border-top-right-radius:7px}
  /* The measure cap is the whole point: a 750-character paragraph in a 780px column is a
     wall. 33rem is ~73 characters of this type at 16px — measured, not guessed: the average
     prose character is 7.2px, so this is where the copy stops being tiring. */
  .turn.agent .bubble{
    background:var(--imsg-in); color:var(--imsg-in-ink);
    border-bottom-left-radius:6px;
    max-width:min(100%,33rem);
  }
  .turn.agent + .turn.agent .bubble{border-bottom-left-radius:var(--r-imsg)}
  .turn.agent:has(+ .turn.agent) .bubble{border-top-left-radius:7px}

  /* ---------- formatted replies ----------
     Agent output goes through a small safe renderer: text nodes only, never
     innerHTML. These rules are what make a long reply readable. */
  .bubble > *:first-child{margin-top:0}
  .bubble > *:last-child{margin-bottom:0}
  .bubble p{margin:0 0 13px; text-wrap:pretty}
  .bubble p:last-child{margin-bottom:0}
  .bubble .md-h1,.bubble .md-h2,.bubble .md-h3{
    margin:20px 0 9px; font-weight:750; letter-spacing:-.02em; line-height:1.3;
    color:var(--cloud); text-wrap:balance;
  }
  .bubble > *:first-child.md-h1,.bubble > *:first-child.md-h2,.bubble > *:first-child.md-h3{margin-top:0}
  .bubble .md-h1{font-size:18.5px}
  .bubble .md-h2{font-size:16.5px}
  .bubble .md-h2::after{content:"";display:block;width:26px;height:2px;border-radius:2px;margin-top:7px;background:rgba(79,196,178,.5)}
  .bubble .md-h3{font-size:15px;color:var(--dsub);text-transform:none}

  .bubble ul,.bubble ol{margin:0 0 13px; padding-left:0; list-style:none}
  .bubble ul ul,.bubble ul ol,.bubble ol ul,.bubble ol ol{margin:6px 0 0}
  .bubble li{position:relative;padding-left:20px;margin:0 0 7px}
  .bubble li:last-child{margin-bottom:0}
  .bubble li::before{content:"";position:absolute;left:4px;top:.62em;width:5px;height:5px;border-radius:50%;background:var(--accent);opacity:.85}
  .bubble ol{counter-reset:mdli}
  .bubble ol > li{counter-increment:mdli}
  .bubble ol > li::before{
    content:counter(mdli); top:.1em; left:0; width:auto; height:auto; border-radius:0;
    font-family:var(--mono); font-size:12px; color:var(--accent); opacity:1;
  }
  .bubble .md-check{position:absolute;left:0;top:.18em;width:13px;height:13px;border:1px solid var(--dline);border-radius:4px;
    display:flex;align-items:center;justify-content:center;font-size:10px;line-height:1;color:var(--ink);background:transparent}
  .bubble li.md-task::before{display:none}
  .bubble li.md-task.done .md-check{background:var(--accent);border-color:var(--accent)}

  .bubble blockquote{
    margin:0 0 13px; padding:9px 14px; border-radius:10px;
    border-left:2px solid rgba(79,196,178,.5); background:var(--dsurf);
    color:var(--dsub);
  }
  .bubble blockquote p{margin:0 0 8px}
  .bubble blockquote p:last-child{margin:0}
  .bubble hr{border:0;border-top:1px solid var(--dline);margin:18px 0}
  .bubble code{
    font-family:var(--mono); font-size:.86em; padding:2px 6px; border-radius:6px;
    background:hsla(196 34% 72% / .1); border:1px solid var(--dline-soft);
    overflow-wrap:anywhere; color:#F3F8F9;
  }
  .bubble pre{
    margin:0 0 13px; padding:12px 14px; max-width:100%; overflow-x:auto;
    border:1px solid var(--dline); border-radius:10px; background:rgba(3,8,10,.66);
  }
  .bubble pre code{padding:0;border:0;background:none;font-size:13px;line-height:1.65;white-space:pre;color:var(--dtext)}
  .bubble .md-code{margin:0 0 13px}
  .bubble .md-lang{
    font-family:var(--mono); font-size:9.5px; letter-spacing:.14em; text-transform:uppercase;
    color:var(--dlabel); margin:0 0 6px 2px;
  }
  .bubble .md-code pre{margin:0}
  /* Tables: the format a reply most often arrives in when the agent has done real work. */
  .bubble .md-table-wrap{
    margin:0 0 13px; max-width:100%; overflow-x:auto;
    border:1px solid var(--dline); border-radius:10px; background:rgba(3,8,10,.32);
  }
  .bubble table.md-table{border-collapse:collapse; width:100%; font-size:14.5px; line-height:1.5}
  .bubble table.md-table th{
    font-family:var(--mono); font-size:10px; font-weight:400; letter-spacing:.1em; text-transform:uppercase;
    color:var(--dlabel); text-align:left; padding:9px 13px; background:var(--dsurf);
    border-bottom:1px solid var(--dline); white-space:nowrap;
  }
  .bubble table.md-table td{
    padding:9px 13px; border-bottom:1px solid var(--dline-soft); vertical-align:top;
  }
  .bubble table.md-table tbody tr:last-child td{border-bottom:0}
  .bubble table.md-table tbody tr:nth-child(even) td{background:hsla(196 34% 72% / .022)}
  .bubble table.md-table .md-num{text-align:right;font-variant-numeric:tabular-nums}
  .bubble table.md-table .md-center{text-align:center}
  .bubble strong{font-weight:700;color:var(--cloud)}
  .bubble em{font-style:italic}
  .bubble a{color:var(--accent);text-underline-offset:3px;text-decoration-thickness:1px}
  .bubble a:hover{color:#66D3C2}
  .turn.me .bubble a{color:var(--imsg-out-ink);text-decoration:underline;text-underline-offset:2px}
  .turn.me .bubble code{background:rgba(3,8,10,.14);border-color:rgba(3,8,10,.18)}
  .bubble s{opacity:.7}

  /* ---------- working ---------- */
  .progress{display:flex;flex-direction:column;gap:7px;padding-left:2px}
  .progress p{
    margin:0; font-family:var(--mono); font-size:11px; letter-spacing:.01em; color:var(--dlabel);
    display:flex; align-items:baseline; gap:8px;
  }
  .progress p::before{content:"";width:4px;height:4px;border-radius:50%;background:var(--accent);flex:0 0 auto}
  /* The dots that mean "the agent is writing RIGHT NOW", plus the one short line that says
     what it is doing while the wait runs long.

     Muted and small on purpose: an accent-coloured dot pulsing in a just-finished reply is what
     made the owner read "still loading" off a message that had already landed. Nothing here glows.

     ONE ROW PER THREAD, never two. A run can be represented twice for a moment — the events
     channel can name it before this page's own /api/chat stream does, and each of those created
     its own dot group — so two sets of dots sat on screen through the longest part of the wait: the
     owner's report, "there's 2 sets of 3 dots". `onlyThinking()` keeps the newest and takes the
     rest off; the page's double-representation clean-up later finishes the job. */
  .pending{display:flex;gap:9px;align-items:baseline;padding:6px 0 0 2px;min-height:15px}
  .pending .dots{display:inline-flex;gap:5px;align-items:center;flex:0 0 auto}
  .pending i{width:4px;height:4px;border-radius:50%;background:var(--dsub);animation:pulse 1.6s ease-in-out infinite}
  .pending i:nth-child(2){animation-delay:.2s}
  .pending i:nth-child(3){animation-delay:.4s}
  /* The line is a thought, not a log: sentence case, no monospace machinery, and the wording
     every customer already uses for someone working on their problem. */
  .pending .says{font-size:12px;color:var(--dlabel);letter-spacing:.005em;transition:opacity .22s ease;flex:1 1 auto;min-width:0}
  .pending .says.swap{opacity:0}
  @keyframes pulse{0%,100%{opacity:.2}50%{opacity:1}}
  .bubble.writing::after{
    content:""; display:inline-block; width:2px; height:1em; margin-left:3px;
    vertical-align:-.12em; background:var(--accent); animation:caret-blink 1s step-end infinite;
  }
  @keyframes caret-blink{0%,45%{opacity:1}46%,100%{opacity:0}}

  /* ---------- decisions ----------
     The Discord buttons, for a browser. A card, not a command: the flagged command is shown
     verbatim (the agent redacts credentials before it ever reaches this page), what it means
     is said in plain words, and the choices are the AGENT's own list — the portal renders
     exactly what the agent will accept, so a button can never be one it would refuse.
     It sits inside the turn that caused it, because that is what a decision is part of. */
  .decide{
    margin:11px 0 0 38px; padding:14px 15px 13px; max-width:560px;
    border:1px solid var(--dline); border-left:2px solid var(--accent);
    border-radius:12px; background:linear-gradient(180deg,var(--dsurf-hi),var(--dsurf));
  }
  .decide-kicker{
    margin:0 0 9px; font-family:var(--mono); font-size:10px; letter-spacing:.14em;
    text-transform:uppercase; color:var(--accent); display:flex; align-items:center; gap:8px;
  }
  .decide-kicker i{width:6px;height:6px;border-radius:50%;background:var(--accent);animation:pulse 1.25s ease-in-out infinite;flex:0 0 auto}
  .decide-why{margin:0 0 10px; font-size:14.5px; line-height:1.55; color:var(--dsub)}
  .decide-why b{color:var(--dtext);font-weight:600}
  .decide pre{
    margin:0 0 12px; padding:11px 13px; border:1px solid var(--dline); border-radius:10px;
    background:rgba(3,8,10,.6); overflow-x:auto;
  }
  .decide pre code{
    font-family:var(--mono); font-size:12.5px; line-height:1.6; color:var(--dtext);
    white-space:pre-wrap; overflow-wrap:anywhere;
  }
  .decide-actions{display:flex;flex-wrap:wrap;gap:8px}
  .decide-btn{
    font-family:var(--sans); font-size:14px; font-weight:600; letter-spacing:.01em;
    padding:10px 14px; min-height:42px; border-radius:9px; cursor:pointer;
    border:1px solid var(--dline); background:var(--dsurf-hi); color:var(--dtext);
    transition:background .14s ease,border-color .14s ease,transform .1s ease,opacity .14s ease;
  }
  .decide-btn:hover{background:hsla(196 34% 72% / .16)}
  .decide-btn:active{transform:translateY(1px)}
  .decide-btn.allow{border-color:rgba(79,196,178,.5);color:#BDF0E6}
  .decide-btn.allow:hover{background:rgba(79,196,178,.18)}
  .decide-btn.deny{border-color:rgba(226,120,110,.42);color:#F4CFC9}
  .decide-btn.deny:hover{background:rgba(226,120,110,.16)}
  .decide-btn[disabled]{opacity:.42;cursor:default;transform:none}
  .decide-btn[data-picked="1"]{border-color:var(--accent);background:rgba(79,196,178,.2);opacity:1}
  .decide-foot{margin:10px 0 0; font-family:var(--mono); font-size:10.5px; letter-spacing:.03em; line-height:1.5; color:var(--dlabel)}
  /* The live strip. An answered card is the LAST thing in the thread, so the work it unblocked
     is drawn below it — a card that goes still the moment it is answered reads as a frozen page
     (owner, 2026-10-01: "it almost appears to be frozen, since it doesnt update beyond that").
     So the card has three states, not two: WAITING (asking), DONE (answered, run in flight —
     the dot keeps its pulse and the bar sweeps), and SETTLED (run over — no motion anywhere and
     a tick where the dot was). Motion means work; nothing moves under a finished run, which is
     the rule the writing dots already follow. */
  .decide-bar{display:none;position:relative;height:2px;margin:12px 0 0;border-radius:2px;overflow:hidden;background:var(--dline)}
  .decide-bar::after{
    content:""; position:absolute; top:0; bottom:0; left:0; width:34%; border-radius:2px;
    background:linear-gradient(90deg,transparent,var(--accent),transparent);
    animation:decide-sweep 1.7s cubic-bezier(.45,.05,.55,.95) infinite;
  }
  @keyframes decide-sweep{0%{transform:translateX(-110%)}100%{transform:translateX(320%)}}
  .decide[data-state="done"] .decide-bar{display:block}
  /* The poll ran out of tries, not the agent out of work: the motion stops and the card says so,
     because an indicator that never ends is the complaint this answers. */
  .decide[data-quiet="1"] .decide-bar{display:none}
  .decide[data-quiet="1"] .decide-kicker i{animation:none;opacity:.45}
  /* Answered or lapsed: the card keeps its place in the timeline and stops asking. */
  .decide[data-state="done"],.decide[data-state="lapsed"],.decide[data-state="settled"]{background:var(--dsurf)}
  .decide[data-state="done"] .decide-kicker,.decide[data-state="lapsed"] .decide-kicker,.decide[data-state="settled"] .decide-kicker{color:var(--dlabel)}
  /* Answered is NOT finished, so the dot keeps its pulse. */
  .decide[data-state="done"] .decide-kicker i{opacity:1;background:var(--accent)}
  /* Finished: a tick in place of the dot, and nothing on the card moving. */
  .decide[data-state="settled"] .decide-kicker i{
    width:13px;height:13px;border-radius:4px;background-color:var(--accent);
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3.2 7.5l2.4 2.4 5.2-5.4' fill='none' stroke='%2304120F' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat:no-repeat;background-position:center;background-size:10px 10px;
    animation:none;opacity:1;
  }
  .decide[data-state="lapsed"]{border-left-color:rgba(226,120,110,.5)}
  .decide[data-state="lapsed"] .decide-kicker i{animation:none;opacity:.6;background:#E2786E}
  @media (prefers-reduced-motion:reduce){
    .decide-kicker i{animation:none;opacity:.75}
    .decide-bar::after{animation:none;width:100%;opacity:.5;background:var(--accent)}
  }
  @media (max-width:560px){ .decide{margin-left:0} .decide-btn{flex:1 1 100%} }

  .note{
    align-self:center; max-width:520px; text-align:center; font-size:13px; color:var(--dsub);
    padding:10px 15px; border:1px solid var(--dline); border-radius:12px; background:var(--dsurf);
  }
  .dateline{
    align-self:center; display:flex; align-items:center; gap:10px; width:100%; max-width:520px;
    font-family:var(--mono); font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--dlabel);
  }
  .dateline::before,.dateline::after{content:"";height:1px;background:var(--dline-soft);flex:1 1 auto}

  /* "Show earlier": the top of a thread that has grown past a dozen questions, folded away.
     It sits IN the scroller rather than pinned like button.jump — the reader who wants it is at
     the very top of the conversation, where it scrolls off as they read on, instead of hanging
     over the history they came back to read. */
  .earlier{align-self:center}
  .earlier button{
    min-height:34px; padding:5px 14px; cursor:pointer;
    border:1px solid var(--dline); border-radius:999px; background:var(--dsurf-hi); color:var(--dtext);
    font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase;
  }
  .earlier button:hover{background:hsla(196 34% 72% / .16)}

  /* The seam: a zero-height node between revealed history and what was already on screen.
     The grouping rules below use ADJACENCY (`+`) to decide that a message continues the one
     before it — so without a node in between, a reply revealed from history and the reply that
     was already drawn merge into one utterance: 4px apart, name and clock suppressed. Its
     negative margins eat the wrap's own 22px gap on both sides, so the seam is invisible. */
  .seam{height:0;margin-top:-11px;margin-bottom:-11px}

  /* The chats bar that used to sit here (sticky at the top of the thread: "New chat" and the way
     back through the old ones) is gone — owner direction 2026-09-18, "the feature isn't needed any
     more". Chats are no longer cut by hand, so there is nothing to start and nothing to step
     between; the live thread is the one conversation, folded to the window the server decides.
     What remains of archives is READ-only and URL-driven (`?chat=`), which is still why the
     reading-old rules below exist. */
  /* Reading an earlier chat: the composer goes, and the thread takes its row — a box that cannot
     send anything is a lie about what this screen does. */
  body.reading-old form.composer{display:none}

  /* A seat that cannot send says so where the box was, instead of offering a box.
     The public demo's composer read "Ask your agent anything" and was fully live; every
     message a stranger typed was refused by the server, so the first thing the demo taught
     a prospect was that the product does not work. The same class covers a viewer seat on a
     real tenant, whose copy is different — see setComposerMode in portal.js. */
  form.composer.is-viewonly .field,
  form.composer.is-viewonly .hintrow{display:none}
  .composer-note{
    margin:0; padding:11px 13px; border:1px solid var(--dline-soft); border-radius:var(--r-card);
    color:var(--dsub); font-size:13px; line-height:1.5;
  }
  .composer-note a{color:var(--accent); text-decoration:underline}
  body.reading-old .app{grid-template-rows:auto 1fr;grid-template-areas:"side" "thread"}
  @media (min-width:1000px){body.reading-old .app{grid-template-areas:"side thread"}}

  .empty{margin:auto;text-align:center;max-width:420px;padding:22px;display:flex;flex-direction:column;align-items:center;gap:12px}
  .empty h1{font-size:19px;font-weight:750;letter-spacing:-.025em;margin:0;color:var(--cloud)}
  .empty p{margin:0;color:var(--dsub);font-size:15px}
  /* States the console did not have. A read that failed used to be drawn as an empty inbox
     ("You're all caught up.", "Nothing new."), which is a claim about the client's work made on
     the strength of a request that never landed. The retry is the way out of it. */
  .waiting-nothing{margin:0 0 14px;color:var(--dsub);font-size:13px}
  .waiting-error{margin:8px 0 0;color:rgba(255,154,154,.95);font-size:12px;line-height:1.45}
  .alerts-error{margin:0;color:var(--dsub);font-size:14px}
  .waiting-retry,.alerts-retry{display:block;margin:16px auto 0;min-height:34px;padding:0 14px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}
  .waiting-retry:hover,.alerts-retry:hover{border-color:var(--accent);color:var(--cloud)}
  /* The notification's title is the control that opens it. It inherits the h3's type so the row
     reads exactly as it did, and the row itself is no longer a button with a button inside it. */
  .alert-open{width:100%;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer}
  .alert-open:hover{color:var(--cloud)}

  /* ---------- composer ---------- */
  form.composer{
    /* The positioning context for the jump bubble, which hangs above this panel (see button.jump).
       Relative with no z-index: the composer still paints where it did, under the sticky chats bar. */
    position:relative;
    grid-area:composer; display:flex; flex-direction:column; gap:8px;
    padding:13px 16px; padding-bottom:max(13px,env(safe-area-inset-bottom));
    border-top:1px solid var(--dline);
    background:rgba(3,8,10,.74); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  }
  /* 5px on all four sides: the field's inset used to be 15px left / 5px right, which was
     right for a bare text field and wrong once the + was inserted at the head of the row —
     it pushed the + 10px further in than Send and read as a control that had drifted. */
  .field{
    width:100%; max-width:768px; margin:0 auto;
    display:flex; align-items:flex-end; gap:8px;
    padding:5px; border:1px solid var(--dline); border-radius:15px; background:var(--dsurf);
    transition:border-color .16s ease, box-shadow .16s ease, background .16s ease;
  }
  .field:focus-within{border-color:rgba(79,196,178,.45);box-shadow:0 0 0 3px rgba(79,196,178,.09);background:var(--dsurf-hi)}
  textarea{
    /* A pixel floor, not 0. `min-width:0` lets the field shrink to nothing, but it also
       means the row's intrinsic width is the placeholder's full width — and that became the
       grid track's min-content, pushing the whole page 8px wider than a 390px phone once the
       attach control joined the row. A floor keeps the row shrinkable and the page inside
       the viewport. */
    flex:1 1 auto; min-width:76px; resize:none; min-height:44px; max-height:132px;
    padding:10px 0; border:0; background:transparent; color:var(--dtext);
    font-family:var(--sans); font-size:16px; line-height:1.45;
  }
  textarea:focus{outline:none}
  textarea::placeholder{color:var(--dlabel)}
  /* 10px, not 11px: the field is 15px with a 5px inset, and concentric radii are what make an
     inset control look seated rather than stuck on. The highlight is the small thing that
     stops a flat accent rectangle reading as a placeholder. */
  button.send{
    flex:0 0 auto; min-width:84px; min-height:46px; padding:0 18px; cursor:pointer;
    border:1px solid transparent; border-radius:10px; background:var(--accent); color:var(--ink);
    font-family:var(--sans); font-weight:650; font-size:14.5px; letter-spacing:-.005em;
    box-shadow:inset 0 1px 0 rgba(255,255,255,.18), 0 8px 20px -14px rgba(79,196,178,.75);
    transition:background .16s ease,transform .16s ease,box-shadow .16s ease;
  }
  button.send:hover{background:var(--accent-deep);box-shadow:inset 0 1px 0 rgba(255,255,255,.22), 0 10px 22px -14px rgba(79,196,178,.9)}
  button.send:active{transform:translateY(1px);box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
  button.send[disabled]{opacity:.5;cursor:default;transform:none}
  .hintrow{
    display:none; width:100%; max-width:768px; margin:0 auto; text-align:center;
    font-family:var(--mono); font-size:10px; letter-spacing:.06em; color:var(--dlabel); opacity:.8;
  }

  /* The "there is more below" bubble. It FLOATS above the composer rather than sitting in it:
     as the composer's first row, the panel's own blurred surface and its top border grew up to
     wrap the control, so the bubble read as part of the composer instead of a thing belonging to
     the thread above it (owner report 2026-09-18: "the background goes up to it").
     Out of flow it also stops resizing the composer when it appears, which retires the slack
     `atBottom()` carried for the pill's own height.
     It hangs over the composer's top edge, right-aligned to the MESSAGE COLUMN rather than to the
     window — `max(16px, (100% - 768px)/2)` is the field's own right edge under any symmetric
     composer padding, so the bubble lines up with Send at every width. Solid surface (--dpanel,
     not a translucent wash): it floats over message text and must not let it read through. */
  button.jump{
    position:absolute; z-index:5; bottom:calc(100% + 12px);
    right:max(16px, calc((100% - 768px) / 2));
    display:inline-flex; align-items:center; gap:8px;
    min-height:38px; padding:0 15px 0 13px; cursor:pointer;
    border:1px solid var(--dline); border-radius:999px;
    background:var(--dpanel); color:var(--dtext);
    font-family:var(--mono); font-size:10px; letter-spacing:.12em; text-transform:uppercase;
    box-shadow:0 14px 30px -16px rgba(0,0,0,.95), 0 2px 8px rgba(0,0,0,.4),
               inset 0 1px 0 hsla(198 22% 78% / .07);
    transition:background .16s ease, border-color .16s ease, color .16s ease, transform .16s ease,
               box-shadow .16s ease;
  }
  /* The way BACK DOWN is a downward arrow: it says which way the thread goes before the label
     is read, which is the whole of what a floating scroll control has to do. */
  button.jump svg{width:15px;height:15px;flex:0 0 auto;opacity:.9}
  button.jump:hover{background:hsl(240 8% 11%);border-color:hsla(198 22% 78% / .3)}
  button.jump:active{transform:translateY(1px)}
  /* Something actually arrived while you were reading back: the bubble wears the accent, so the
     state is visible at a glance and there is no badge to decode. */
  button.jump[data-fresh="1"]{
    border-color:rgba(79,196,178,.46); color:var(--accent);
    box-shadow:0 14px 30px -16px rgba(0,0,0,.95), 0 10px 26px -18px rgba(79,196,178,.9),
               inset 0 1px 0 hsla(196 34% 72% / .12);
  }
  button.jump[data-fresh="1"]:hover{background:hsl(196 24% 12%);border-color:rgba(79,196,178,.62)}
  button.jump[hidden]{display:none}

  /* A turn being written on ANOTHER device right now (see the live channel). It is drawn like
     any other agent turn so it does not read as a different kind of thing, with the working
     dots still on it and the last thing the agent did underneath. */
  .turn.live .progress{margin-top:0}
  .turn.live .bubble{opacity:.94}
  .live-stop{
    margin:7px 0 0 38px; font-family:var(--mono); font-size:10.5px; letter-spacing:.04em;
    color:var(--dlabel);
  }
  @media (max-width:560px){ .live-stop{margin-left:0} }

  /* A comment sent while the agent is still writing (see comment() below). It sits under the
     reader's own bubble, in the reader's own column, and says what happened to it: added to
     the answer being written, or held until that answer is done. Small and quiet — it is a
     receipt for the sender's own words, not a message of its own. */
  .atnote{
    margin:7px 2px 0 auto; font-family:var(--mono); font-size:10.5px; letter-spacing:.04em;
    color:var(--dlabel); text-align:right; max-width:min(76%, 420px);
  }
  @media (max-width:560px){ .atnote{max-width:88%} }

  @media (prefers-reduced-motion:reduce){
    .pending i{animation:none;opacity:.6}
    .bubble.writing::after{animation:none;opacity:.8}
    button.send:active,button.attach:active{transform:none}
  }

  /* Grid items default to `min-width:auto`, which means the widest min-content inside a
     track becomes the track's floor — the composer row was 398px of min-content, so the
     whole page was 8px wider than a 390px phone. These three are the grid areas; letting
     them shrink is what keeps every screen inside the viewport. */
  .app > aside.side,.app > main.thread,.app > form.composer{min-width:0}

  /* ---------- documents ----------
     A document is a turn in the same timeline as the conversation, because that is how a
     client thinks about it: "I sent you the quote on Tuesday" sits between what they said
     on Monday and what the agent said on Wednesday. The card carries the name, the size,
     who it came from and one download button — nothing else, because the alternative is a
     file manager nobody asked for. */
  .fcard{
    display:flex; align-items:center; gap:11px; min-width:0;
    padding:11px 12px; border:1px solid var(--dline); border-radius:13px;
    background:var(--dsurf); max-width:min(100%,26rem);
  }
  .ficon{
    flex:0 0 auto; width:36px; height:40px; border-radius:8px;
    display:flex; align-items:center; justify-content:center;
    border:1px solid var(--dline); background:hsla(196 34% 72% / .10); color:var(--dsub);
    font-family:var(--mono); font-size:9px; letter-spacing:.04em; text-transform:uppercase;
  }
  .fmeta{min-width:0; flex:1 1 auto; display:flex; flex-direction:column; gap:2px}
  .fmeta b{font-size:14.5px; font-weight:600; color:var(--dtext); overflow-wrap:anywhere}
  .fmeta small{font-family:var(--mono); font-size:9.5px; letter-spacing:.05em; color:var(--dlabel)}
  .fnote{font-size:13px; line-height:1.45; color:var(--dsub); overflow-wrap:anywhere}
  a.fdl{
    flex:0 0 auto; text-decoration:none; padding:9px 11px; border-radius:9px;
    border:1px solid var(--dline); color:var(--accent);
    font-family:var(--mono); font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  }
  a.fdl:hover{border-color:rgba(79,196,178,.45); background:hsla(196 34% 72% / .07)}

  /* The dock is above the box, so an upload is visible while it happens — a card that
     appears with no explanation is how "did it send?" turns into a support question. */
  .dock{width:100%; max-width:768px; margin:0 auto; display:flex; flex-direction:column; gap:6px}
  /* `hidden` has to win. The UA's [hidden]{display:none} loses to the author `display:flex`, so a
     hidden dock stayed a 0-height item in the composer's column and its 8px gap pushed the field
     down — measured: 21px above the field against 13px below, which is the composer looking
     top-heavy for no visible reason. */
  .dock[hidden]{display:none}
  .chip{
    display:flex; align-items:center; gap:9px; min-width:0;
    padding:8px 11px; border:1px solid var(--dline); border-radius:10px; background:var(--dsurf);
    font-size:13.5px; color:var(--dsub);
  }
  .chip b{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dtext); font-weight:600}
  .chip small{flex:0 0 auto; max-width:58%; text-align:right; font-family:var(--mono); font-size:9.5px; letter-spacing:.04em; color:var(--dlabel)}
  .chip .bar{flex:1 1 auto; height:3px; border-radius:2px; background:var(--dline); overflow:hidden}
  .chip .bar i{display:block; height:100%; width:0; background:var(--accent); transition:width .2s ease}
  .chip.bad{border-color:rgba(255,140,140,.45)}
  .chip.bad small{color:#ffb3b3}
  /* A staged attachment is NOT on its way yet: it waits for Send, so the text in the box can
     become its caption — and it can be taken back. The × is a real button, because on a phone
     there is no "just clear the field". */
  .chip.staged{border-style:solid}
  .chip.staged b{flex:1 1 auto}
  .chip .x{
    flex:0 0 auto; width:26px; height:26px; padding:0; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    border:0; border-radius:7px; background:transparent; color:var(--dsub);
  }
  .chip .x:hover{background:var(--dline); color:var(--dtext)}
  .chip .x svg{display:block; width:11px; height:11px}
  /* The caption is carried by the file, so the thread shows it under the file's card rather
     than as a second bubble. Same voice, one turn. */
  .fnote{font-style:normal}
  /* The cross is a 24-unit drawing in a flex-centred box, not a "+" character: a text glyph
     is positioned by its font's baseline and math axis, and at 20px/1 that put the ink 3px
     below the button's centre (measured from a 4x screenshot). Geometry cannot drift. */
  button.attach{
    flex:0 0 auto; width:46px; min-height:46px; padding:0; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    border:1px solid var(--dline); border-radius:10px; background:transparent;
    color:var(--dsub);
    transition:border-color .16s ease, color .16s ease, background .16s ease, transform .16s ease;
  }
  button.attach svg{display:block;width:19px;height:19px}
  button.attach:hover{border-color:rgba(79,196,178,.45); color:var(--accent); background:var(--dsurf)}
  button.attach:active{transform:translateY(1px)}
  button.attach[disabled]{opacity:.45;cursor:default;transform:none}
  ul.docs{display:none}
  .docs-all{display:none}

  /* ---------- desktop: the rail ---------- */
  @media (min-width:1000px){
    .app{
      grid-template-columns:264px 1fr; grid-template-rows:1fr auto;
      grid-template-areas:"side thread" "side composer";
    }
    .side{
      flex-direction:column; align-items:stretch; gap:16px;
      padding:24px 22px; border-bottom:0; border-right:1px solid var(--dline);
      background:linear-gradient(180deg, hsla(196 34% 72% / .045), rgba(3,8,10,0) 46%);
    }
    .brand{gap:12px}
    .mark{font-size:30px}
    .brandtext{display:flex;flex-direction:column;gap:4px;line-height:1.1}
    .brandtext b{font-size:16px;font-weight:800;letter-spacing:-.03em;color:var(--cloud)}
    .brandtext small{
      font-family:var(--mono);font-size:9.5px;letter-spacing:.15em;
      text-transform:uppercase;color:var(--dlabel);
    }
    .railhead{
      display:block; margin-top:6px; padding-top:16px; border-top:1px solid var(--dline-soft);
      font-family:var(--mono); font-size:9.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--dlabel);
    }
    .identity{flex:0 0 auto;padding:14px 15px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
    .identity .biz{font-size:15.5px;white-space:normal;line-height:1.35}
    .identity .agent-name{margin-top:6px}
    .status{align-self:flex-start}
    .conversation-tools{flex:0 0 auto}
    /* The rail opens the panel low on the screen — under the identity card — so its cap has to leave
       that much room above it, or a long conversation list runs off the bottom of the window.
       Measured 2026-09-30: the panel's top edge sits at y=353 at rail width. */
    .conversation-panel{max-height:min(60vh,calc(100vh - 390px))}
    .sidefoot{
      display:block; margin-top:auto; padding-top:16px; border-top:1px solid var(--dline-soft);
      font-family:var(--mono); font-size:10px; line-height:1.8; letter-spacing:.04em; color:var(--dlabel);
    }
    ul.docs{
      display:flex; flex-direction:column; gap:2px; min-height:0; overflow-y:auto;
      margin:0; padding:0; list-style:none;
    }
    ul.docs li{margin:0}
    ul.docs a{
      display:block; padding:7px 9px; border-radius:8px; text-decoration:none;
      border:1px solid transparent; color:var(--dsub); font-size:13px; line-height:1.35;
      overflow-wrap:anywhere;
    }
    ul.docs a:hover{border-color:var(--dline); background:var(--dsurf); color:var(--dtext)}
    ul.docs a small{display:block; font-family:var(--mono); font-size:9px; letter-spacing:.05em; color:var(--dlabel)}
    .docs-all{display:block; margin-top:4px; color:var(--accent); font:10px var(--mono); text-decoration:none}
    main.thread{padding:32px 44px 16px}
    form.composer{padding:15px 44px 17px}
    /* The hint explains the composer's own keyboard affordances, so it belongs over the
       conversation and nowhere else. It used to print under the composer on Invoices, Log, Today
       and Waiting too: a developer note on four screens that have no conversation on them. It is
       desktop-only for the same reason it exists at all — a soft keyboard has no Shift key. */
    body[data-tab="ask"] .hintrow{display:block}
    .wrap{gap:24px}
  }
  @media (min-width:1000px) and (max-width:1180px){
    main.thread,form.composer{padding-left:30px;padding-right:30px}
  }

  /* ---------- console shell ---------- */
  .mast-tools{display:flex;align-items:center;gap:8px;flex:0 0 auto;margin-left:auto}
  .mast-action{display:grid;place-items:center;width:32px;height:32px;padding:0;border:1px solid var(--dline);border-radius:9px;background:var(--dsurf);color:var(--dlabel)}
  .mast-action svg{width:16px;height:16px}
  .account-action{display:grid;place-items:center;width:32px;height:32px;padding:0;border:1px solid var(--dline);border-radius:9px;background:var(--dsurf);color:var(--dsub);cursor:pointer}
  .account-action svg{width:16px;height:16px}
  .mast-action{position:relative;cursor:pointer}
  .mast-action:hover{color:var(--dtext);border-color:var(--dline-strong)}
  .mast-badge,.tab-badge{display:inline-grid;place-items:center;min-width:16px;height:16px;padding:0 4px;border-radius:99px;background:var(--accent);color:#061011;font:9px var(--mono);font-weight:700}
  .mast-badge{position:absolute;top:-6px;right:-6px}
  /* `hidden` has to win here, and it did not: these badges set a display of their own, and an author
     display beats the UA's `[hidden]{display:none}`. `badge()` in portal-waiting.js empties AND hides
     a badge at zero, but the bubble was still painted — measured 2026-09-26 at 390px: the Waiting
     cell wore a 16x16 "0" pill, `hidden` true and `display:grid`, at all times. Same failure as
     `.tab[hidden]` above; the badge needed the rule the tabs got. */
  .mast-badge[hidden],.tab-badge[hidden]{display:none}
  .search-slot{display:flex;align-items:center;position:relative;width:184px}
  .search-slot input{width:100%;height:44px;padding:0 42px 0 30px;border:1px solid var(--dline);border-radius:9px;background:var(--dsurf);color:var(--dtext);font:11px var(--mono);letter-spacing:.02em}
  .search-slot input::placeholder{color:var(--dlabel)}
  .search-slot input:disabled{opacity:.62;cursor:default}
  .search-submit{position:absolute;right:0;top:0;min-width:44px;min-height:44px;padding:0;border:0;background:transparent;color:var(--dlabel);font-size:18px;cursor:pointer}
  .tabs{grid-area:tabs;display:flex;align-items:center;gap:4px;padding:10px 16px 0;border-bottom:1px solid var(--dline);background:rgba(3,8,10,.62);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
  .tab{position:relative;display:inline-flex;align-items:center;min-height:36px;padding:0 13px;color:var(--dlabel);text-decoration:none;font-size:13px;font-weight:700;letter-spacing:-.01em}
  .tab:hover{color:var(--dtext)}
  .tab.is-on{color:var(--cloud)}
  .tab.is-on::after{content:"";position:absolute;right:10px;bottom:-1px;left:10px;height:2px;border-radius:2px 2px 0 0;background:var(--accent)}
  /* `hidden` has to win here, and it did not. `.tab` sets a display of its own, and an author
     display beats the UA's `[hidden]{display:none}` — so every tab the tier gate turned off stayed
     on screen and clickable, and a `site` tenant's bar drew Dash, Invoices, Docs and Value
     anyway (measured 2026-09-25 at 390px: four tabs it does not buy, 43px each, `hidden` true and
     `display:flex`). Every gated panel already carries its own rule like this one; the tabs did not. */
  .tab[hidden]{display:none}
  /* `More` is the last cell of the row, and the six destinations it opens live in `.more-sheet`.
     Wider than a phone, the sheet and its list are `display:contents`, so those six are the row
     items they have always been and the row is unchanged. The `order` values are today's row order
     spelled out, because the sheet's items are contiguous in the DOM and the row's are not — without
     them a desktop row would read Chat, Today, Waiting, Docs, then the six. Scoped to a width where
     the row is one line: on a phone those same values would sort the row into the wrong order.
     `#tab-team` carries a value like every other tab, even though it is owner-only and `hidden` for
     everyone else: an undeclared tab computes to `order:0` and an owner's row drew Team FIRST, left
     of Dash (measured 2026-09-27 at 1440px, x=308 against Dash's 371). */
  @media (min-width:761px){
    .tab.tab-more{display:none}
    .more-head{display:none}
    .more-sheet,.more-list,.more-list li{display:contents}
    #tab-noc{order:1}#tab-ask{order:2}#tab-today{order:3}#tab-website{order:4}#tab-waiting{order:5}#tab-log{order:6}#tab-invoices{order:7}#tab-documents{order:8}#tab-team{order:9}#tab-data{order:10}#tab-apps{order:11}
  }
  .panel-today{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}
  .panel-ask{display:contents}
  .panel-ask[hidden]{display:none}
  .panel-today[hidden]{display:none}
  .today{width:min(100%,980px);margin:0 auto;padding-bottom:20px}
  .today-head{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;margin-bottom:24px}
  .today-kicker,.today-label{margin:0;font:10px var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--dlabel)}
  .today-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}
  .today-state{display:flex;align-items:center;gap:7px;color:var(--dsub);font:11px var(--mono);white-space:nowrap}
  .today-state-dot{width:7px;height:7px;border-radius:50%;background:var(--dlabel)}
  .today-grid{display:grid;grid-template-columns:minmax(0,1.12fr) minmax(0,.88fr);gap:14px}
  .today-card{min-width:0;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
  .today-card-wide{grid-column:1/-1}
  .today-card h2{margin:4px 0 14px;color:var(--cloud);font-size:18px;line-height:1.25;letter-spacing:-.025em}
  .today-list{display:grid;gap:9px;margin:0;padding:0;list-style:none}
  .today-row{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;padding:10px 0;border-top:1px solid var(--dline-soft)}
  .today-row:first-child{border-top:0;padding-top:0}
  .today-row:last-child{padding-bottom:0}
  .today-row-main{min-width:0}
  .today-row-title{display:block;color:var(--dtext);font-size:14px;line-height:1.4}
  .today-row-detail{display:block;margin-top:3px;color:var(--dsub);font-size:12px;line-height:1.45;overflow-wrap:anywhere}
  .today-row-time{flex:0 0 auto;color:var(--dlabel);font:10px var(--mono);white-space:nowrap}
  .today-more{margin:10px 0 0;color:var(--accent);font:11px var(--mono)}
  .today-empty{margin:0;color:var(--dsub);font-size:14px}
  .today-link{color:var(--accent);text-decoration:none}
  .today-link:hover{text-decoration:underline}
  .today-docs a{display:flex;align-items:center;justify-content:space-between;gap:12px}
  .today-docs .today-row-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .today-thread-link{display:inline-block;margin-top:12px;color:var(--accent);font-size:12px;font-weight:700;text-decoration:none}
  .today-thread-link:hover{text-decoration:underline}
  .panel-waiting{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}
  .panel-waiting[hidden]{display:none}
  .waiting{width:min(100%,980px);margin:0 auto;padding-bottom:20px}
  .waiting-head{margin-bottom:24px}.waiting-kicker,.waiting-label{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em;text-transform:uppercase}
  .waiting-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}
  .waiting-card{margin-top:14px;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
  .waiting-card:first-of-type{margin-top:0}.waiting-group-detail{margin:7px 0 0;color:var(--dsub);font-size:12px;line-height:1.45}
  .waiting-list{display:grid;gap:9px;margin-top:14px}.waiting-row{display:flex;justify-content:space-between;gap:18px;padding:12px 0;border-top:1px solid var(--dline-soft)}
  .waiting-row:first-child{border-top:0;padding-top:0}.waiting-row:last-child{padding-bottom:0}.waiting-row-main{min-width:0}.waiting-row-title{margin:0;color:var(--dtext);font-size:14px;line-height:1.4}.waiting-row-detail{margin:4px 0 0;color:var(--dsub);font-size:12px;line-height:1.45;overflow-wrap:anywhere}.waiting-due{margin:6px 0 0;color:var(--accent);font:10px var(--mono)}
  .waiting-row-side{display:flex;flex:0 0 auto;align-items:flex-start;gap:10px}.waiting-row-time,.alert-time{color:var(--dlabel);font:10px var(--mono);white-space:nowrap}.waiting-choices{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.waiting-action,.alert-action,.alerts-mark{min-height:30px;padding:0 10px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}.alerts-mark[hidden],#alerts-badge[hidden]{display:none}.waiting-action:hover,.alert-action:hover,.alerts-mark:hover{border-color:var(--accent);color:var(--cloud)}
  .waiting-empty,.alerts-empty{margin:0;color:var(--dsub);font-size:14px}.alerts-panel{position:absolute;z-index:5;top:74px;right:16px;width:min(360px,calc(100vw - 32px));max-height:min(560px,calc(100vh - 96px));overflow:auto;padding:16px;border:1px solid var(--dline);border-radius:var(--r-card);background:rgba(10,18,21,.98);box-shadow:0 20px 60px rgba(0,0,0,.35)}.alerts-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.alerts-head h2{margin:0;color:var(--cloud);font-size:15px}.alerts-list{display:grid;gap:8px}.alert-row{display:flex;gap:10px;padding:11px 0;border-top:1px solid var(--dline-soft)}.alert-row:first-child{border-top:0}.alert-main{min-width:0;flex:1}.alert-title{margin:0;color:var(--dtext);font-size:12px;line-height:1.4}.alert-body{margin:3px 0;color:var(--dsub);font-size:11px;line-height:1.45;overflow-wrap:anywhere}.alert-action{align-self:flex-start;white-space:nowrap}.alert-row.is-read{opacity:.68}
  .panel-log,.panel-search,.panel-invoices,.panel-documents{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}
  .panel-log[hidden],.panel-search[hidden],.panel-invoices[hidden],.panel-documents[hidden]{display:none}
  .log,.search-results{width:min(100%,980px);margin:0 auto;padding-bottom:20px}
  .log-head,.search-head{margin-bottom:24px}.log-kicker,.search-kicker{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em}.log-title,.search-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}
  .log-day{margin-top:20px;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.log-day:first-of-type{margin-top:0}.log-day-head{display:flex;justify-content:space-between;gap:16px;align-items:baseline}.log-day-title{margin:0;color:var(--cloud);font-size:15px}.log-day-usage{margin:0;color:var(--accent);font:10px var(--mono);white-space:nowrap}.log-entry{padding:15px 0;border-top:1px solid var(--dline-soft)}.log-entry:first-of-type{border-top:0;padding-top:14px}.log-entry-at,.search-row-time{display:block;color:var(--dlabel);font:10px var(--mono)}.log-question{margin:6px 0 0;color:var(--dtext);font-size:15px;line-height:1.45}.log-answer{margin:8px 0 0;color:var(--dsub);font-size:14px;line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}.log-meta{margin:8px 0 0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.03em}.log-produced{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0 0;padding:0;list-style:none}.log-produced li{padding:5px 8px;border:1px solid var(--dline);border-radius:6px;color:var(--dsub);font:10px var(--mono);overflow-wrap:anywhere}.log-empty,.search-empty{margin:0;color:var(--dsub);font-size:14px}.log-more{display:block;margin:16px auto 0;min-height:34px;padding:0 14px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}.log-more:hover{border-color:var(--accent);color:var(--cloud)}
  .search-group{margin-top:14px;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.search-group:first-of-type{margin-top:0}.search-group-title{margin:0;color:var(--cloud);font-size:15px}.search-list{display:grid}.search-row{padding:13px 0;border-top:1px solid var(--dline-soft)}.search-row:first-child{margin-top:10px}.search-row-title{margin:0;color:var(--dtext);font-size:14px;line-height:1.4}.search-row-detail{margin:4px 0;color:var(--dsub);font-size:13px;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}.search-row-time{margin-top:6px}
  .invoices{width:min(100%,980px);margin:0 auto;padding-bottom:20px}.invoices-head{margin-bottom:24px}.invoices-kicker{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em;text-transform:uppercase}.invoices-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}.invoice-row,.invoice-detail{padding:17px 18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.invoice-row{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;width:100%;text-align:left;color:inherit;cursor:pointer}.invoice-row:hover{border-color:var(--accent)}.invoice-number{margin:0;color:var(--cloud);font-size:15px}.invoice-meta,.invoice-copy,.invoice-muted{margin:5px 0 0;color:var(--dsub);font-size:12px;line-height:1.5;overflow-wrap:anywhere}.invoice-total{color:var(--cloud);font:12px var(--mono);white-space:nowrap}.invoice-empty{margin:0;color:var(--dsub);font-size:14px}.invoice-detail-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.invoice-back,.invoice-claim button{min-height:32px;padding:0 11px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}.invoice-back:hover,.invoice-claim button:hover{border-color:var(--accent);color:var(--cloud)}.invoice-lines{display:grid;margin:20px 0 0;border-top:1px solid var(--dline-soft)}.invoice-line{display:flex;justify-content:space-between;gap:14px;padding:11px 0;border-bottom:1px solid var(--dline-soft);color:var(--dtext);font-size:13px}.invoice-line small{display:block;color:var(--dsub);font-size:11px}.invoice-copy{white-space:pre-wrap}.invoice-claim{margin-top:20px;padding-top:18px;border-top:1px solid var(--dline-soft)}.invoice-claim h3{margin:0;color:var(--cloud);font-size:14px}.invoice-claim label{display:grid;gap:6px;margin-top:12px;color:var(--dlabel);font:10px var(--mono);text-transform:uppercase;letter-spacing:.08em}.invoice-claim input,.invoice-claim textarea{width:100%;padding:9px;border:1px solid var(--dline);border-radius:7px;background:var(--dsurf);color:var(--dtext);font:12px var(--mono)}.invoice-claim textarea{min-height:64px;resize:vertical}.invoice-claim button{margin-top:12px;color:var(--cloud)}.invoice-payment{padding:11px 0;border-top:1px solid var(--dline-soft);color:var(--dsub);font-size:12px;line-height:1.45}.invoice-status{margin:12px 0 0;color:var(--accent);font:11px var(--mono)}
  .documents{width:min(100%,980px);margin:0 auto;padding-bottom:20px;color:var(--dsub)}
  .documents-head{margin-bottom:24px}.documents-kicker{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em}.documents-title{margin:5px 0 0;color:var(--dsub);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}.documents-summary{margin:8px 0 0;color:var(--dsub);font:11px var(--mono)}
  .documents-controls{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-bottom:20px}.documents-filters{display:flex;flex-wrap:wrap;gap:6px}.documents-filter,.documents-controls input,.documents-more{min-height:40px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf);color:var(--dsub);font:10px var(--mono)}.documents-filter{padding:0 11px;cursor:pointer}.documents-filter[aria-pressed="true"],.documents-filter:hover,.documents-more:hover{border-color:var(--accent);color:var(--accent)}.documents-controls input{width:min(260px,100%);padding:0 11px}.documents-controls input::placeholder{color:var(--dlabel)}
  .document-month{margin-top:20px;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.document-month:first-child{margin-top:0}.document-month-title{margin:0;color:var(--dsub);font-size:15px}.document-row{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:15px 0;border-top:1px solid var(--dline-soft)}.document-row:first-of-type{margin-top:10px}.document-row-main{min-width:0}.document-kind{display:inline-flex;align-items:center;justify-content:center;width:34px;height:24px;margin-right:9px;border:1px solid var(--dline);border-radius:var(--r-card);color:var(--dlabel);font:9px var(--mono)}.document-name{color:var(--dsub);font-size:14px;line-height:1.4;overflow-wrap:anywhere}.document-meta{margin:6px 0 0;color:var(--dsub);font:10px var(--mono);overflow-wrap:anywhere}.document-note{margin:7px 0 0;color:var(--dsub);font-size:12px;line-height:1.45;overflow-wrap:anywhere}.document-download{flex:0 0 auto;color:var(--accent);font:10px var(--mono);text-decoration:none}.document-download:hover{text-decoration:underline}.documents-more{display:block;margin:16px auto 0;padding:0 14px;cursor:pointer}.documents-empty-copy{margin:0;color:var(--dsub);font-size:14px}.documents-chat-link{display:inline-block;margin-top:12px;color:var(--accent);font-size:13px;text-decoration:none}.documents-chat-link:hover{text-decoration:underline}
  @media (max-width:760px){
    .app{grid-template-rows:auto auto minmax(0,1fr) auto;grid-template-areas:"side" "tabs" "thread" "composer"}
    .tabs{padding-left:16px;padding-right:16px}
    .panel-today{padding:22px 16px 20px}
    .panel-waiting{padding:22px 16px 20px}
    .panel-log,.panel-search,.panel-invoices,.panel-documents{padding:18px 16px 0}
    .today-head{align-items:flex-start;flex-direction:column;gap:10px;margin-bottom:18px}
    .today-grid{grid-template-columns:1fr}
    .today-card-wide{grid-column:auto}
  }
  @media (min-width:761px) and (max-width:999px){
    .app{grid-template-columns:1fr;grid-template-rows:auto auto minmax(0,1fr) auto;grid-template-areas:"side" "tabs" "thread" "composer"}
  }
  @media (min-width:1000px){
    .app{grid-template-columns:264px 1fr;grid-template-rows:auto 1fr auto;grid-template-areas:"side tabs" "side thread" "side composer"}
    .tabs{padding-left:44px;padding-right:44px}
    .side .mast-tools{width:100%;margin-left:0}
    .side .search-slot{flex:1 1 auto;width:auto}
    .panel-today{padding-left:44px;padding-right:44px}
    .panel-waiting{padding-left:44px;padding-right:44px}
    .panel-log,.panel-search,.panel-invoices,.panel-documents{padding-left:44px;padding-right:44px}
    .side{position:relative}
  }
  @media (min-width:1000px) and (max-width:1180px){
    .tabs{padding-left:30px;padding-right:30px}
    .panel-today{padding-left:30px;padding-right:30px}
    .panel-waiting{padding-left:30px;padding-right:30px}
    .panel-log,.panel-search,.panel-invoices,.panel-documents{padding-left:30px;padding-right:30px}
  }
  body.reading-old .app{grid-template-rows:auto auto 1fr;grid-template-areas:"side tabs" "side thread" "side thread"}
  body.reading-old .tabs{display:flex}
  @media (max-width:760px){body.reading-old .app{grid-template-rows:auto auto 1fr;grid-template-areas:"side" "tabs" "thread"}}

  /* portal redesign: controls remain comfortable on a finger and the two header popovers stay
     attached to their controls instead of covering the bell. */
  .composer[hidden]{display:none}
  .tabs{overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;white-space:nowrap}
  .tabs::-webkit-scrollbar{display:none}
  .tab{min-height:44px;flex:0 0 auto}
  .mast-tools{position:relative}
  .mast-action,.account-action{min-width:44px;min-height:44px;width:auto}
  .account-action{padding:0}
  .account-menu{position:absolute;z-index:8;top:calc(100% + 8px);right:0;width:min(280px,calc(100vw - 24px));padding:16px;border:1px solid var(--dline);border-radius:var(--r-card);background:rgba(10,18,21,.99);box-shadow:0 20px 60px rgba(0,0,0,.35)}
  .is-target{outline:2px solid var(--accent);outline-offset:4px;scroll-margin-block:35vh}
  .focus-failure{margin:12px 0;padding:10px 12px;border:1px solid var(--dline);border-radius:8px;color:var(--dsub);font-size:13px}
  .account-email,.account-business,.account-role{margin:0;color:var(--dtext);font-size:12px;overflow-wrap:anywhere}.account-business,.account-role{margin-top:5px;color:var(--dsub);font-size:11px}.account-signout,.alerts-close,.search-close{min-width:44px;min-height:44px;border:1px solid var(--dline);border-radius:8px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}.account-signout{width:100%;margin-top:14px}.account-signout:hover,.alerts-close:hover,.search-close:hover{border-color:var(--accent);color:var(--cloud)}
  .search-slot{position:relative;min-width:44px}.search-icon{position:absolute;left:11px;top:11px;color:var(--dlabel);font-size:18px;line-height:1;pointer-events:none}.search-slot input{height:44px;padding-left:30px}
  /* Owner report 2026-09-19: "on PC the notifications go off the screen to the left, you can't read
     them." The panel hung off the tools cluster's RIGHT edge, which in the two-column layout
     (>=1000px) sits inside the 264px rail, so a 380px panel ran past the viewport's left edge
     (measured: -6px at 768-999px, -191px at 1024-1920px). It now hangs off the cluster's LEFT edge
     and opens rightward over the content column: measured at x=22 for >=1000px and x=66 for the
     full-width header below that, always fully inside the viewport. The account menu keeps its
     right-anchored form down there because 280px fits from the cluster's right edge, and flips at
     1000px to match. Declared after both popovers on purpose: media queries add no specificity, so
     source order is what flips them.
     The 220px reserve is for short viewports: at 844x390 the header ends around y=150, so a 70vh
     panel ran off the bottom edge. Fixed positioning is not an option here — aside.side carries
     backdrop-filter:blur(12px), which makes it the containing block for fixed children, so a fixed
     panel resolves against the 32x250 rail instead of the viewport (measured: panel at y=-56). */
  .alerts-panel{position:absolute;top:calc(100% + 8px);left:0;right:auto;width:min(380px,calc(100vw - 32px));max-height:min(70vh,calc(100vh - 220px));overflow:auto;padding:16px}
  @media (min-width:1000px){.account-menu{left:0;right:auto}}
  /* .side carries backdrop-filter:blur(12px), so it is a stacking context: no z-index on the panel
     itself can lift it above later siblings (.tabs z-index 21, .composer). Raising .side while a
     popover is open is what actually puts the dropdown on top — measured at 844x390, the composer
     input was hit-testing over the open panel before this. */
  .side:has(.alerts-panel:not([hidden])),.side:has(.account-menu:not([hidden])),.side:has(.conversation-panel:not([hidden])){z-index:40}
  .alerts-panel:focus{outline:none}.alerts-head{align-items:flex-start}.alerts-head-actions{display:flex;align-items:center;gap:6px}.alerts-count{margin:3px 0 0;color:var(--dlabel);font:10px var(--mono)}.alerts-mark{min-height:44px}.alerts-close{font-size:18px;line-height:1}.alert-row{align-items:flex-start}.alert-dot{width:7px;height:7px;margin-top:6px;border-radius:50%;background:var(--accent);flex:0 0 auto}.alert-row.is-read .alert-dot{display:none}.alert-action{min-height:44px}
  .needs-strip{display:flex;align-items:center;justify-content:space-between;gap:12px;max-width:768px;margin:18px auto 0;padding:12px 16px;border:1px solid rgba(79,196,178,.4);border-radius:var(--r-card);background:#0d1a19;color:var(--dtext);font-size:13px}.needs-strip[hidden]{display:none}.needs-strip-link{min-height:44px;display:inline-flex;align-items:center;color:var(--accent);font-weight:700;text-decoration:none}
  .today-more{display:inline-flex;min-height:44px;align-items:center}.today-summary{margin:0;color:var(--dsub);font-size:14px}.today-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.today-stat{display:flex;min-height:76px;flex-direction:column;justify-content:center;gap:5px;padding:10px;border:1px solid var(--dline);border-radius:9px;color:var(--dtext);text-decoration:none}.today-stat:hover{border-color:var(--accent)}.today-stat strong{font-size:24px;line-height:1;color:var(--cloud)}.today-stat span{color:var(--dsub);font:10px var(--mono);line-height:1.25}.today-window{margin:10px 0 0;color:var(--dsub);font:11px var(--mono);line-height:1.4}.today-example{margin:10px 0 0;color:var(--dsub);font-size:13px;line-height:1.5}.today-docs .today-row-title{overflow-wrap:anywhere;white-space:normal;text-overflow:clip}.search-ask{display:inline-flex;align-items:center;min-height:44px;margin-top:12px;color:var(--accent);font-size:13px;font-weight:700}
  .search-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.search-head-copy{min-width:0}.search-count{margin:0 0 14px;color:var(--dlabel);font:10px var(--mono)}.search-row{display:block;min-height:44px;color:inherit;text-decoration:none}.search-row:hover{background:var(--dsurf)}
  .waiting-action,.invoice-back,.invoice-claim button,.log-more{min-height:44px}
  .backdrop::after{opacity:.22;transform:translateY(18vh)}
  .biz{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}
  @media (max-width:760px){.side{gap:8px;align-items:flex-start;flex-wrap:wrap}.status{padding:2px 0;min-height:0;border:0;background:transparent}.mast-tools{gap:4px;flex:1 1 100%;width:100%;min-width:0;margin-left:0;flex-wrap:wrap;justify-content:flex-end}.search-slot{order:5;flex:1 1 100%;width:100%;min-width:0}.today-grid{gap:12px}.today-card{padding:16px}.today-stats{gap:6px}.today-stat{min-height:72px;padding:9px 8px}.panel-ask{overflow:auto}.needs-strip{margin:12px 16px 0}.composer{min-width:0}}
  @media (min-width:1000px) and (max-width:1240px){.search-slot{width:150px}}
  /* Tablet widths keep the header on ONE line. A 100% flex basis on the Conversation group made it
     take a full row and squeeze the business name into six lines (measured 2026-09-30 at 768px: a
     245px header, 141px of it a wrapped name). As a chip at the end of the row the group costs
     nothing: the name gets its width back and the header is a single 68px line. The panel stays
     anchored to the chip's LEFT edge — right-anchoring it here would run it off the left of the
     screen, because the chip sits before the search/bell/account cluster, not after it. */
  @media (min-width:761px) and (max-width:999px){
    .conversation-tools{flex:0 0 auto;width:auto}
    .conversation-menu{width:auto}
    /* Collateral repair, same gate. Below 1000px the tools cluster is squeezed to the RIGHT end of
       the header row, so a panel anchored to its left edge (`left:0`, the rule for the two-column
       rail) starts at x=464 at 768px and runs 76px off the screen — the alerts panel had that
       defect at 761-999px at every one of these widths. Right-anchored it hangs off the cluster's
       right edge, which is the header's own padding edge. */
    .alerts-panel{left:auto;right:0}
  }
  .panel-ask{display:flex;flex-direction:column;min-height:0;overflow:hidden}.panel-ask main.thread{flex:1 1 auto;min-height:0}

  /* ---------- phone app shell ---------- */
  .tab-icon{display:contents}
  .tab-icon svg{display:none}
  @media (max-width:760px){
    .app{
      grid-template-rows:auto minmax(0,1fr) auto auto;
      grid-template-areas:"side" "thread" "composer" "tabs";
    }

    .side{
      display:grid; grid-template-columns:auto minmax(0,1fr) 44px 44px;
      grid-template-rows:auto auto; column-gap:0; row-gap:4px;
      align-items:center; padding: max(6px, env(safe-area-inset-top)) 16px 6px;
      background:var(--ink); backdrop-filter:none; -webkit-backdrop-filter:none;
    }
    .brand{grid-column:1;grid-row:1;gap:0}
    .mark{font-size:26px}
    .identity{grid-column:2;grid-row:1;min-width:0;align-self:center}
    .biz{font-size:16px;line-height:1.15;letter-spacing:-.02em;white-space:normal;overflow:visible;text-overflow:clip}
    .identity-meta{display:flex;align-items:center;min-width:0;line-height:1.2}
    .trialpill{display:inline;vertical-align:baseline;font-size:10px;margin-left:8px;margin-top:0;overflow:hidden;text-overflow:ellipsis}
    .agent-name{display:inline;min-width:0;margin:0;overflow:visible;text-overflow:clip;font-size:11px;letter-spacing:.04em;text-transform:none;white-space:nowrap}
    .status{display:flex;align-items:center;gap:6px;min-width:0;padding:0;border:0;background:transparent;font-size:11px;letter-spacing:.02em;text-transform:none;white-space:nowrap}
    .status::before{content:"·";margin:0 6px;color:var(--dlabel)}
    .dot{width:7px;height:7px}
    .mast-tools{display:contents}
    .mast-action{grid-column:3;grid-row:1;width:44px;height:44px}
    .mast-action svg{width:20px;height:20px}
    .account-action{grid-column:4;grid-row:1;width:44px;height:44px;padding:0}
    .account-action svg{width:20px;height:20px}
    .search-slot{grid-column:1/4;grid-row:2;width:100%;min-width:0;height:44px}
    /* The Conversation control takes the fourth cell of the SEARCH row, under the account button.
       The header is two 44px rows on a phone against a 116px budget (portal_mobile_check), so the
       group cannot have a row of its own; the panel it opens is what carries the list. */
    .conversation-tools{grid-column:4;grid-row:2;display:block;align-self:center;min-width:0}
    .conversation-menu{width:44px;height:44px;min-height:44px;padding:0;justify-content:center;gap:0;border:1px solid var(--dline);border-radius:9px;background:var(--dsurf);color:var(--dlabel)}
    .conversation-menu svg{width:20px;height:20px}
    .conversation-menu-label{display:none}
    /* Anchored to the RIGHT edge of the cell, not the left: the control is at the right end of the
       row, and a left-anchored 380px panel ran 76px off the screen (same defect the alerts panel
       has at this width — see portal_popover_geometry). */
    .conversation-panel{left:auto;right:0}
    .search-slot input{height:44px;padding:0 44px 0 12px;font-size:16px}
    .search-icon{display:none}

    .tabs{
      position:relative; z-index:21;
      min-height:56px; height:auto; padding:0 0 env(safe-area-inset-bottom);
      border:0; border-top:1px solid var(--dline); background:var(--ink);
      backdrop-filter:none; -webkit-backdrop-filter:none; overflow:hidden; gap:0;
    }
    .tab{
      flex:1 1 auto; min-width:44px; min-height:56px; height:56px; padding:4px 12px 3px;
      display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px;
      color:var(--dlabel); font-size:11px; line-height:1.1; letter-spacing:0;
    }
    .tab:hover{color:var(--dlabel)}
    .tab.is-on{color:var(--accent)}
    .tab.is-on::after{display:none}
    /* A colour change alone is a weak active state on a phone: it is the one piece of the shell
       that says where you are, and every platform convention for a bottom bar (Material 3, iOS,
       Vercel's 2026 bar) fills the active item rather than tinting its text. The pill sits inside
       the tab's own 56px box, so the tap target is unchanged.

       The pill spans the whole cell minus 2px, and the cell sizes to its own label (see `.tab`
       above): the labels are not equal width — `Invoices` is 46px at 11px while `Log` is 20px — so
       equal-width cells starve the long one. Measured at 390px before the fix: the oval was 32.8px
       wide inside a 48.8px cell and `Invoices` hung 6.6px past it on each side. `min-width:44px`
       on the cell keeps every tab a full tap target. */
    .tab.is-on::before{content:"";position:absolute;inset:6px 2px 5px;border-radius:12px;background:var(--glow);border:1px solid rgba(79,196,178,.24)}
    .tab-icon,.tab-label{position:relative;z-index:1}
    .tab-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px}
    .tab-icon svg{display:block;width:22px;height:22px;stroke:currentColor}
    .tab-badge{position:absolute;top:-5px;right:-8px;min-width:16px;height:16px;padding:0 4px;font-size:9px}
    .tab-label{display:block;font-size:11px;line-height:1.1}

    /* The row is five cells now, so the two sub-390px overrides it used to need are gone with the
       ninth tab. Five cells at the 44px tap floor need 220px and the row has never measured under
       320px: the floor is not in question at any phone width, so no width squeezes the cells
       (`flex:1 1 0` with `min-width:0`) and none needs the row to scroll (`overflow-x:auto` with
       `flex:0 0 auto`). Measured 2026-09-26 at 320px: 64px cells, the narrowest label (`Waiting`,
       39px at 11px) keeps 12.5px of air each side. */
    /* `More` is a cell of the row, and the destinations it opens are the sheet. A sheet row is
       a full-width row, not a 56px column, so every sheet row overrides the phone `.tab` rule. */
    .tab.tab-more{display:flex;background:transparent;border:0;font-family:inherit;cursor:pointer}
    .more-sheet{display:none}
    /* The sheet sits ABOVE the bar rather than covering it, so the bar stays visible and stays the
       way out of the sheet: a row cell tapped with the sheet open navigates, and the navigation
       closes the sheet. No scrim, because a scrim would dim a bar that still takes taps. */
    .more-sheet.is-open{
      display:block; position:fixed; z-index:30; left:0; right:0;
      /* 57px, not 56: the bar's own height plus the 1px line on its top edge, so the sheet sits ON
         that line rather than over it (measured at 390px: a 56px offset left the sheet's last row
         overlapping the bar's first pixel). */
      bottom:calc(57px + env(safe-area-inset-bottom)); top:auto;
      width:100%; max-height:min(60vh,420px); overflow:auto; border:0; border-top:1px solid var(--dline);
      border-radius:18px 18px 0 0; padding:10px 16px 12px; background:var(--dpanel);
      box-shadow:0 -18px 40px rgba(0,0,0,.45);
    }
    .more-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 0 4px}
    .more-head h2{margin:0;color:var(--cloud);font-size:15px}
    .more-close{width:44px;height:44px;margin-right:-10px;padding:0;border:0;background:transparent;color:var(--dsub);font-size:22px;line-height:1;cursor:pointer}
    .more-list{list-style:none;margin:0;padding:0}
    .more-sheet .tab{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:12px;width:100%;height:auto;min-height:52px;padding:0 4px;color:var(--dtext);font-size:14px;font-weight:650;letter-spacing:0}
    /* A row whose own `hidden` is true is not drawn here either. The rule above comes AFTER
       `.tab[hidden]{display:none}` at equal specificity, so without this line it wins inside the
       sheet: a tab the page has taken off the row — Website for a tenant without the site card,
       Team for a seat that is not the owner — is still listed, 358x52, and taking it opens nothing
       (measured 2026-09-27 at 390px: the drawn Team row left `portalCurrentTab` on "ask" and put
       `#team` in the URL with no panel under it). This is the same rule `.tab[hidden]` is for; the
       sheet just had to say it again. */
    .more-sheet .tab[hidden]{display:none}
    .more-sheet .tab + .tab{border-top:1px solid var(--dline-soft)}
    .more-sheet .tab-icon{width:22px;height:22px}
    .more-sheet .tab-label{font-size:14px;line-height:1.2}
    .more-sheet .tab.is-on{color:var(--accent)}
    .more-sheet .tab.is-on::before{display:none}

    form.composer{min-width:0;padding:8px 16px 10px;gap:4px}
    .field{min-width:0}

    .account-menu,.alerts-panel{
      position:fixed; z-index:30; left:0; right:0; bottom:0; top:auto;
      width:100%; max-height:78vh; overflow:auto; border:0; border-top:1px solid var(--dline);
      border-radius:18px 18px 0 0; padding:16px 16px calc(16px + env(safe-area-inset-bottom));
      background:var(--dpanel); box-shadow:0 -100vmax 0 100vmax rgba(0,0,0,.55);
    }
    .account-menu{right:auto}
    /* Owner report follow-up 2026-09-19: the sheet is the scroll container for a 49-item alert list,
       so it has to own its own touch. With pointer-events:none on the panel/list/rows a drag over
       the sheet passed straight through to the page behind — the list could not be scrolled and the
       rows were unreachable — while the scrim still looked interactive. The sheet keeps its own
       events now; the tab bar stays shielded because the sheet's box covers it. */
    .alerts-panel .alerts-head-actions,.alerts-panel .alert-action{pointer-events:auto}

    .panel-today,.panel-waiting,.panel-log,.panel-search,.panel-invoices,.panel-documents{padding:16px 16px 20px}
    .documents-controls{align-items:stretch;flex-direction:column}.documents-controls input{width:auto}
    .today-head,.waiting-head,.log-head,.search-head,.invoices-head{margin-bottom:18px}
    .today-head{gap:0}
    .today-kicker,.waiting-kicker,.log-kicker,.search-kicker,.invoices-kicker,.today-label,.waiting-label{
      font-size:11px; line-height:1.2; letter-spacing:.12em
    }
    .today-title,.waiting-title,.log-title,.search-title,.invoices-title{
      margin-top:6px; font-size:clamp(22px,6.4vw,28px); line-height:1.15; letter-spacing:-.03em
    }
    .today-grid{gap:12px}
    .today-card,.waiting-card,.log-day,.search-group,.invoice-row,.invoice-detail{padding:16px;border-radius:14px}
    .today-card h2{margin:6px 0 18px}
    .today-list,.waiting-list{gap:0}
    .today-row,.waiting-row{gap:12px;padding:12px 0}
    .today-row:first-child,.waiting-row:first-child{padding-top:0}
    .today-row:last-child,.waiting-row:last-child{padding-bottom:0}
    .today-row-title,.waiting-row-title,.search-row-title{font-size:15px;line-height:1.35}
    .today-row-detail,.waiting-row-detail,.search-row-detail{margin-top:4px;font-size:13px;line-height:1.45}
    .today-row-time,.waiting-row-time,.alert-time,.log-entry-at,.search-row-time{font-size:11px;line-height:1.25}
    .alert-title{font-size:13px;font-weight:650;color:var(--cloud)}
    .alert-body{font-size:12px;color:var(--dsub)}
    .alert-time{font:11px var(--mono);color:var(--dlabel)}
    .today-more,.search-ask{min-height:44px}
    .waiting-card{margin-top:12px}
    .waiting-list{margin-top:12px}
    .waiting-row-side{gap:8px}
    .log-day{margin-top:12px}
    .log-entry{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:8px;padding:12px 0}
    .log-entry:first-of-type{padding-top:0}
    .log-entry-at{grid-column:2;grid-row:1;display:block;text-align:right}
    .log-question{grid-column:1;grid-row:1;margin:0;font-size:15px;line-height:1.35}
    .log-answer,.log-meta,.log-produced{grid-column:1/-1}
    .log-answer{margin-top:8px}
    .log-more,.invoice-back,.invoice-claim button,.invoice-delete,.invoice-delete-confirm,.invoice-delete-cancel,.waiting-action,.alert-action,.alerts-mark,.search-close{min-height:44px}
    .log-produced li{font-size:11px}
    .search-group{margin-top:12px}
    .search-group-title{margin-bottom:8px}
    .search-row{position:relative;min-height:44px;padding:12px 0;padding-right:72px}
    .search-row:first-child{margin-top:0}
    .search-row-time{position:absolute;top:12px;right:0;margin:0;text-align:right}
    .invoice-row{min-height:44px;gap:12px}
    .invoice-meta{font-size:13px}
    .invoice-back{padding:0 11px}
    .copy{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px}
    .needs-strip{
      position:absolute; z-index:2; top:4px; left:16px; right:16px; width:auto; max-width:none;
      height:44px; min-height:44px; margin:0; padding:0 12px; border-radius:14px;
      font-size:13px; white-space:nowrap; overflow:visible
    }
    .needs-strip-link{min-height:44px;padding:0 4px}
    .panel-ask{position:relative}
    .panel-ask:has(.needs-strip:not([hidden])) main.thread{padding-top:64px}

    body.reading-old .app{
      grid-template-rows:auto minmax(0,1fr) auto;
      grid-template-areas:"side" "thread" "tabs";
    }
    body.reading-old .tabs{display:flex}
  }
  @media (max-width:340px){
    .biz{font-size:15px;letter-spacing:-.015em}
  }

  /* The Website panel's form. It sits ABOVE the touch floor, not with the other panel blocks at
     the foot of the sheet: its `input`/`textarea` rule sets 13px, and a control font rule after
     the floor demotes the floor (portal_mobile_check.mjs asserts the ordering, not just the
     outcome). The floor carries these two selectors as well, because specificity would let a
     13px rule win on a phone even from above it — which is the bug the floor exists for. */
/* ---------- website panel ---------- */
/* The Website tab's own furniture. It was drawn with the Teach panel's classes until the Teach
   panel was retired (2026-09-25); these rules are that block, under this panel's own names, so the
   form renders exactly as it did. Kept beside the other panels rather than shared: a rule two
   panels depend on is a rule one of them cannot be removed without. */
.website-field{display:grid;gap:5px;min-width:0;margin-bottom:10px}
.website-field span{color:var(--dlabel);font:10px var(--mono);letter-spacing:.04em;text-transform:uppercase}
.website-field input,.website-field textarea{width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dline);border-radius:7px;background:rgba(0,0,0,.16);color:var(--dtext);font:13px var(--sans)}
.website-field textarea{min-height:76px;resize:vertical;line-height:1.45}
.website-save-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px}
.website-save{min-height:44px;padding:0 12px;border:1px solid var(--dline);border-radius:7px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer;background:var(--accent);border-color:var(--accent);color:var(--imsg-out-ink);font-weight:700}
.website-save:disabled{cursor:wait;opacity:.55}
.website-status{min-height:1.4em;color:var(--accent);font:11px var(--mono)}
.website-status.is-failed{color:hsl(38 70% 68%)}

  .invoices-controls { display:flex; align-items:center; gap:14px; margin-bottom:20px; }
  .invoices-controls input { width:min(260px,100%); min-height:40px; padding:0 11px; border:1px solid var(--dline); border-radius:var(--r-card); background:var(--dsurf); color:var(--dsub); font:10px var(--mono); }
  .invoices-controls input::placeholder { color:var(--dlabel); }
  .invoice-action-status { min-height:1.2em; margin:0 0 12px; color:var(--dsub); font-size:13px; line-height:1.45; }
  .invoice-row-actions,.invoice-detail-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; flex-wrap:wrap; }
  .invoice-delete,.invoice-delete-confirm,.invoice-delete-cancel,.invoice-section-more,.invoice-clear-search { min-height:32px; padding:0 9px; border:1px solid var(--dline); border-radius:7px; background:transparent; color:var(--dsub); font:10px var(--mono); cursor:pointer; }
  .invoice-delete:hover,.invoice-delete-confirm:hover,.invoice-delete-cancel:hover,.invoice-section-more:hover,.invoice-clear-search:hover { border-color:var(--accent); color:var(--accent); }
  .invoice-delete-wrap,.invoice-delete-confirm { display:inline-flex; align-items:center; gap:6px; }
  .invoice-delete-prompt { color:var(--dsub); font:10px var(--mono); white-space:nowrap; }
  .invoice-search-empty { display:grid; gap:10px; }

  /* The compact 32px rule above sits LATER in the sheet than the 760px block that hands the older
     buttons their 44px, and a media query adds no specificity — so on a phone every control in that
     rule computed 32px. This block therefore comes after it; codex-review caught the override as a
     P2 and portal_invoices_check.mjs now asserts the measured height, not just the rule. */
  @media (max-width:760px){
    .invoice-delete,.invoice-delete-confirm,.invoice-delete-cancel,.invoice-section-more,.invoice-clear-search{min-height:44px}
  }

  /* ---------- the touch floor, deliberately last ----------
     A focused text control that computes under 16px makes a phone's browser zoom the page on tap;
     iOS Safari and Samsung Internet both do it. This is the whole set of controls in the portal, so
     no field can zoom the page — and it is the LAST rule in this sheet on purpose. It has the same
     specificity as the compact class-scoped rules above (`.search-slot input` at 11px,
     `.invoice-claim input/textarea` at 12px), so an equal-specificity rule written after it would
     win: the first attempt at this sat next to the search rules and left the invoice claim fields
     at 12px on touch, which codex-review caught as a P1. `portal_mobile_check.mjs` asserts the
     ordering, not just the outcome, so the floor cannot be demoted silently.

     `any-pointer:coarse` and `hover:none` are OR'd in with `pointer:coarse` because a phone running
     Request Desktop Website narrows the viewport away while staying a touch device, and a hybrid
     laptop reports a fine *primary* pointer next to its touch screen. Desktop with a mouse matches
     none of the three, so the 11px Geist Mono rail is untouched. */
  @media (pointer:coarse), (any-pointer:coarse), (hover:none){
    input, textarea, select, .search-slot input, .invoice-claim input, .invoice-claim textarea, .website-field input, .website-field textarea{font-size:16px}
  }
/* Invoice source sections. Keep the rows readable when the portal narrows to a phone. */
.invoice-sections { display: grid; gap: 2rem; }
.invoice-section { min-width: 0; }
.invoice-section-title { margin: 0 0 .75rem; }
.invoice-section-list { display: grid; gap: .65rem; }
.invoice-empty { color: var(--muted, #6b7280); margin: 0; }
/* The row's right column is a stack -- total, then status, then the controls -- so every amount ends
   at the same x. As inline flow the total's x moved with the width of the status word ("Awaiting
   payment" vs "Paid") and with whether the row carried a document link: measured 2026-09-20, 8 rows
   gave 8 distinct right edges with 149px of spread at 1440px. The side box's right edge is the same
   for every row, which is why the amounts line up at 390/768/1440 now. `min-width:0` on the row is
   what stops the section list's auto track from sizing to max-content (that overflowed the panel at
   768px, where the shell is already squeezed). */
.invoice-row, .invoice-row-main { min-width: 0; }
.invoice-row-side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; min-width: 6rem; text-align: right; }
/* .invoice-status carries a 12px top margin for the detail header; inside the row the stack's gap is
   the spacing, so it is dropped here rather than in the shared rule. */
.invoice-row-side .invoice-status { margin: 0; }
.invoice-total { font-variant-numeric: tabular-nums; }
.invoice-issuer, .invoice-counterparty, .invoice-meta { overflow-wrap: anywhere; }
.invoice-overdue { color: var(--danger, #b42318); font-size: .82rem; font-weight: 700; }
.invoice-document { white-space: nowrap; }
.invoice-detail-info { display: grid; gap: .35rem; }
.invoice-detail-info p { margin: 0; }
.invoice-detail-total { margin: 1rem 0 0; color: var(--cloud); font: 20px var(--mono); }
.invoice-subheading { margin: 0 0 .6rem; color: var(--dlabel); font: 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; }
.invoice-form-message { margin: 10px 0 0; color: var(--dsub); font-size: 13px; line-height: 1.45; }
.invoice-note { margin: 12px 0 0; color: var(--dsub); font-size: 13px; line-height: 1.5; white-space: pre-wrap; }
.invoice-paid { margin: 12px 0 0; color: var(--accent); font: 11px var(--mono); }
.invoice-history { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--dline-soft); }
.invoice-receipt { white-space: nowrap; }
.invoice-receipt-label { display: block; }
.invoice-payment-instructions { margin: 12px 0 0; color: var(--dsub); font-size: 13px; line-height: 1.5; white-space: pre-wrap; }
.invoice-line-description { color: var(--dsub); overflow-wrap: anywhere; }
.invoice-line-total { color: var(--cloud); font: 12px var(--mono); white-space: nowrap; }
.invoice-status-awaiting-payment,.invoice-status-paid { color: var(--accent); }
.invoice-status-overdue { color: #ffb18c; }
.invoice-status-void,.invoice-status-draft,.invoice-status-received { color: var(--dsub); }
.documents-list { display: grid; gap: .65rem; }
.documents-empty { display: grid; gap: .65rem; }
@media (max-width: 520px) {
  .invoice-row { align-items: flex-start; gap: .75rem; }
  .invoice-row-main, .invoice-row-side { min-width: 0; }
  .invoice-row-side { flex: 0 1 7rem; text-align: right; }
  .invoice-row-side .invoice-document { display: block; white-space: normal; }
  .invoice-detail-head { flex-wrap: wrap; gap: .5rem; }
  .invoice-delete-prompt { white-space: normal; }
  .invoice-detail-title { min-width: 0; overflow-wrap: anywhere; }
}

/* ---------- document deletion controls ----------
   Keep the confirmation inline so a phone never loses the document name while an action is pending. */
.documents-delete-all{display:flex;align-items:flex-start;gap:8px;margin-top:12px;flex-wrap:wrap}.documents-delete,.documents-delete-confirm,.documents-delete-cancel{min-height:44px;padding:0 10px;border:1px solid var(--dline);border-radius:7px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}.documents-delete:hover,.documents-delete-confirm:hover,.documents-delete-cancel:hover{border-color:var(--accent);color:var(--accent)}.documents-delete-wrap,.document-actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.documents-delete-prompt{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap;color:var(--dsub);font:10px var(--mono);line-height:1.4}.documents-delete-copy{display:block}.documents-status{min-height:1.3em;margin:12px 0 0;color:var(--dsub);font-size:13px;line-height:1.45}.document-actions{flex:0 0 auto;justify-content:flex-end}.document-actions .document-download{min-height:44px;display:inline-flex;align-items:center}.document-actions .documents-delete-wrap{justify-content:flex-end}
@media (max-width:520px){.document-row{gap:8px}.document-actions{max-width:48%;align-items:flex-end}.documents-delete-prompt{max-width:100%;}.documents-delete-prompt .documents-delete-copy{max-width:100%;overflow-wrap:anywhere}.documents-delete-wrap{justify-content:flex-end}.documents-delete,.documents-delete-confirm,.documents-delete-cancel{padding:0 8px}}

/* ---------- team roster ---------- */
.panel-team{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}
.panel-team[hidden]{display:none}
.team{width:min(100%,40rem);margin:0 auto;padding-bottom:32px;color:var(--dtext)}
.team-head{margin-bottom:24px}
.team-kicker{margin:0;color:var(--dlabel);font:11px var(--mono);letter-spacing:.16em;text-transform:uppercase}
.team-title-row{display:flex;align-items:center;gap:16px;margin-top:4px}
.team-title{margin:0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}
.team-summary{margin:8px 0 0;color:var(--dsub);font-size:14px;line-height:1.5}
.team-seat-count{display:inline-flex;align-items:center;min-height:32px;padding:0 12px;border:1px solid var(--dline);border-radius:99px;background:var(--dsurf-hi);color:var(--dtext);font:12px var(--mono);white-space:nowrap}
.team-invite{display:grid;grid-template-columns:minmax(0,1fr) 9rem auto;align-items:end;gap:16px;margin-bottom:16px;padding:16px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
.team-field{display:grid;gap:8px;min-width:0}
.team-label{color:var(--dlabel);font:12px var(--mono);letter-spacing:.08em;text-transform:uppercase}
.team input,.team select{width:100%;height:44px;min-height:44px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dpanel);color:var(--dtext);font-size:16px}
.team input{padding:0 16px;font-size:16px}
.team input::placeholder{color:var(--dlabel)}
.team select{appearance:none;padding:0 32px 0 16px;background-color:var(--dpanel);background-image:linear-gradient(45deg,transparent 50%,var(--dlabel) 50%),linear-gradient(135deg,var(--dlabel) 50%,transparent 50%);background-position:calc(100% - 16px) center,calc(100% - 8px) center;background-size:8px 8px,8px 8px;background-repeat:no-repeat;text-transform:capitalize}
.team select:disabled{opacity:.5;cursor:not-allowed}
.team-primary,.team-ghost{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 16px;border-radius:var(--r-card);font-size:14px;cursor:pointer}
.team-primary{border:1px solid var(--accent);background:var(--accent);color:#06231f;font-weight:700}
.team-primary:hover{background:var(--accent-deep);border-color:var(--accent-deep)}
.team-ghost{border:1px solid var(--dline);background:transparent;color:var(--dsub)}
.team-ghost:hover{border-color:var(--accent);color:var(--dtext);background:var(--dsurf-hi)}
.team-status{min-height:0;margin:0 0 16px;color:var(--dsub);font-size:14px;line-height:1.5}
.team-roster{display:grid;gap:16px}
.team-seat{padding:16px;border:1px solid var(--dline-soft);border-radius:var(--r-card);background:var(--dsurf);transition:background 120ms ease}
.team-seat:hover{background:var(--dsurf-hi)}
.team-seat-content{display:flex;align-items:center;justify-content:space-between;gap:24px}
.team-seat-identity{display:flex;align-items:center;gap:16px;min-width:0}
.team-monogram{display:grid;place-items:center;flex:0 0 36px;width:36px;height:36px;border-radius:50%;background:rgba(79,196,178,.16);color:var(--accent);font-size:16px;font-weight:700}
.team-seat-copy{min-width:0}
.team-seat-name-line{display:flex;align-items:center;flex-wrap:wrap;gap:8px}
.team-seat-name{color:var(--dtext);font-size:15px;line-height:1.4;overflow-wrap:anywhere}
.team-role-chip{display:inline-flex;align-items:center;min-height:24px;padding:0 8px;border:1px solid var(--dline);border-radius:99px;color:var(--dsub);font:12px var(--mono);text-transform:capitalize}
.team-seat-meta{margin:4px 0 0;color:var(--dlabel);font-size:12px;line-height:1.5}
.team-seat-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:0 0 auto}
.team-seat-actions select{width:9rem}
.team-seat-note{margin:0;color:var(--dlabel);font-size:12px;line-height:1.5;white-space:nowrap}
.team-empty{margin:16px 0 0;color:var(--dsub);font-size:14px}
.team-error-state{padding:24px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
.team-error-state .team-title{margin-top:4px}
.team-error{margin:16px 0 0;color:var(--dtext);font-size:16px;line-height:1.5}
.team input:focus-visible,.team select:focus-visible,.team button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.team-seat{transition:none}}
@media (max-width:760px){
  .panel-team{padding:16px 16px 20px}
}
@media (max-width:560px){
  .team-head{display:flex;flex-direction:column;gap:0}
  .team-title-row{display:contents}
  .team-title{order:1}
  .team-summary{order:2}
  .team-seat-count{order:3;align-self:flex-start;margin-top:16px}
  .team-invite{grid-template-columns:1fr;gap:16px}
  .team-field{gap:8px}
  .team-seat-content{align-items:stretch;flex-direction:column;gap:16px}
  .team-seat-actions{align-items:center;flex-wrap:wrap;width:100%}
  .team-seat-actions select{width:9rem}
  .team-seat-note{margin-left:auto;white-space:normal;text-align:right}
  .team-seat-actions .team-ghost{margin-left:auto}
}



/* The row's cells size to their own label, floored at 44px (the phone rule above), so nothing here
   narrows a tab: a rule that squeezed nine cells into a 360px row (`flex:1 1 0;min-width:0` at
   `min-width:360px and max-width:520px`) took the tap floor and the labels' air with it, and went
   when the row went to five cells. */

/* ---------- received bill payment controls ----------
   Keep pay and unpay beside deletion, while the later phone rule preserves a usable tap target. */
.invoice-pay-wrap{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.invoice-pay,.invoice-pay-confirm,.invoice-pay-cancel{min-height:32px;padding:0 9px;border:1px solid var(--dline);border-radius:7px;background:transparent;color:var(--dsub);font:10px var(--mono);cursor:pointer}
.invoice-pay-primary{border-color:var(--accent);background:var(--accent);color:var(--dpanel)}
.invoice-pay-secondary:hover,.invoice-pay-cancel:hover,.invoice-pay-primary:hover{border-color:var(--accent);color:var(--accent)}
.invoice-pay-primary:hover{background:transparent}
.invoice-pay-prompt{color:var(--dsub);font:10px var(--mono);white-space:nowrap}
.invoice-pay-status{margin:0;color:var(--accent);font:11px var(--mono)}
.invoice-pay-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:1rem}
.invoice-row-actions .invoice-pay{white-space:nowrap}
@media (max-width:760px){
  .invoice-pay,.invoice-pay-confirm,.invoice-pay-cancel{min-height:44px}
}

/* ---------- NOC dashboard ---------- */
.panel-noc{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:34px 44px 30px}
.panel-noc[hidden]{display:none}
.noc{width:min(1320px,100%);margin:0 auto;min-width:0}
.noc-head{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:24px;padding-bottom:20px;border-bottom:1px solid var(--dline-soft)}
.noc-head-copy{min-width:0}
.noc-kicker{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em;text-transform:uppercase}
.noc-title{margin:6px 0 0;color:var(--cloud);font-size:clamp(28px,3vw,40px);line-height:1.08;letter-spacing:-.045em}
.noc-subtitle{margin:9px 0 0;color:var(--dsub);font-size:13px;line-height:1.5;overflow-wrap:anywhere}
.noc-head-tools{display:flex;align-items:center;gap:12px;flex:0 0 auto}
.noc-read{color:var(--dlabel);font:10px var(--mono);letter-spacing:.06em;white-space:nowrap}
.noc-refresh{min-height:38px;padding:0 16px;border:1px solid var(--dline);border-radius:10px;background:transparent;color:var(--dsub);font:10px var(--mono);letter-spacing:.1em;text-transform:uppercase;cursor:pointer;transition:border-color .15s ease,color .15s ease,background .15s ease}
.noc-refresh:hover{border-color:var(--accent);color:var(--accent);background:var(--dsurf)}

.noc-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:14px;align-items:start}
.noc-card{grid-column:span 12;display:flex;min-width:0;flex-direction:column;padding:18px 18px 16px;border:1px solid var(--dline);border-radius:16px;background:var(--dsurf);box-shadow:inset 0 1px 0 hsla(198 22% 78% / .05);overflow:hidden;transition:border-color .15s ease,background .15s ease}
.noc-card:hover{border-color:hsla(198 22% 78% / .22);background:var(--dsurf-hi)}
.noc-card-head{display:flex;align-items:center;gap:10px;min-width:0}
.noc-card-title{margin:0;flex:0 0 auto;color:var(--dlabel);font:400 10px var(--mono);letter-spacing:.15em;text-transform:uppercase}
.noc-card-rule{flex:1 1 auto;height:1px;min-width:12px;background:var(--dline-soft)}
.noc-jump{flex:0 0 auto;color:var(--dlabel);font:10px var(--mono);letter-spacing:.08em;text-transform:uppercase;text-decoration:none;display:inline-flex;align-items:center;gap:5px;transition:color .15s ease}
.noc-jump::after{content:"\2192";font-size:11px;line-height:1}
.noc-jump:hover{color:var(--accent)}

.noc-copy{display:flex;min-width:0;flex-direction:column;gap:7px;margin-top:15px}
.noc-meta{margin:0;color:var(--dsub);font-size:12.5px;line-height:1.55;overflow-wrap:anywhere}
.noc-countline{margin:0;color:var(--dtext);font-size:13px;line-height:1.5;overflow-wrap:anywhere}
.noc-empty{margin:0;color:var(--dlabel);font-size:12.5px;line-height:1.55;overflow-wrap:anywhere}
.noc-headline{margin:0;color:var(--cloud);font:clamp(24px,2.2vw,34px)/1.15 var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.025em;overflow-wrap:anywhere}

.noc-status{display:flex;align-items:center;gap:9px;margin:0;color:var(--dtext);font:13px var(--mono);letter-spacing:.04em}
.noc-status-dot{width:9px;height:9px;border-radius:50%;background:var(--dlabel);flex:0 0 auto}
.noc-status.is-online .noc-status-dot{background:var(--accent);box-shadow:0 0 0 4px rgba(79,196,178,.13)}
.noc-status.is-offline .noc-status-dot{box-shadow:0 0 0 4px hsla(198 22% 78% / .07)}

.noc-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 10px}
.noc-stat{min-width:0}
.noc-stat strong{display:block;color:var(--cloud);font:clamp(22px,2vw,32px)/1.1 var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.03em;overflow-wrap:anywhere}
.noc-stat span{display:block;margin-top:5px;color:var(--dlabel);font:9.5px var(--mono);letter-spacing:.1em;text-transform:uppercase;overflow-wrap:anywhere}

.noc-list{display:grid;gap:0;margin:0;padding:0;list-style:none}
/* A row is `dot | title + detail | stamp` when it has a dot, and `title + detail | stamp` when it
   does not. The two shapes are separate tracks on purpose: with one three-track rule, an undotted
   row put its main block in the `auto` dot track and its stamp in the `1fr` middle track, so the Log
   and Team stamps floated mid-card instead of landing on the right edge and the Team content was
   sized by its own max-content width. */
.noc-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:10px;padding:11px 0;border-top:1px solid var(--dline-soft);min-width:0}
.noc-row.has-dot{grid-template-columns:auto minmax(0,1fr) auto}
.noc-row:first-child{border-top:0;padding-top:0}
.noc-row-dot{width:5px;height:5px;margin-top:7px;border-radius:50%;background:var(--dline);flex:0 0 auto}
.noc-row.is-attention .noc-row-dot{background:var(--accent)}
.noc-row-main{display:flex;min-width:0;flex-direction:column;gap:3px}
.noc-row-title{min-width:0;color:var(--dtext);font-size:13.5px;line-height:1.4;overflow-wrap:anywhere}
.noc-row-detail{min-width:0;color:var(--dsub);font-size:12px;line-height:1.5;overflow-wrap:anywhere}
.noc-row-time{flex:0 0 auto;padding-top:2px;color:var(--dlabel);font:10px var(--mono);letter-spacing:.06em;white-space:nowrap}

.noc-bar{height:5px;margin-top:2px;border-radius:999px;background:var(--dline);overflow:hidden}
.noc-bar-fill{display:block;height:100%;border-radius:inherit;background:var(--accent);transition:width .2s ease}
.noc-loading{margin:0;color:var(--dsub);font:12px var(--mono)}

@media (min-width:760px){
  .noc-card--agent,.noc-card--today,.noc-card--invoices,.noc-card--documents,.noc-card--website,.noc-card--team{grid-column:span 6}
}
@media (min-width:1280px){
  /* Explicit lines, not auto-placement: the DOM order is Agent, Today, Waiting, Invoices,
     Documents, Log (the order that reads right on a phone, where Waiting is the third card). Auto
     placement would put Agent+Today on row 1 and then wrap the 7-column Waiting, leaving six
     columns of dead space and pushing Invoices and Documents onto separate rows. */
  .noc-card--agent{grid-column:1/span 3;grid-row:1}
  .noc-card--today{grid-column:4/span 3;grid-row:1}
  .noc-card--invoices{grid-column:7/span 3;grid-row:1}
  .noc-card--documents{grid-column:10/span 3;grid-row:1}
  .noc-card--needs{grid-column:1/span 7;grid-row:2}
  .noc-card--log{grid-column:8/span 5;grid-row:2}
  .noc-card--website{grid-column:1/span 6;grid-row:3}
  .noc-card--team{grid-column:7/span 6;grid-row:3}
}
@media (max-width:759px){
  .panel-noc{padding:18px 16px 22px}
  .noc-head{flex-direction:column;align-items:flex-start;gap:14px;margin-bottom:18px;padding-bottom:16px}
  .noc-head-tools{width:100%;justify-content:space-between}
  .noc-card{padding:16px}
  .noc-bar-fill{transition:none}
}
@media (prefers-reduced-motion:reduce){
  .noc-card,.noc-jump,.noc-refresh{transition:none}
  .noc-bar-fill{transition:none}
}

.account-support{display:block;min-height:44px;padding-top:12px;color:var(--accent);font:11px var(--mono);text-decoration:none}.account-support:hover{text-decoration:underline}
.panel-data{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}.panel-data[hidden]{display:none}
.panel-apps{grid-area:thread;min-width:0;min-height:0;overflow:auto;padding:32px 44px 24px}.panel-apps[hidden]{display:none}.apps{width:min(920px,100%);margin:0 auto}.apps-head{margin-bottom:22px}.apps-kicker{margin:0;color:var(--dlabel);font:10px var(--mono);letter-spacing:.16em;text-transform:uppercase}.apps-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(25px,3vw,36px);line-height:1.1;letter-spacing:-.045em}.apps-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.apps-card{min-width:0;padding:18px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.apps-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.apps-card-title{margin:0;color:var(--dtext);font-size:16px;line-height:1.25;overflow-wrap:anywhere}.apps-pill{display:inline-flex;flex:0 0 auto;padding:5px 8px;border:1px solid rgba(79,196,178,.35);border-radius:999px;color:var(--accent);font:10px var(--mono);white-space:nowrap}.apps-proof,.apps-checked,.apps-note,.apps-empty{color:var(--dsub);font-size:12px;line-height:1.45}.apps-proof{margin:16px 0 0}.apps-footer{display:grid;gap:5px;margin-top:18px}.apps-checked,.apps-note{margin:0}.apps-note{color:var(--dlabel);font:11px var(--mono)}.apps-error-state{padding:24px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}.apps-error{margin:16px 0 0;color:var(--dtext);font-size:16px;line-height:1.5}.apps-empty{margin:0;font-size:14px}@media (max-width:760px){.panel-apps{padding:16px 16px 20px}.apps-grid{grid-template-columns:1fr;gap:12px}.apps-card{padding:16px}.apps-error-state{padding:16px}}
.data{display:block;width:min(100%,980px);margin:0 auto;padding-bottom:20px}.data-head{margin-bottom:24px}.data-kicker{margin:0;color:var(--dlabel);font:11px var(--mono);letter-spacing:.16em;text-transform:uppercase}.data-title{margin:5px 0 0;color:var(--cloud);font-size:clamp(24px,3vw,34px);line-height:1.12;letter-spacing:-.045em}.data-copy{color:var(--dsub);font-size:14px;line-height:1.5}
.data-card{padding:20px;border:1px solid var(--dline-soft);border-radius:var(--r-card);background:var(--dsurf);display:grid;gap:20px}.data-download{min-height:44px;padding:0 14px;border:1px solid var(--dline);border-radius:var(--r-card);background:transparent;color:var(--dsub);font:12px var(--mono);cursor:pointer}.data-download:hover{border-color:var(--accent);color:var(--dtext);background:var(--dsurf-hi)}.data-download:disabled{opacity:.5;cursor:not-allowed}
.data-list{display:grid;gap:10px;margin:0;padding:0;list-style:none;color:var(--dtext);font-size:13px;line-height:1.45}.data-list li{padding-left:18px;position:relative}.data-list li::before{content:"";position:absolute;left:0;top:.55em;width:6px;height:6px;border-radius:50%;background:var(--accent)}.data-note{margin:0;color:var(--dlabel);font:11px var(--mono);line-height:1.55}.data-status{min-height:17px;margin:0;color:var(--dsub);font:11px var(--mono);line-height:1.5}.data-status.is-error{color:#E2786E}
input[type="checkbox"],input[type="radio"]{accent-color:var(--accent)}
@media (max-width:760px){.panel-data{padding:16px 16px 20px}.data-card{padding:16px}}





/* --- invoice row: facts, money and actions are three children placed on one grid --- */
.invoice-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:start;column-gap:16px;row-gap:6px}
.invoice-row-main{grid-column:1;grid-row:1;min-width:0}
.invoice-row-side{grid-column:2;grid-row:1}
.invoice-row-actions{grid-column:2;grid-row:2}
/* --- phone: the facts stack in one aligned column, money stays on the right, and the
       actions take a full-width footer, so no row has a floating date or a dead corner --- */
@media (max-width:760px){
  .invoice-row{column-gap:14px;row-gap:10px}
  .invoice-row-main{display:block;line-height:1.32}
  .invoice-row-main>*{display:block;margin:0}
  .invoice-number{font-size:16px;letter-spacing:-.01em}
  .invoice-row-main .invoice-meta{margin-top:6px}
  .invoice-row-side{min-width:0;gap:5px}
  .invoice-row-side .invoice-status{margin:0}
  .invoice-row-actions{grid-column:1/-1;grid-row:2;justify-content:flex-start;padding-top:10px;border-top:1px solid var(--dline-soft)}
}


/* --- A panel the plan does not include is not a blank tab: the tab opens this notice, drawn by
       the gate in portal.js, so the tab is a real surface with a name and a way to the
       conversation. Same card furniture as the rest of the portal: one panel, one message. --- */
.panel-notice{width:min(560px,100%);margin:0 auto;padding:20px;border:1px solid var(--dline);border-radius:var(--r-card);background:var(--dsurf)}
.panel-notice[hidden]{display:none}
.panel-notice-title{margin:0;color:var(--cloud);font-size:18px;line-height:1.25;letter-spacing:-.025em}
.panel-notice-body{margin:10px 0 0;color:var(--dsub);font-size:14px;line-height:1.55}
.panel-notice-link{display:inline-flex;align-items:center;min-height:44px;margin-top:16px;padding:0 14px;border:1px solid var(--dline);border-radius:var(--r-card);color:var(--accent);font:11px var(--mono);text-decoration:none}
.panel-notice-link:hover{border-color:var(--accent);color:var(--cloud)}
@media (max-width:760px){.panel-notice{padding:16px}}
