/* ============================================================================
   ContractorCX — UI kit for the customer portal
   MKT-11 REV_02 · Direction 02 "CX Lock" · NEON/HUD · DARK ONLY
   ----------------------------------------------------------------------------
   Layer 1 (§1) is the token set, copied byte-for-byte from
   MKT-11_CXLock_Assets/06_code/contractorcx-tokens.css. Do not edit it here.
   Layer 2 (§3+) is the app primitives the marketing site never needed.

   THREE RULES THAT ARE ENGINEERING, NOT TASTE — see ContractorCX_Design_System.md
     1. --cx-blue and --cx-violet may NEVER encode two different meanings.
     2. A missed call is MAGENTA. Never red. Red is reserved for system faults.
     3. Glow is decoration. Remove every glow and the interface must still read.
   ============================================================================ */

/* ---------- 1. Tokens — from MKT-11, unchanged ---------- */
:root{
  --cx-grad: linear-gradient(102deg, #4490FD 0%, #9E71FD 48%, #E650A8 100%);
  --cx-blue:#4490FD; --cx-violet:#9E71FD; --cx-magenta:#E650A8; --cx-cyan:#60DDE7;

  --cx-void:#010617; --cx-panel:#050E1F; --cx-raised:#0E182A; --cx-inset:#0E182A;
  --cx-hairline:#1D2739; --cx-grid:#162033; --cx-line-active:#60697B;

  --cx-ink:#F1F3F8; --cx-ink-2:#B3BBC9; --cx-ink-3:#969FAF;
  --cx-on-brand:#010617; --cx-focus:#60DDE7;

  /* call status — dot + glyph only, NEVER row text */
  --cx-answered:#5FD780;
  --cx-missed:#CA338F;        /* brand magenta — NEVER red */
  --cx-voicemail:#AD8DFE;
  --cx-oncall:#A0ECFF;

  --cx-live:#8BDE9D; --cx-caution:#F7BA6C;
  --cx-fault:#FEB0A9;         /* SYSTEM FAULTS ONLY — not "missed", not "overdue" */
  --cx-info:#60DDE7;

  --cx-font-display:'Chakra Petch', ui-sans-serif, system-ui, sans-serif;
  --cx-font-mono:'JetBrains Mono', ui-monospace, monospace;
  --cx-font-text:'Archivo', ui-sans-serif, system-ui, sans-serif;
}

/* ---------- 2. Portal additions — every value measured, see the doc ---------- */
:root{
  /* status chip surfaces, from MKT-11 §4 */
  --cx-chip-answered-bg:#002C0F;  --cx-chip-answered-ink:#8BE69F;  --cx-chip-answered-line:#005725;
  --cx-chip-missed-bg:#44002C;    --cx-chip-missed-ink:#FEB4D9;    --cx-chip-missed-line:#800657;
  --cx-chip-voicemail-bg:#2A0B55; --cx-chip-voicemail-ink:#D1C4FE; --cx-chip-voicemail-line:#512B92;
  --cx-chip-oncall-bg:#002831;    --cx-chip-oncall-ink:#A0ECFF;    --cx-chip-oncall-line:#03515F;

  --cx-status-answered-ink:#75D78D;
  --cx-status-missed-ink:#FE98CD;
  --cx-status-voicemail-ink:#C3AFFE;
  --cx-status-oncall-ink:#A0ECFF;

  /* layout */
  --cx-sidebar-w:248px;
  --cx-topbar-h:60px;

  /* ── v14 lane MP — THE SETUP AGENT BUBBLE'S GEOMETRY, AS NAMED NUMBERS ────
     These four are tokens rather than literals because test/setup-agent/
     bubble-layout.test.js DOES ARITHMETIC WITH THEM. There is no browser on the
     build machine (Playwright is not installed here, checked 2026-09-27), so the
     "the bubble never covers the texting controls" check is a layout test over
     these declared values instead of a rendered screenshot — and that only works
     if the clearance is a number the test can read and compare against the
     composer's OWN declarations further down this file.

     --cx-composer-max-h is the tallest .cx-thread-composer can be: its own
     padding-top plus the textarea's max-height. If either of those changes and
     this does not, that test fails — which is the point of writing it down here
     rather than eyeballing a gap. */
  --cx-agent-size:56px;
  --cx-agent-size-sm:36px;
  --cx-composer-max-h:calc(8em + var(--cx-3));
  --cx-agent-clear:calc(var(--cx-composer-max-h) + var(--cx-3));
  --cx-r:12px;
  --cx-r-sm:8px;
  --cx-r-pill:999px;
  --cx-gut:clamp(16px, 3vw, 28px);

  /* spacing scale — 4px base. Use these, not arbitrary px. */
  --cx-1:4px; --cx-2:8px; --cx-3:12px; --cx-4:16px; --cx-5:20px;
  --cx-6:24px; --cx-8:32px; --cx-10:40px; --cx-12:48px; --cx-16:64px;

  --cx-shadow:0 8px 28px rgba(1,6,23,.55);
  --cx-dur:.18s;
}

/* ---------- 3. Base ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; color-scheme:dark}
body{
  margin:0; background:var(--cx-void); color:var(--cx-ink);
  font-family:var(--cx-font-text); font-size:15px; line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:var(--cx-font-display); font-weight:700; line-height:1.15; margin:0 0 .4em}
h1{font-size:1.75rem} h2{font-size:1.35rem} h3{font-size:1.1rem} h4{font-size:.98rem;font-weight:600}
p{margin:0 0 .9em} p:last-child{margin-bottom:0}
a{color:var(--cx-cyan)} a:hover{color:var(--cx-ink)}
small{font-size:.86rem}
code,kbd{font-family:var(--cx-font-mono); font-size:.9em}

/* Focus — one ring, everywhere, no exceptions. */
:focus-visible{outline:2px solid var(--cx-focus); outline-offset:2px; border-radius:4px}

.cx-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* HUD micro-label — the signature element. Cyan, mono, tracked. */
.cx-hud{
  font-family:var(--cx-font-mono); font-weight:500; font-size:.68rem;
  letter-spacing:.16em; text-transform:uppercase; color:var(--cx-cyan);
  display:block; margin:0 0 var(--cx-3);
}
.cx-num{font-family:var(--cx-font-mono); font-variant-numeric:tabular-nums}
.cx-muted{color:var(--cx-ink-3)}
.cx-dim{color:var(--cx-ink-2)}

/* ---------- 4. App shell ---------- */
.cx-app{display:grid; grid-template-columns:var(--cx-sidebar-w) 1fr; min-height:100vh}
.cx-sidebar{
  background:var(--cx-panel); border-right:1px solid var(--cx-hairline);
  padding:var(--cx-5) var(--cx-4); display:flex; flex-direction:column; gap:var(--cx-6);
}
.cx-sidebar .cx-brand{display:block; padding:var(--cx-2) 0 var(--cx-4)}
.cx-sidebar .cx-brand svg{width:150px; height:auto; display:block}
.cx-main{min-width:0; display:flex; flex-direction:column}
.cx-topbar{
  height:var(--cx-topbar-h); border-bottom:1px solid var(--cx-hairline);
  display:flex; align-items:center; gap:var(--cx-4); padding:0 var(--cx-gut); flex:0 0 auto;
}
.cx-content{padding:var(--cx-6) var(--cx-gut) var(--cx-12); flex:1 1 auto; min-width:0}
.cx-page-head{display:flex; align-items:flex-start; gap:var(--cx-4); margin-bottom:var(--cx-6); flex-wrap:wrap}
.cx-page-head h1{margin:0}
.cx-page-head .cx-actions{margin-left:auto; display:flex; gap:var(--cx-2)}

/* Mobile nav toggle — pure CSS (checkbox hack), no client-side JS. See the
   #cx-nav-toggle input in console-shared.js's renderPage for why the DOM
   order (input, then .cx-sidebar, then .cx-nav-backdrop, then .cx-main) is
   load-bearing: every rule below reaches its target with ~, which only
   matches a LATER sibling. Everything here is scoped inside
   max-width:880px, so above that width none of it applies and the sidebar
   is exactly what it was before this change — always visible, static,
   untouched. */
.cx-nav-toggle-btn{
  display:none; align-items:center; justify-content:center;
  width:36px; height:36px; border-radius:var(--cx-r-sm); cursor:pointer;
  flex:0 0 auto; -webkit-tap-highlight-color:transparent;
}
.cx-nav-toggle-btn:hover{background:var(--cx-raised)}
.cx-nav-toggle-icon,.cx-nav-toggle-icon::before,.cx-nav-toggle-icon::after{
  display:block; width:18px; height:2px; background:var(--cx-ink); border-radius:1px;
}
.cx-nav-toggle-icon{position:relative}
.cx-nav-toggle-icon::before,.cx-nav-toggle-icon::after{content:''; position:absolute; left:0}
.cx-nav-toggle-icon::before{top:-6px}
.cx-nav-toggle-icon::after{top:6px}
/* The checkbox itself is visually hidden (.cx-sr) but stays focusable, so
   reflect its :focus-visible state onto the visible label — otherwise a
   keyboard user tabbing to an invisible control gets no focus ring at all. */
#cx-nav-toggle:focus-visible ~ .cx-main .cx-topbar .cx-nav-toggle-btn{
  outline:2px solid var(--cx-focus); outline-offset:2px;
}

.cx-nav-close{
  display:none; align-self:flex-end; cursor:pointer;
  padding:var(--cx-1) var(--cx-2); border-radius:var(--cx-r-sm);
  color:var(--cx-ink-2); font-size:1.1rem; line-height:1;
}
.cx-nav-close:hover{background:var(--cx-raised); color:var(--cx-ink)}

.cx-nav-backdrop{display:none}

@media (max-width:880px){
  .cx-app{grid-template-columns:1fr}
  .cx-nav-toggle-btn{display:inline-flex}
  .cx-nav-close{display:inline-flex}
  .cx-sidebar{
    position:fixed; inset:0 auto 0 0; width:var(--cx-sidebar-w); z-index:60;
    transform:translateX(-100%); visibility:hidden;
    /* visibility flips to hidden only after the slide-out transition
       finishes, so it (a) doesn't cut the closing animation short and
       (b) takes the off-screen nav links out of the tab order and the
       accessibility tree while closed, rather than leaving them reachable
       but invisible. */
    transition:transform var(--cx-dur) ease, visibility 0s linear var(--cx-dur);
    /* Lane KR — the drawer is a fixed, full-height panel with no scroll
       rule; PROPOSAL_Console_Navigation_2026-09-26.md §D measured (from the
       stylesheet, not a handset) that the full nav ran to roughly 1,200px
       against an 844px viewport, clipping the last several entries —
       Billing among them — below the fold with no way to reach them. This
       is arithmetic, not a visual change: nothing above 880px is affected,
       and nothing here alters what the drawer looks like, only whether the
       part that doesn't fit can be reached. */
    overflow-y:auto;
  }
  #cx-nav-toggle:checked ~ .cx-sidebar{
    transform:none; visibility:visible;
    transition:transform var(--cx-dur) ease, visibility 0s linear 0s;
  }
  /* Tap-outside-to-close: a second label for the same checkbox, covering
     the viewport, with no JS involved — clicking/tapping it is a native
     label click that unchecks the control. */
  .cx-nav-backdrop{
    display:none; position:fixed; inset:0; z-index:55;
    background:rgba(1,6,23,.6);
  }
  #cx-nav-toggle:checked ~ .cx-nav-backdrop{display:block}
}

/* Account menu — the avatar in the topbar was a decorative aria-hidden span;
   people tap a top-right avatar expecting an account menu everywhere on the
   web, so it now has to be one (JT — "the J in the top right, people tend
   to think they can click on that too"). Built as a SECOND instance of the
   mobile nav toggle's pure-CSS checkbox-hack disclosure directly above, not
   a parallel mechanism: its own hidden checkbox (#cx-account-toggle, see
   console-shared.js), its own label-as-trigger, its own tap-outside
   backdrop label, focus reflected from the checkbox the same way. Unlike
   the nav toggle this one is NOT scoped to the max-width:880px breakpoint —
   the avatar sits in the topbar at every width, and "people think they can
   click that" is not a phone-only problem.

   Two independent checkboxes on one page means nothing stops a keyboard
   user checking both at once (Tab/Space to the hamburger, Tab/Space to the
   avatar — no tap-outside involved, so the "one tap dismisses the open
   overlay" argument below does not apply to keyboard use). That state is
   given an explicit, defended outcome rather than left to chance: the nav
   drawer's z-index (60 sidebar / 55 backdrop) is fixed HIGHER than this
   menu's (50 panel / 45 backdrop), so if both are ever open the nav drawer
   — the more disruptive, full-viewport affordance — visually wins; this
   menu sits behind its opaque backdrop rather than beside it.

   For pointer/touch use the two also never fight for a tap: the nav
   backdrop is position:fixed;inset:0, so while the drawer is open it
   already covers the avatar's position in the topbar — a tap there hits
   the NAV backdrop first and closes the drawer instead of opening this
   menu underneath it. This menu's own backdrop is fixed;inset:0 the same
   way (just transparent, since a small dropdown does not need to dim the
   whole page the way a full drawer does), so the reverse holds too: a tap
   on the hamburger while this menu is open closes the menu first. One tap
   always dismisses whichever full-viewport layer is on top before it can
   reach the control behind it; a second tap performs the action. */
.cx-account{position:relative; display:flex; align-items:center; margin-left:auto}
.cx-avatar-btn{
  display:inline-flex; align-items:center; justify-content:center;
  padding:2px; border-radius:50%; cursor:pointer; -webkit-tap-highlight-color:transparent;
}
.cx-avatar-btn:hover .cx-avatar{background:var(--cx-raised); border-color:var(--cx-ink-3)}
/* Same reasoning as #cx-nav-toggle:focus-visible above: the checkbox is
   visually hidden but stays focusable, so its focus ring is reflected onto
   the visible control. */
#cx-account-toggle:focus-visible ~ .cx-main .cx-topbar .cx-avatar-btn{
  outline:2px solid var(--cx-focus); outline-offset:2px;
}
.cx-account-backdrop{display:none; position:fixed; inset:0; z-index:45; background:transparent}
#cx-account-toggle:checked ~ .cx-main .cx-topbar .cx-account-backdrop{display:block}
.cx-account-menu{
  display:none; position:absolute; top:calc(100% + var(--cx-2)); right:0; z-index:50;
  min-width:220px; background:var(--cx-panel); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r); box-shadow:var(--cx-shadow); padding:var(--cx-3);
}
#cx-account-toggle:checked ~ .cx-main .cx-topbar .cx-account-menu{display:block}
.cx-account-menu-who{margin:0 0 var(--cx-3); color:var(--cx-ink-2); font-size:.86rem}
.cx-account-menu form{margin:0}
.cx-account-menu-item{
  display:block; width:100%; text-align:left; background:none; border:0;
  color:var(--cx-ink); font:inherit; font-weight:600; padding:var(--cx-2); margin:0;
  border-radius:var(--cx-r-sm); cursor:pointer;
}
.cx-account-menu-item:hover{background:var(--cx-raised)}

/* At 390px the topbar holds the hamburger, the tenant name and the avatar
   button, all in one row. The hamburger and avatar are fixed-size controls;
   the tenant name is the one element with no intrinsic size limit, so it is
   the one that gives way first — truncating with an ellipsis rather than
   pushing the avatar button off-screen or wrapping the topbar to a second
   row. Scoped to .cx-topbar specifically: .cx-hud is reused elsewhere
   (panel heads, stat tiles) where it never needs to truncate and should not
   start doing so here. */
.cx-topbar .cx-hud{min-width:0; flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* Sidebar nav */
.cx-nav{display:flex; flex-direction:column; gap:2px}
.cx-nav a{
  display:flex; align-items:center; gap:var(--cx-3);
  padding:var(--cx-2) var(--cx-3); border-radius:var(--cx-r-sm);
  color:var(--cx-ink-2); text-decoration:none; font-size:.94rem; font-weight:600;
  border-left:2px solid transparent;
}
.cx-nav a:hover{background:var(--cx-raised); color:var(--cx-ink)}
.cx-nav a[aria-current="page"]{
  background:var(--cx-raised); color:var(--cx-ink); border-left-color:var(--cx-cyan);
}
.cx-nav-group{margin-top:var(--cx-4)}
.cx-nav-group > .cx-hud{padding-left:var(--cx-3); margin-bottom:var(--cx-2)}

/* ---------- 5. Panels ---------- */
.cx-panel{
  background:var(--cx-panel); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r); padding:var(--cx-5); position:relative;
}
/* A <fieldset> gets `min-inline-size: min-content` from the UA stylesheet, and
   that rule is not overridden by an author `width` the way it is on every other
   element. So a fieldset.cx-panel wrapping a .cx-table-wrap inherits the
   table's own `min-width:640px` as its OWN minimum, grows past the viewport,
   and drags the scroll container out with it — the wrap never scrolls, the
   PAGE does. Measured live at 390px on /app/config/ring-list, 2026-09-07:
   documentElement.scrollWidth 702 against a 390 viewport; setting this one
   property in the live DOM took it to 375 and the table began scrolling inside
   its own wrap at 297px, which is the designed behaviour.
   Scoped to fieldset deliberately — .cx-panel on a <div> never had the bug. */
fieldset.cx-panel{min-width:0}
/* HUD corner brackets. Decorative — never the only indication of anything. */
.cx-panel--hud::before,.cx-panel--hud::after{
  content:''; position:absolute; width:12px; height:12px; pointer-events:none;
  border-color:var(--cx-cyan); border-style:solid; opacity:.75;
}
.cx-panel--hud::before{top:-1px;left:-1px;border-width:2px 0 0 2px;border-top-left-radius:var(--cx-r)}
.cx-panel--hud::after{bottom:-1px;right:-1px;border-width:0 2px 2px 0;border-bottom-right-radius:var(--cx-r)}
.cx-panel-head{display:flex; align-items:center; gap:var(--cx-3); margin-bottom:var(--cx-4)}
.cx-panel-head h2,.cx-panel-head h3{margin:0}
.cx-panel-head .cx-actions{margin-left:auto}

.cx-grid{display:grid; gap:var(--cx-4)}
.cx-grid--2{grid-template-columns:repeat(2,1fr)}
.cx-grid--3{grid-template-columns:repeat(3,1fr)}
.cx-grid--4{grid-template-columns:repeat(4,1fr)}
@media (max-width:1100px){.cx-grid--4{grid-template-columns:repeat(2,1fr)}}
@media (max-width:900px){.cx-grid--3,.cx-grid--2{grid-template-columns:1fr}}
@media (max-width:620px){.cx-grid--4{grid-template-columns:1fr}}

/* ---------- 6. Stat tile ---------- */
.cx-stat{background:var(--cx-panel); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r); padding:var(--cx-4) var(--cx-5)}
.cx-stat .cx-hud{margin-bottom:var(--cx-2)}
.cx-stat-value{font-family:var(--cx-font-display); font-weight:700; font-size:2rem;
  line-height:1; color:var(--cx-ink); font-variant-numeric:tabular-nums}
.cx-stat-sub{color:var(--cx-ink-3); font-size:.86rem; margin-top:var(--cx-2)}
/* Deltas carry a glyph as well as a colour — colour is never the only signal. */
.cx-delta{font-family:var(--cx-font-mono); font-size:.82rem}
.cx-delta--up{color:var(--cx-live)}
.cx-delta--down{color:var(--cx-caution)}
.cx-delta::before{margin-right:.35em}
.cx-delta--up::before{content:'\2191'}
.cx-delta--down::before{content:'\2193'}

/* ---------- 7. Call status — dot, chip, and the rules ---------- */
.cx-dot{
  display:inline-block; width:8px; height:8px; border-radius:50%;
  flex:0 0 auto; vertical-align:middle;
}
.cx-dot--answered {background:var(--cx-answered);  box-shadow:0 0 8px 0 var(--cx-answered)}
.cx-dot--missed   {background:var(--cx-missed);    box-shadow:0 0 8px 0 var(--cx-missed)}
.cx-dot--voicemail{background:var(--cx-voicemail); box-shadow:0 0 8px 0 var(--cx-voicemail)}
.cx-dot--oncall   {background:var(--cx-oncall);    box-shadow:0 0 8px 0 var(--cx-oncall)}

.cx-chip{
  display:inline-flex; align-items:center; gap:var(--cx-2);
  font-family:var(--cx-font-mono); font-size:.7rem; font-weight:500;
  letter-spacing:.1em; text-transform:uppercase;
  padding:3px var(--cx-2) 3px 7px; border-radius:var(--cx-r-pill);
  border:1px solid transparent; white-space:nowrap;
}
.cx-chip--answered {background:var(--cx-chip-answered-bg);  color:var(--cx-chip-answered-ink);  border-color:var(--cx-chip-answered-line)}
.cx-chip--missed   {background:var(--cx-chip-missed-bg);    color:var(--cx-chip-missed-ink);    border-color:var(--cx-chip-missed-line)}
.cx-chip--voicemail{background:var(--cx-chip-voicemail-bg); color:var(--cx-chip-voicemail-ink); border-color:var(--cx-chip-voicemail-line)}
.cx-chip--oncall   {background:var(--cx-chip-oncall-bg);    color:var(--cx-chip-oncall-ink);    border-color:var(--cx-chip-oncall-line)}
/* Neutral chip for counts and tags — carries no status meaning. */
.cx-chip--neutral  {background:var(--cx-raised); color:var(--cx-ink-2); border-color:var(--cx-hairline)}

/* Status label text, when a chip is too heavy. Text colour only, never row text. */
.cx-status{font-size:.88rem; display:inline-flex; align-items:center; gap:var(--cx-2)}
.cx-status--answered {color:var(--cx-status-answered-ink)}
.cx-status--missed   {color:var(--cx-status-missed-ink)}
.cx-status--voicemail{color:var(--cx-status-voicemail-ink)}
.cx-status--oncall   {color:var(--cx-status-oncall-ink)}

/* ---------- 8. Buttons ---------- */
.cx-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--cx-2);
  font-family:var(--cx-font-display); font-weight:700; font-size:.94rem;
  padding:9px var(--cx-4); border-radius:var(--cx-r-sm);
  border:1px solid transparent; cursor:pointer; text-decoration:none;
  background:transparent; color:var(--cx-ink); white-space:nowrap;
  transition:filter var(--cx-dur) ease, border-color var(--cx-dur) ease;
}
.cx-btn--primary{background:var(--cx-grad); color:var(--cx-on-brand)}
.cx-btn--primary:hover{color:var(--cx-on-brand); filter:brightness(1.12)}
.cx-btn--secondary{border-color:var(--cx-line-active); color:var(--cx-ink)}
.cx-btn--secondary:hover{border-color:var(--cx-cyan)}
.cx-btn--quiet{color:var(--cx-ink-2)}
.cx-btn--quiet:hover{background:var(--cx-raised); color:var(--cx-ink)}
/* Destructive: outlined, never a red fill. A red slab reads as "danger, everywhere". */
.cx-btn--danger{border-color:var(--cx-fault); color:var(--cx-fault)}
.cx-btn--danger:hover{background:var(--cx-raised)}
.cx-btn--sm{font-size:.84rem; padding:6px var(--cx-3)}
.cx-btn--lg{font-size:1.02rem; padding:12px var(--cx-6)}
.cx-btn[disabled],.cx-btn[aria-disabled="true"]{opacity:.45; cursor:not-allowed; filter:none}
.cx-btn--icon{padding:8px; width:34px; height:34px}
.cx-btn-row{display:flex; gap:var(--cx-2); flex-wrap:wrap}

/* ---------- 9. Forms ---------- */
.cx-field{margin-bottom:var(--cx-5)}
.cx-label{
  display:block; font-family:var(--cx-font-mono); font-size:.7rem;
  letter-spacing:.12em; text-transform:uppercase; color:var(--cx-cyan);
  margin-bottom:var(--cx-2);
}
.cx-label .cx-req{color:var(--cx-ink-3); text-transform:none; letter-spacing:0; margin-left:.4em}
.cx-input,.cx-select,.cx-textarea{
  width:100%; font-family:var(--cx-font-text); font-size:.95rem; color:var(--cx-ink);
  background:var(--cx-inset); border:1px solid var(--cx-line-active);
  border-radius:var(--cx-r-sm); padding:9px var(--cx-3);
}
.cx-textarea{min-height:110px; resize:vertical}
.cx-input::placeholder,.cx-textarea::placeholder{color:var(--cx-ink-3)}
.cx-input:focus,.cx-select:focus,.cx-textarea:focus{border-color:var(--cx-cyan)}
.cx-input[disabled],.cx-select[disabled],.cx-textarea[disabled]{opacity:.5; cursor:not-allowed}
.cx-help{font-size:.84rem; color:var(--cx-ink-3); margin-top:var(--cx-2)}
/* Invalid: colour AND a glyph AND text. Never colour alone. */
.cx-input[aria-invalid="true"],.cx-select[aria-invalid="true"],.cx-textarea[aria-invalid="true"]{
  border-color:var(--cx-fault);
}
.cx-error{font-size:.84rem; color:var(--cx-fault); margin-top:var(--cx-2);
  display:flex; align-items:flex-start; gap:var(--cx-2)}
.cx-error::before{content:'\26A0'; flex:0 0 auto}

.cx-check{display:grid; grid-template-columns:18px 1fr; gap:var(--cx-3); align-items:start}
.cx-check input{width:18px; height:18px; margin:2px 0 0; accent-color:var(--cx-cyan)}
.cx-check label{font-size:.92rem; color:var(--cx-ink-2); line-height:1.5}

.cx-switch{display:inline-flex; align-items:center; gap:var(--cx-3); cursor:pointer}
.cx-switch input{position:absolute; opacity:0; width:0; height:0}
.cx-switch-track{
  width:40px; height:22px; border-radius:var(--cx-r-pill);
  background:var(--cx-raised); border:1px solid var(--cx-line-active);
  position:relative; transition:background var(--cx-dur) ease, border-color var(--cx-dur) ease;
  flex:0 0 auto;
}
.cx-switch-track::after{
  content:''; position:absolute; top:2px; left:2px; width:16px; height:16px;
  border-radius:50%; background:var(--cx-ink-3); transition:transform var(--cx-dur) ease, background var(--cx-dur) ease;
}
.cx-switch input:checked + .cx-switch-track{background:var(--cx-chip-oncall-bg); border-color:var(--cx-cyan)}
.cx-switch input:checked + .cx-switch-track::after{transform:translateX(18px); background:var(--cx-cyan)}
.cx-switch input:focus-visible + .cx-switch-track{outline:2px solid var(--cx-focus); outline-offset:2px}
/* The switch's state must also be readable as text next to it — see the doc. */

/* ---------- 10. Tables ---------- */
.cx-table-wrap{overflow-x:auto; -webkit-overflow-scrolling:touch;
  border:1px solid var(--cx-hairline); border-radius:var(--cx-r); background:var(--cx-panel)}
.cx-table{border-collapse:collapse; width:100%; min-width:640px; font-size:.92rem}
.cx-table caption{text-align:left; padding:var(--cx-4) var(--cx-4) 0; color:var(--cx-ink-3); font-size:.86rem}
.cx-table thead th{
  font-family:var(--cx-font-mono); font-weight:500; font-size:.68rem;
  letter-spacing:.13em; text-transform:uppercase; color:var(--cx-cyan);
  text-align:left; padding:var(--cx-3) var(--cx-4);
  border-bottom:1px solid var(--cx-line-active); white-space:nowrap;
}
.cx-table tbody td,.cx-table tbody th{
  padding:var(--cx-3) var(--cx-4); border-bottom:1px solid var(--cx-hairline);
  color:var(--cx-ink); text-align:left; font-weight:400; vertical-align:middle;
}
.cx-table tbody tr:last-child td,.cx-table tbody tr:last-child th{border-bottom:0}
.cx-table tbody tr:hover{background:var(--cx-raised)}
.cx-table tbody tr[aria-selected="true"]{background:var(--cx-raised); box-shadow:inset 2px 0 0 var(--cx-cyan)}
.cx-table td.cx-num,.cx-table th.cx-num{font-family:var(--cx-font-mono); font-variant-numeric:tabular-nums}
.cx-table .cx-cell-2nd{color:var(--cx-ink-3); font-size:.84rem; display:block}
.cx-table td.cx-right{text-align:right}
/* Sortable header */
.cx-th-sort{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;
  display:inline-flex;align-items:center;gap:var(--cx-2);letter-spacing:inherit;text-transform:inherit}
.cx-th-sort::after{content:'\2195'; opacity:.5}
.cx-th-sort[aria-sort="ascending"]::after{content:'\2191'; opacity:1}
.cx-th-sort[aria-sort="descending"]::after{content:'\2193'; opacity:1}

/* ---------- 11. Empty state ---------- */
.cx-empty{text-align:center; padding:var(--cx-12) var(--cx-6); color:var(--cx-ink-2)}
.cx-empty h3{color:var(--cx-ink)}
.cx-empty p{max-width:44ch; margin:0 auto var(--cx-5)}
.cx-empty .cx-hud{display:inline-block; margin-bottom:var(--cx-4)}

/* ---------- 12. Pagination ---------- */
.cx-pager{display:flex; align-items:center; gap:var(--cx-2); padding:var(--cx-3) var(--cx-4);
  border-top:1px solid var(--cx-hairline); flex-wrap:wrap}
.cx-pager-info{color:var(--cx-ink-3); font-family:var(--cx-font-mono); font-size:.78rem;
  letter-spacing:.06em; margin-right:auto}

/* ---------- 13. Tabs ---------- */
.cx-tabs{display:flex; gap:var(--cx-5); border-bottom:1px solid var(--cx-hairline);
  margin-bottom:var(--cx-5); overflow-x:auto}
.cx-tab{
  background:none; border:0; cursor:pointer; white-space:nowrap;
  font-family:var(--cx-font-text); font-size:.94rem; font-weight:600; color:var(--cx-ink-3);
  padding:var(--cx-3) 0; border-bottom:2px solid transparent; margin-bottom:-1px;
}
.cx-tab:hover{color:var(--cx-ink-2)}
.cx-tab[aria-selected="true"]{color:var(--cx-ink); border-bottom-color:var(--cx-cyan)}

/* ---------- 14. Modal ---------- */
.cx-modal{border:0; padding:0; background:transparent; max-width:min(560px, calc(100vw - 32px))}
.cx-modal::backdrop{background:rgba(1,6,23,.72)}
.cx-modal-inner{
  background:var(--cx-panel); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r); box-shadow:var(--cx-shadow); padding:var(--cx-6);
}
.cx-modal-head{display:flex; align-items:flex-start; gap:var(--cx-4); margin-bottom:var(--cx-4)}
.cx-modal-head h2{margin:0}
.cx-modal-foot{display:flex; gap:var(--cx-2); justify-content:flex-end; margin-top:var(--cx-6)}

/* ---------- 15. Toast ---------- */
.cx-toasts{position:fixed; right:var(--cx-5); bottom:var(--cx-5); z-index:80;
  display:flex; flex-direction:column; gap:var(--cx-2); max-width:380px}
.cx-toast{
  background:var(--cx-raised); border:1px solid var(--cx-hairline);
  border-left:3px solid var(--cx-cyan); border-radius:var(--cx-r-sm);
  padding:var(--cx-3) var(--cx-4); box-shadow:var(--cx-shadow);
  display:flex; gap:var(--cx-3); align-items:flex-start; font-size:.92rem;
}
.cx-toast--ok{border-left-color:var(--cx-live)}
.cx-toast--warn{border-left-color:var(--cx-caution)}
.cx-toast--fault{border-left-color:var(--cx-fault)}
.cx-toast strong{display:block; color:var(--cx-ink)}
.cx-toast p{color:var(--cx-ink-2); margin:2px 0 0; font-size:.88rem}

/* ---------- 16. Banner / inline notice ---------- */
.cx-note{
  border:1px solid var(--cx-hairline); border-left:3px solid var(--cx-cyan);
  background:var(--cx-raised); border-radius:0 var(--cx-r-sm) var(--cx-r-sm) 0;
  padding:var(--cx-4); margin:var(--cx-4) 0;
}
.cx-note--warn{border-left-color:var(--cx-caution)}
.cx-note--fault{border-left-color:var(--cx-fault)}
.cx-note h4{margin-bottom:var(--cx-2)}
.cx-note p{color:var(--cx-ink-2); font-size:.92rem}

/* ---------- 17. Definition list — detail views ---------- */
.cx-dl{display:grid; grid-template-columns:minmax(120px,180px) 1fr; gap:var(--cx-3) var(--cx-5); margin:0}
.cx-dl dt{font-family:var(--cx-font-mono); font-size:.7rem; letter-spacing:.12em;
  text-transform:uppercase; color:var(--cx-cyan); padding-top:2px}
.cx-dl dd{margin:0; color:var(--cx-ink)}
@media (max-width:560px){.cx-dl{grid-template-columns:1fr; gap:var(--cx-1) 0}
  .cx-dl dd{margin-bottom:var(--cx-3)}}

/* ---------- 18. Avatar ---------- */
.cx-avatar{
  width:32px; height:32px; border-radius:50%; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--cx-raised); border:1px solid var(--cx-hairline);
  font-family:var(--cx-font-mono); font-size:.72rem; color:var(--cx-ink-2);
}

/* ---------- 19. Loading ---------- */
.cx-skel{
  background:var(--cx-raised); border-radius:var(--cx-r-sm);
  height:1em; animation:cx-pulse 1.4s ease-in-out infinite;
}
@keyframes cx-pulse{0%,100%{opacity:1}50%{opacity:.5}}
.cx-spinner{
  width:16px;height:16px;border-radius:50%;flex:0 0 auto;
  border:2px solid var(--cx-hairline); border-top-color:var(--cx-cyan);
  animation:cx-spin .8s linear infinite;
}
@keyframes cx-spin{to{transform:rotate(360deg)}}
/* A spinner is never the only indication that something is happening — pair it
   with text. Screen readers get nothing from a rotating border. */

/* ---------- 20. Motion ---------- */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
}

/* ---------- 21. Print ---------- */
@media print{
  body{background:#fff; color:#000}
  .cx-sidebar,.cx-topbar,.cx-toasts,.cx-nav-backdrop,.cx-agent-launcher{display:none}
  /* The thread prints as what it is — the last panel on the page. */
  [data-cx-agent="on"] [data-cx-agent-window]{position:static; display:block; max-height:none; width:auto; inset:auto}
  .cx-panel,.cx-table-wrap{border-color:#ccc}
}

/* ---------- 22. Conversation thread — blended calls + texts ----------
   LANE EU — one per-contact scroll (S5.3.1/S5.3.2 + LANE CB's timeline).
   Familiar texting geometry, our tokens: outbound bubbles carry the brand
   gradient, inbound sit on the raised panel tone, a call is an inline EVENT
   never dressed up as a bubble (rule: a missed call is a HUD event, not a
   message someone sent). 390px first — .cx-bubble's max-width is a
   percentage of the thread column, not a fixed px, so it never gets
   uncomfortably wide on a phone or absurdly narrow on desktop. */

.cx-thread{display:flex; flex-direction:column; gap:var(--cx-3); padding-bottom:var(--cx-4)}

.cx-thread-day{
  display:flex; align-items:center; justify-content:center;
  margin:var(--cx-4) 0 var(--cx-2);
}
.cx-thread-day span{
  font-family:var(--cx-font-mono); font-size:.68rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--cx-ink-3);
  background:var(--cx-raised); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r-pill); padding:4px var(--cx-3);
}

.cx-thread-older{text-align:center; margin:0 0 var(--cx-3)}
.cx-thread-latest{text-align:center; margin:var(--cx-3) 0 0}

/* A single bubble row — one message. Flex row so the bubble can hug either
   edge without a wrapper needing its own width. */
.cx-bubble-row{display:flex; flex-direction:column; max-width:82%}
.cx-bubble-row--out{align-self:flex-end; align-items:flex-end}
.cx-bubble-row--in{align-self:flex-start; align-items:flex-start}
@media (min-width:640px){.cx-bubble-row{max-width:62%}}

.cx-bubble{
  padding:var(--cx-2) var(--cx-3); font-size:.94rem; line-height:1.4;
  word-break:break-word; box-shadow:var(--cx-shadow);
}
.cx-bubble-row--out .cx-bubble{
  background:var(--cx-grad); color:var(--cx-on-brand);
  border-radius:var(--cx-r) var(--cx-r) 4px var(--cx-r);
}
.cx-bubble-row--in .cx-bubble{
  background:var(--cx-raised); color:var(--cx-ink); border:1px solid var(--cx-hairline);
  border-radius:var(--cx-r) var(--cx-r) var(--cx-r) 4px;
}
/* A failed outbound send stays legible as a bubble — never re-skinned as a
   call event — but the fault token marks it unmistakably (--cx-fault is
   reserved for a system fault, and an undelivered send IS one). */
.cx-bubble-row--out.cx-bubble-row--failed .cx-bubble{
  background:var(--cx-inset); color:var(--cx-fault); border:1px solid var(--cx-fault);
}

.cx-bubble-meta{
  display:flex; align-items:center; gap:var(--cx-2); margin-top:3px;
  font-family:var(--cx-font-mono); font-size:.7rem; color:var(--cx-ink-3);
}
.cx-bubble-meta--failed{color:var(--cx-fault)}

.cx-bubble-media{
  display:block; max-width:100%; border-radius:var(--cx-r-sm); margin-top:var(--cx-2);
}

/* A call, a voicemail, or a missed-call+textback pair — an EVENT, not a
   message, so it is centered and full-width-ish rather than pinned to an
   edge like a bubble. This is the one deliberate visual break from "every
   row is a bubble": a phone thread never renders a call as if someone typed
   it, and neither does this one. */
.cx-call-event{
  align-self:center; width:100%; max-width:420px;
  display:flex; flex-direction:column; gap:var(--cx-1);
  padding:var(--cx-2) var(--cx-4); border-radius:var(--cx-r-pill);
  background:var(--cx-raised); border:1px solid var(--cx-hairline);
  text-align:center; font-size:.86rem; color:var(--cx-ink-2);
}
.cx-call-event--missed{border-color:var(--cx-chip-missed-line); color:var(--cx-status-missed-ink)}
.cx-call-event--voicemail{border-color:var(--cx-chip-voicemail-line); color:var(--cx-status-voicemail-ink)}
.cx-call-event-head{display:flex; align-items:center; justify-content:center; gap:var(--cx-2)}
.cx-call-event .cx-num{color:inherit}

/* The "we texted them back" sub-line under a missed call — deliberately
   quieter than the event itself (this is a footnote to the miss, not a
   second event) and never magenta: the miss is the HUD-worthy fact, the
   textback is confirmation something was already done about it. */
.cx-textback-marker{
  display:flex; align-items:center; gap:var(--cx-1); justify-content:center;
  font-size:.76rem; color:var(--cx-ink-3);
}

.cx-voicemail-transcript{margin-top:var(--cx-1)}
.cx-voicemail-transcript summary{cursor:pointer; color:var(--cx-cyan); font-size:.8rem}
.cx-voicemail-transcript p{
  text-align:left; color:var(--cx-ink-2); font-size:.86rem; margin:var(--cx-2) 0 0;
  font-style:italic;
}

/* Pinned composer — rounded input, brand-gradient send, same geometry as a
   familiar texting app's compose bar, our tokens throughout. */
.cx-thread-composer{
  position:sticky; bottom:0; display:flex; gap:var(--cx-2); align-items:flex-end;
  padding:var(--cx-3) 0 0; margin-top:var(--cx-3);
  background:linear-gradient(var(--cx-void) 60%, var(--cx-void) 100%);
}
.cx-thread-composer textarea{
  flex:1 1 auto; border-radius:var(--cx-r-pill); resize:none;
  padding:11px var(--cx-4);
  /* Override .cx-textarea's 110px min-height — JT, 2026-09-08, from his own
     phone: on a 390px screen that ate roughly a third of it. rows="1" (the
     markup) already gives the natural one-line height; field-sizing lets it
     grow with a wrapped reply instead of scrolling, capped so a long paste
     can't push the thread off-screen. No client-side JS (console-shared.js's
     own posture) — this is CSS-only, same as PR #205's disclosure. */
  min-height:0; max-height:8em;
  field-sizing:content;
}
.cx-thread-composer button{border-radius:var(--cx-r-pill); flex:0 0 auto}
.cx-thread-composer[aria-disabled="true"] textarea{opacity:.5}

/* LANE desk53 — the thread's action row (Call, Mark handled), directly above
   the sticky composer. Left-aligned on purpose; src/webapp/timeline.js's
   renderThreadActions explains why the terminal action must not sit under
   Send. `align-items:flex-start` so a wrapped row keeps both controls on
   their own baseline rather than stretching the shorter one. */
.cx-thread-actions{display:flex; flex-wrap:wrap; gap:var(--cx-2); align-items:flex-start; margin:var(--cx-3) 0 0}
.cx-thread-actions form{margin:0}

.cx-stop-banner{border-left-color:var(--cx-fault); background:var(--cx-inset)}

/* ---------- 23. The Setup Agent bubble ----------
   v14 lane MP. JT, 2026-09-27: "i envisioned the agent as like a floating
   bubble in the bottom right that popped up and literally chatted with the
   user ... i just dont want the bubble to get in the way of like the texting
   button ... perhaps it should use the same UI as the inbox?"

   ⚠️ READ THIS BLOCK BEFORE CHANGING ANY RULE IN IT. Everything between here and
   the end of the file is arranged around one fact: THE DEFAULT PRESENTATION IS
   THE NO-JAVASCRIPT ONE. The window is an ordinary panel in the page's flow, the
   launcher and the close control are display:none, and the composer is a plain
   form that posts and redirects — exactly what #411 shipped. The bubble only
   exists inside [data-cx-agent="on"], an attribute nothing but
   src/webapp/static/setup-agent-bubble.js sets. Invert that gate and a browser
   with scripting off gets a button that does nothing instead of a working
   thread. */

/* The window in its ordinary, scriptless state needs nothing: .cx-panel already
   styles it. Only the two controls that would be inert without a script are
   hidden. */
.cx-agent-launcher,.cx-agent-close{display:none}

/* The conversation inside it. .cx-thread and .cx-bubble-* are the INBOX's own
   classes (section 22) and are not redeclared here — the Setup Agent thread looks
   like a customer thread because it is the same markup. The one difference is the
   scroll box: a customer thread is the whole page, this one lives in a window. */
.cx-setup-agent-thread{gap:var(--cx-2)}
.cx-agent-working .cx-bubble{color:var(--cx-ink-3); font-style:italic}

/* ── JAVASCRIPT ON ─────────────────────────────────────────────────────────── */

[data-cx-agent="on"] .cx-agent-launcher{
  display:inline-flex; align-items:center; justify-content:center; position:relative;
  width:var(--cx-agent-size-sm); height:var(--cx-agent-size-sm);
  flex:0 0 auto; padding:0; cursor:pointer;
  border:1px solid var(--cx-hairline); border-radius:var(--cx-r-pill);
  background:var(--cx-raised); color:var(--cx-ink);
  -webkit-tap-highlight-color:transparent;
}
[data-cx-agent="on"] .cx-agent-launcher:hover{color:var(--cx-on-brand); background:var(--cx-grad); border-color:transparent}
[data-cx-agent="on"] .cx-agent-launcher-glyph{display:flex; align-items:center; justify-content:center}
[data-cx-agent="on"] .cx-agent-launcher-glyph svg{width:18px; height:18px; display:block}

/* The dot and its count are ONE element: a pill with a number in it, so there is
   never a dot that disagrees with a count. --cx-cyan, not --cx-fault: something
   waiting for him is not a system fault (the fault token is reserved, see
   section 22's failed-bubble comment). */
[data-cx-agent="on"] .cx-agent-launcher-count{
  position:absolute; top:-5px; right:-5px; min-width:17px; height:17px;
  display:flex; align-items:center; justify-content:center; padding:0 4px;
  border-radius:var(--cx-r-pill); font-size:.62rem; font-weight:700; line-height:1;
  background:var(--cx-cyan); color:var(--cx-on-brand);
}

[data-cx-agent="on"] .cx-agent-close{
  display:inline-flex; align-items:center; justify-content:center;
  margin-left:auto; width:28px; height:28px; flex:0 0 auto; padding:0; cursor:pointer;
  border:0; border-radius:var(--cx-r-sm); background:transparent;
  color:var(--cx-ink-2); font-size:1.2rem; line-height:1;
}
[data-cx-agent="on"] .cx-agent-close:hover{background:var(--cx-raised); color:var(--cx-ink)}

/* Closed is the default once the script is in charge — the bubble is what is on
   screen, not the window. */
[data-cx-agent="on"] [data-cx-agent-window]{display:none}
[data-cx-agent="on"] [data-cx-agent-window][data-cx-agent-open="true"]{display:flex; flex-direction:column}

/* The body scrolls, the head and the composer do not. .cx-thread-composer is
   already position:sticky;bottom:0 (section 22), which inside this scroll box
   pins it to the bottom of the window exactly as it pins to the bottom of a
   conversation page. */
[data-cx-agent="on"] .cx-agent-body{overflow-y:auto; min-height:0; flex:1 1 auto}
[data-cx-agent="on"] [data-cx-agent-window] .cx-panel-head{flex:0 0 auto}
[data-cx-agent="on"] [data-cx-agent-window] .cx-thread-composer{
  /* Section 22 fades the composer into the page background. Inside the window the
     background behind it is the panel, not the void. */
  background:linear-gradient(var(--cx-panel) 60%, var(--cx-panel) 100%);
}

/* ── WIDE SCREENS: THE FLOATING BUBBLE ─────────────────────────────────────── */

@media (min-width:641px){
  /* The launcher is rendered inside .cx-topbar and LEAVES the header's flow here.
     One button for both breakpoints (see renderSetupAgentLauncher's comment) —
     position:fixed is what lets the same node be a header icon on a phone and a
     floating bubble on a desktop without a second copy to keep in step. */
  [data-cx-agent="on"] .cx-agent-launcher{
    position:fixed; right:var(--cx-4); bottom:var(--cx-4); z-index:64;
    width:var(--cx-agent-size); height:var(--cx-agent-size);
    background:var(--cx-grad); color:var(--cx-on-brand); border-color:transparent;
    box-shadow:var(--cx-shadow);
  }
  [data-cx-agent="on"] .cx-agent-launcher-glyph svg{width:24px; height:24px}

  [data-cx-agent="on"] [data-cx-agent-window][data-cx-agent-open="true"]{
    position:fixed; right:var(--cx-4); z-index:70;
    bottom:calc(var(--cx-4) + var(--cx-agent-size) + var(--cx-2));
    width:min(400px, calc(100vw - 2 * var(--cx-4)));
    max-height:min(560px, calc(100vh - var(--cx-4) - var(--cx-agent-size) - var(--cx-2) - var(--cx-4)));
    padding:var(--cx-4); box-shadow:var(--cx-shadow);
  }

  /* ⚠️ THE ONE RULE JT ASKED FOR BY NAME: "i just dont want the bubble to get in
     the way of like the texting button." On a screen that renders a composer —
     src/webapp/console-shared.js's pageHasComposer stamps .cx-app with
     data-cx-composer — the bubble is lifted clear of the composer's tallest
     possible box (--cx-composer-max-h) instead of sitting on top of the
     right-aligned Send button. The clearance is a declared token so
     test/setup-agent/bubble-layout.test.js can check the arithmetic without a
     browser. */
  [data-cx-agent="on"] .cx-app[data-cx-composer="true"] .cx-agent-launcher{
    bottom:var(--cx-agent-clear);
  }
  [data-cx-agent="on"] .cx-app[data-cx-composer="true"] [data-cx-agent-window][data-cx-agent-open="true"]{
    bottom:calc(var(--cx-agent-clear) + var(--cx-agent-size) + var(--cx-2));
    max-height:min(560px, calc(100vh - var(--cx-agent-clear) - var(--cx-agent-size) - var(--cx-2) - var(--cx-4)));
  }
}

/* A SHORT WIDE VIEWPORT — a laptop with a keyboard open, a half-height window.
   Lifting the bubble 144px off the bottom on a 560px-tall screen would put it in
   the middle of the thread, so on a composer screen it goes TOP-right instead,
   below the topbar. This is the "(or top-right if the viewport is too short)"
   half of this lane's brief, and it only applies where the clearance applies. */
@media (min-width:641px) and (max-height:640px){
  [data-cx-agent="on"] .cx-app[data-cx-composer="true"] .cx-agent-launcher{
    top:calc(var(--cx-topbar-h) + var(--cx-3)); bottom:auto;
  }
  [data-cx-agent="on"] .cx-app[data-cx-composer="true"] [data-cx-agent-window][data-cx-agent-open="true"]{
    top:calc(var(--cx-topbar-h) + var(--cx-3) + var(--cx-agent-size) + var(--cx-2));
    bottom:var(--cx-4); max-height:none;
  }
}

/* ── PHONES: AN ICON IN THE HEADER, AND A FULL-SCREEN SHEET ────────────────── */

/* JT: "perhaps this should work differently in the mobile version." It does, and
   this is the half that guarantees the composer is never covered on a phone:
   there is NO fixed bubble at this width at all. The launcher stays exactly where
   the markup puts it — in .cx-topbar, beside the account avatar — so nothing
   floats over the thread, over the textarea, or over Send.

   The script also never auto-opens at this width (it checks the same 640px
   breakpoint): a sheet that covers the whole screen the moment he lands on a page
   is an interruption, not a notification. The count on the header icon is what
   tells him, and tapping it is what opens it. */
@media (max-width:640px){
  [data-cx-agent="on"] [data-cx-agent-window][data-cx-agent-open="true"]{
    position:fixed; inset:0; z-index:70;
    width:100%; max-width:none; height:100%; max-height:none;
    border:0; border-radius:0; padding:var(--cx-4);
  }
}
