/* ---------------------------------------------------------------------------
   Colour, in two themes.

   Stefan's note after seeing Andrew's FBOM: the black background is hard on
   the eyes for a full working day. So light is now a real theme rather than an
   inversion, and dark has been lifted off pure black -- #0b0f15 became #131820,
   which reads as a dark room rather than a switched-off screen.

   The rule that makes both work: NOTHING declares a colour outside :root. Every
   surface, hairline and hover used to be a literal var(--lift-2), which
   is a "lift" in dark and invisible-on-white in light. Those are now --lift-N
   tokens that go the other way round under light. Same for shadows, which have
   to be far softer on paper than on black or every card looks stamped on.
--------------------------------------------------------------------------- */
:root {
  color-scheme: dark;
  --bg: #131820;
  --panel: #1a212b;
  --panel-2: #212a36;
  --sidebar: #161d26;
  --line: var(--lift-3);
  /* Stronger than --line, as it is in the light theme (.10 / .20). It was
     var(--lift-1), .025 -- FAINTER than the line it is meant to outrank --
     so in dark mode, the default, every secondary button, menu and modal
     edge drawn with it all but vanished. 10-02-2026. */
  --line-strong: rgba(255,255,255,.16);
  --text: #f1f5f9;
  --muted: #9aa8bc;
  /* AA on the dark panels: 5.3:1 at the worst of them. Was #6b7a90
     (3.8) -- helper text, dates and card subtitles all failed. */
  --subtle: #8494ab;
  /* The accent. "Blue" by name because the app was built blue; the value
     comes from the colour a person picked in Appearance (data-accent on
     <html>), and every tint in the sheet is mixed from it. */
  --blue: var(--accent-ink-dark, #4387f5);
  /* Blue as a SOLID behind white text, which is a different job from blue as
     ink. --blue is tuned to read on a dark ground; white on it measures
     3.49:1, under the 4.5:1 that button-sized text needs, so every filled
     button in the app was failing in dark mode. This one passes in both
     themes (4.88:1) and still reads as a blue button against the panel. Use
     --blue for text and borders, this for fills. */
  --blue-solid: var(--accent-solid, #2f6fd0);
  /* The lighter end of a filled gradient (your own bubbles, the brand tiles):
     mostly the solid, a little of the ink, so white text stays readable on
     every accent. */
  --accent-fill-2: color-mix(in srgb, var(--blue-solid) 72%, var(--blue));
  --select-arrow: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%228%22%20viewBox%3D%220%200%2012%208%22%3E%3Cpath%20d%3D%22M1%201.5%206%206.5%2011%201.5%22%20fill%3D%22none%22%20stroke%3D%22%239a958e%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  --violet: #8b5cf6;
  /* THE PRIMARY FILL. Anchored on --blue-solid, which is the accent tuned to
     carry white text, and running to that accent's partner. Both ends are
     measured for white; the solid stays the floor. */
  --btn-grad: linear-gradient(135deg, var(--blue-solid),
                              var(--accent-grad, #6d45c9));
  --green: #35c98f;
  --amber: #f5ae4e;
  --red: #fb6472;
  --on-accent: #fff;
  /* Lifts: a wash over whatever is behind. In dark they lighten, in light they
     darken, which is why each theme sets its own.

     These were written as `--lift-1: var(--lift-1)` -- a property defined as
     itself, which is invalid and resolves to nothing. So in dark mode, the
     default and the one Stefan uses, every hover built on a lift did nothing at
     all: the nav, secondary buttons, table rows, all of it. The app had no
     hover feedback in its own default theme and the CSS looked correct. */
  --lift-1: rgba(255,255,255,.025);
  --lift-2: rgba(255,255,255,.055);
  --lift-3: rgba(255,255,255,.09);
  --lift-4: rgba(255,255,255,.13);
  --sink-1: rgba(0,0,0,.18);
  --field: #0f151d;
  --radius: 14px;
  --shadow: 0 18px 60px rgba(0,0,0,.28);
  --shadow-sm: 0 2px 10px rgba(0,0,0,.22);
  --shadow-lg: 0 30px 80px rgba(0,0,0,.45);
  --tint-blue: color-mix(in srgb, var(--blue) 16%, transparent);
  /* Blue text on a blue tint: the accent, pushed away from the tint until
     it passes AA (it read 3.9:1 before). Tracks whatever accent is set. */
  --blue-on-tint: color-mix(in srgb, var(--blue) 88%, #fff);
  --blue-line: color-mix(in srgb, var(--blue) 45%, transparent);
  --tint-green: rgba(53,201,143,.16);
  --tint-amber: rgba(245,174,78,.18);
  --tint-red: rgba(251,100,114,.16);
  --tint-violet: rgba(139,92,246,.16);
  /* Client status has to be readable at a glance and TELLABLE APART. Freeze and
     Final 30 Days were both var(--ink-warn) -- the same colour for "paused" and "ending
     soon", which are opposite problems. And every one of these was a pale tint
     picked for a dark ground, so in light mode they sat on white and vanished.
     Each status now has its own token with a value chosen for each theme.
     The reds are a ramp on purpose, worst last: freeze is a pause, inactive is
     drift, declined is money failing to arrive. */
  --st-active:   var(--ink-good);
  --st-final30:  var(--ink-warn);
  --st-overdue:  #ffb457;
  /* An overdue renewal should be zero. When it is not, it is a failure state
     and it gets alarm treatment rather than a tint -- a solid fill that cannot
     be scrolled past. Dark mode inverts: white on red fails there (3.9:1), so
     the fill goes bright and the text goes dark instead. */
  --alarm-bg:  #ff5c5c;
  --alarm-ink: #1a0d0d;
  /* Money past due alarms beside the renewal one, so it is a different hue on
     purpose: two red buttons side by side read as one thing. */
  --money-alarm: #d1610f;
  --st-freeze:   #ff9d9d;
  --st-inactive: #f2606f;
  --st-declined: #fa7a90;
  --st-inperson: #a9c9fb;
  --st-finished: #9a9a92;

  /* Semantic ink. Seventy-three hard-coded pale hexes were scattered through the
     JS -- var(--ink-good) for good, var(--ink-warn) for warning, var(--ink-bad) for bad -- every one of
     them chosen to sit on a dark ground. In light mode they are pale text on
     white and effectively invisible, which is why "Paid up" and "renews in 67
     days" could not be read. Same names, a value per theme, one place to change. */
  --ink-good: #35c98f;
  --ink-warn: #f5ae4e;
  /* The step between good and warn on the re-sign runway: a client 45-60
     days out is neither. Defined per theme like the rest of the inks. */
  --ink-yellow: #f2d05a;
  /* A star has to look like a star on white as well as on black; --ink-yellow
     is a readable-text yellow (#7a5f00 in light), which is brown. */
  --star-gold: #f5c518;
  --tint-yellow: rgba(242,208,90,.18);
  --ink-bad:  #f2807c;
  --ink-info: #a9c9fb;
  --ink-violet: #c4b5fd;

  /* SWATCH colours, for a filled shape rather than text. Same reason
     --star-gold exists a few lines up: the ink values are tuned to be readable
     AS TEXT on white, which makes --ink-warn #8a5a0c in light mode -- brown. A
     brown stoplight dot beside a dark red one cannot be told apart at a
     glance, and telling them apart at a glance is the whole job. On a dark
     ground the inks already are swatch colours, so these match them there. */
  --sw-green: #35c98f;
  --sw-amber: #f5ae4e;
  --sw-red:   #f2807c;

  /* Older names, still used in a dozen rules. They were only ever reached
     through a hardcoded fallback -- var(--warn, #d9a441) -- which meant those
     rules used a dark-mode amber in light mode and never changed with the
     theme. Aliased once here, so they follow whatever the theme sets and there
     is no second set of values to keep in step. */
  --good: var(--ink-good);
  --warn: var(--ink-warn);
  --bad: var(--ink-bad);
  --accent-soft: var(--tint-blue);

}

/* The accent colours, the way ClickUp offers them. Three values each: ink
   on a dark ground, ink on a light ground, and the solid that sits behind
   white text (checked at 4.5:1 or better). Blue is the house default and
   needs no stamp. */
/* --accent-grad is each accent's OWN partner for the button gradient: one
   step round the wheel, at about the same depth, so the fill reads as a sheen
   rather than two colours fighting. The gradient used to end in a hardcoded
   violet, which is right for blue and wrong for the other eight -- mint went
   green to purple. White text was measured on both ends of every one of
   these; see the table in scratch//button_contrast. */
:root[data-accent="black"]  { --accent-ink-dark: #e6e8ee; --accent-ink-light: #1b1b18; --accent-solid: #2a2d34; --accent-grad: #4b5160; }
:root[data-accent="purple"] { --accent-ink-dark: #a78bfa; --accent-ink-light: #6d28d9; --accent-solid: #6d28d9; --accent-grad: #4f46e5; }
:root[data-accent="violet"] { --accent-ink-dark: #c084fc; --accent-ink-light: #7e22ce; --accent-solid: #7e22ce; --accent-grad: #be185d; }
:root[data-accent="indigo"] { --accent-ink-dark: #818cf8; --accent-ink-light: #4338ca; --accent-solid: #4f46e5; --accent-grad: #7e22ce; }
:root[data-accent="pink"]   { --accent-ink-dark: #f472b6; --accent-ink-light: #be185d; --accent-solid: #db2777; --accent-grad: #9d174d; }
:root[data-accent="orange"] { --accent-ink-dark: #fb923c; --accent-ink-light: #c2410c; --accent-solid: #c2410c; --accent-grad: #9a3412; }
:root[data-accent="teal"]   { --accent-ink-dark: #2dd4bf; --accent-ink-light: #0f766e; --accent-solid: #0f766e; --accent-grad: #115e59; }
:root[data-accent="bronze"] { --accent-ink-dark: #d4a373; --accent-ink-light: #8a5a2b; --accent-solid: #8a5a2b; --accent-grad: #7c4a1e; }
:root[data-accent="mint"]   { --accent-ink-dark: #34d399; --accent-ink-light: #047857; --accent-solid: #047857; --accent-grad: #0f766e; }
/* Black's active tiles and hovers are mixed from near-white ink in the dark
   theme, which reads as grey -- right for a monochrome look. */

/* Light: warm paper, not grey. Taken off Andrew's screenshots -- an off-white
   page, white cards, and hairlines dark enough to actually separate them. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f7f6f3;
  --panel: #ffffff;
  --panel-2: #faf9f7;
  --sidebar: #fbfaf8;
  --line: rgba(24,24,20,.10);
  --line-strong: rgba(24,24,20,.20);
  --text: #1b1b18;
  --muted: #5f5f58;
  /* AA on every background this app actually paints: 4.98:1 at the
     worst of them. Was #8b8b82 (3.2) -- audit, 09-22-2026. */
  --subtle: #6b6b62;
  --blue: var(--accent-ink-light, #2f6fd0);
  --select-arrow: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%228%22%20viewBox%3D%220%200%2012%208%22%3E%3Cpath%20d%3D%22M1%201.5%206%206.5%2011%201.5%22%20fill%3D%22none%22%20stroke%3D%22%236f6b64%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  --violet: #6d45c9;
  --green: #12805a;
  --amber: #9a6410;
  --red: #c2382f;
  --on-accent: #fff;
  --lift-1: rgba(24,24,20,.022);
  --lift-2: rgba(24,24,20,.045);
  --lift-3: rgba(24,24,20,.07);
  --lift-4: rgba(24,24,20,.10);
  --sink-1: rgba(24,24,20,.05);
  --field: #ffffff;
  --shadow: 0 10px 30px rgba(24,24,20,.08);
  --shadow-sm: 0 1px 3px rgba(24,24,20,.07);
  --shadow-lg: 0 20px 50px rgba(24,24,20,.13);
  --tint-blue: color-mix(in srgb, var(--blue) 16%, transparent);
  --blue-on-tint: color-mix(in srgb, var(--blue) 76%, #000);
  --blue-line: color-mix(in srgb, var(--blue) 40%, transparent);
  --tint-green: rgba(18,128,90,.17);
  --tint-amber: rgba(154,100,16,.18);
  --tint-red: rgba(194,56,47,.18);
  --tint-violet: rgba(109,69,201,.16);

    --st-active:   #0f7a52; --st-final30:  #8a5a0c; --st-freeze:   #c2503f;
  --st-overdue:  #a34a00;
  --alarm-bg: #b3241f; --alarm-ink: #ffffff;
  --money-alarm: #a8480a;
    --st-inactive: #b02020; --st-declined: #7a0d0d; --st-inperson: #2a62bd;
    --st-finished: #6a6a62;
  --ink-good: #0f7a52; --ink-warn: #8a5a0c; --ink-bad: #b3241f;
  --ink-info: #2a62bd; --ink-violet: #5b3bb8;
  --ink-yellow: #7a5f00; --tint-yellow: rgba(214,178,40,.22);
  --star-gold: #e0a207;
  --sw-green: #17a06a; --sw-amber: #e8a21c; --sw-red: #d8403c;
}

/* Nobody has chosen yet: follow the system rather than forcing dark. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #f7f6f3; --panel: #ffffff; --panel-2: #faf9f7; --sidebar: #fbfaf8;
    --line: rgba(24,24,20,.10); --line-strong: rgba(24,24,20,.20);
    --text: #1b1b18; --muted: #5f5f58; --subtle: #6b6b62;
    --blue: var(--accent-ink-light, #2f6fd0); --violet: #6d45c9; --green: #12805a;
  --select-arrow: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%228%22%20viewBox%3D%220%200%2012%208%22%3E%3Cpath%20d%3D%22M1%201.5%206%206.5%2011%201.5%22%20fill%3D%22none%22%20stroke%3D%22%236f6b64%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
    --amber: #9a6410; --red: #c2382f;
    --lift-1: rgba(24,24,20,.022); --lift-2: rgba(24,24,20,.045);
    --lift-3: rgba(24,24,20,.07); --lift-4: rgba(24,24,20,.10);
    --sink-1: rgba(24,24,20,.05); --field: #ffffff;
    --shadow: 0 10px 30px rgba(24,24,20,.08);
    --shadow-sm: 0 1px 3px rgba(24,24,20,.07);
    --shadow-lg: 0 20px 50px rgba(24,24,20,.13);
    --tint-blue: color-mix(in srgb, var(--blue) 16%, transparent);
    --blue-on-tint: color-mix(in srgb, var(--blue) 76%, #000); --tint-green: rgba(18,128,90,.17);
    --blue-line: color-mix(in srgb, var(--blue) 40%, transparent);
    --tint-amber: rgba(154,100,16,.18); --tint-red: rgba(194,56,47,.18);
    --tint-violet: rgba(109,69,201,.16);

    --st-active:   #0f7a52; --st-final30:  #8a5a0c; --st-freeze:   #c2503f;
  --st-overdue:  #a34a00;
  --alarm-bg: #b3241f; --alarm-ink: #ffffff;
  --money-alarm: #a8480a;
    --st-inactive: #b02020; --st-declined: #7a0d0d; --st-inperson: #2a62bd;
    --st-finished: #6a6a62;

    --ink-good: #0f7a52; --ink-warn: #8a5a0c; --ink-bad: #b3241f;
    --ink-info: #2a62bd; --ink-violet: #5b3bb8;
    --ink-yellow: #7a5f00; --tint-yellow: rgba(214,178,40,.22);
  --star-gold: #e0a207;
  --sw-green: #17a06a; --sw-amber: #e8a21c; --sw-red: #d8403c;
  }
}

/* Every element carrying the hidden attribute is hidden, whatever else
   it is styled as. Without this any rule that sets `display` silently
   overrides the attribute -- which has now caused three separate bugs:
   a conversation drawn on top of its own empty state, a thread panel
   that would not close, and a notification badge permanently lit with
   nothing in it. */
[hidden] { display: none !important; }
* { box-sizing: border-box; }
html { background: var(--bg); -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* ^ Without this, Safari on a phone "helpfully" inflates the text of any block
   it decides is wide -- the prospect list's preview lines came out at twice
   the size of the names beside them. Sizes here are set on purpose. */
body {
  margin: 0; min-width: 320px; color: var(--text); background: var(--bg);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
}
button, input, select, textarea { font: inherit; }
button { cursor: pointer; }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
a { text-decoration: none; }

.app-shell { display: grid; grid-template-columns: var(--sbw, 248px) minmax(0,1fr); min-height: 100vh; }
body[data-asbar] .app-shell { min-height: calc(100vh - var(--asbar, 40px)); }
.sidebar {
  position: sticky; top: 0; height: 100vh; z-index: 4; padding: 18px 14px; overflow: hidden;
  /* the nav scrolls inside; the sidebar itself must not, or the footer scrolls away */
  border-right: 1px solid var(--line); background: var(--sidebar);
  display: flex; flex-direction: column; gap: 22px; transition: width .2s ease, padding .2s ease;
}
.brand { display: flex; align-items: center; gap: 10px; min-height: 38px; }
/* The logo tile is lit the way Google Tasks lights its icon (10-17-2026): the
   gradient, a gloss over the top half, a rim that catches the light along the
   top edge, and a soft shadow in its own colour underneath -- a raised tile,
   not a flat sticker. The app icons and the SVG masters carry the same three. */
.brand-mark {
  --mark-drop: 0 2px 3px color-mix(in srgb, var(--blue-solid) 34%, transparent),
               0 8px 16px -4px color-mix(in srgb, var(--blue-solid) 50%, transparent);
  width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--on-accent) 24%, transparent), transparent 55%),
    linear-gradient(135deg, var(--blue-solid), color-mix(in srgb, var(--blue-solid) 45%, var(--violet)));
  box-shadow:
    inset 0 1.5px 0 color-mix(in srgb, var(--on-accent) 75%, transparent),
    inset 0 0 0 1.5px color-mix(in srgb, var(--on-accent) 34%, transparent),
    var(--mark-drop);
  font-weight: 800; color: var(--on-accent);
}
.brand-copy { min-width: 0; line-height: 1.05; }
.brand-copy strong { display:block; font-size: 15px; letter-spacing: -.02em; color: var(--text); }
.brand-copy span { display:block; color: var(--subtle); margin-top: 4px; font-size: 11px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase; }
.collapse { margin-left: auto; width: 28px; height: 28px; color: var(--muted); border: 1px solid var(--line); border-radius: 8px; background: transparent; }
.nav-section { display: grid; gap: 2px; }
.nav-label { padding: 0 10px; color: var(--subtle); font-size: 11px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase; }
.nav-item { width:100%; min-height: 31px; padding: 0 9px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font-size: 12.5px; display: flex; align-items: center; gap: 9px; text-align: left; transition: background .16s ease, color .16s ease; }
/* The label takes only the room its words need, so the star and the count
   sit right after it rather than at the far edge of a wide sidebar (Stefan:
   "bring stars closer to labels"). A long label shortens with an ellipsis
   before anything is pushed under the drag handle at the right. */
.nav-item { padding-right: 24px; }
.nav-item-label { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-item:hover { background: var(--lift-2); color: var(--text); }
.nav-item.active { color: white; background: linear-gradient(90deg, color-mix(in srgb, var(--blue) 18%, transparent), color-mix(in srgb, var(--blue) 4%, transparent)); box-shadow: inset 2px 0 0 var(--blue); }
.nav-icon { width: 16px; flex: 0 0 16px; color: inherit; text-align:center; font-size: 13px; }
.sidebar-note { margin-top: auto; padding: 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--lift-1); color: var(--subtle); font-size: 11px; line-height: 1.55; }
/* The sidebar's right edge is a handle. Six pixels wide, invisible until
   hovered, when a thin blue line says "this moves". While dragging, the
   shell's transitions are off so the page follows the pointer exactly. */
.sb-resizer { position: absolute; top: 0; right: -1px; width: 7px; height: 100%; cursor: col-resize; z-index: 6; }
.sb-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; right: 1px; width: 2px;
  background: var(--blue); opacity: 0; transition: opacity .15s ease; }
.sb-resizer:hover::after, body.sb-resizing .sb-resizer::after { opacity: .85; }
body.sb-resizing { cursor: col-resize; user-select: none; -webkit-user-select: none; }
body.sb-resizing .sidebar, body.sb-resizing .app-shell, body.sb-resizing main { transition: none !important; }
body.sb-resizing iframe { pointer-events: none; }   /* an iframe would swallow the drag */
@media (max-width: 940px) { .sb-resizer { display: none; } }

/* The collapsed rail ---------------------------------------------------------
   Hiding the labels is not enough: the favourite star, the drag handle and the
   section gaps all stayed, so starred items had a star wedged against their
   icon and the whole rail read as half-cut-off. Collapsed is its own layout,
   not the open sidebar with the text switched off. */
.sidebar.collapsed { width: 64px; padding: 18px 8px; gap: 14px; }
.app-shell.collapsed { grid-template-columns: 64px minmax(0,1fr); }
.sidebar.collapsed .brand-copy,
.sidebar.collapsed .nav-label,
.sidebar.collapsed .nav-item-label,
.sidebar.collapsed .nav-star,
.sidebar.collapsed .nav-grip,
.sidebar.collapsed .sidebar-note,
.sidebar.collapsed .sidebar-foot,
.sidebar.collapsed .nav-caret { display: none !important; }
.sidebar.collapsed .brand { justify-content: center; }

/* A square, evenly spaced hit target. The icon is the whole control, so it
   gets the whole button. */
.sidebar.collapsed .nav-item { justify-content: center; padding: 0;
  min-height: 40px; width: 40px; margin: 0 auto; border-radius: 10px; gap: 0; }
.sidebar.collapsed .nav-icon { width: 20px; flex: 0 0 20px; font-size: 15px; }
/* Folded, the icon IS the link. Its "Change icon" button stood on top of it,
   so every click opened the emoji picker instead of the page (Stefan,
   10-16-2026). Clicks pass through to the row; icons are changed open. */
.sidebar.collapsed .nav-icon-btn { pointer-events: none; }
/* The open state marks the active row with a left bar; at this width a bar on
   a 40px square just looks clipped, so it becomes a filled tile. */
.sidebar.collapsed .nav-item.active { background: color-mix(in srgb, var(--blue) 20%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--blue) 40%, transparent); }

/* Sections keep their separation without their headings. */
.sidebar.collapsed .nav-group + .nav-group { margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line); }
.sidebar.collapsed .nav-items { gap: 4px; }
/* A collapsed SECTION still hides its items when the rail is collapsed --
   otherwise closing a section and then the sidebar shows it reopened. */
.sidebar.collapsed .nav-section.collapsed .nav-items { display: none; }

/* The toggle sat outside the rail on a negative offset, which put it under the
   page's own scrollbar and made it look cut off. It belongs inside. */
.sidebar.collapsed .collapse { position: static; margin: 0 auto; width: 32px;
  height: 32px; background: transparent; }
.sidebar.collapsed .collapse svg { transform: rotate(180deg); }

/* The rail scrolls when there are more modules than height. Give it a thin
   scrollbar rather than the browser's default slab, which at 64px wide takes a
   quarter of the rail. */
.sidebar.collapsed #sidebarNav { scrollbar-width: thin; }
.sidebar.collapsed #sidebarNav::-webkit-scrollbar { width: 3px; }
.sidebar.collapsed #sidebarNav::-webkit-scrollbar-thumb { background: var(--line); }

/* A column, so the footer can be pushed to the foot of the window by the
   page above it. The 36px that used to sit under every page is gone: the
   gap below the content is .page's own padding now, which means it is
   inside the box that grows, and a page that fits the screen does not
   scroll past itself to reach the footer (10-13-2026). */
main { min-width: 0; padding: 0 26px 0; display: flex; flex-direction: column; }
/* Pinned. The title, the search, the Brain and the icons stay put while the
   page scrolls under them -- on the desk, on the phone, and in the installed
   app. It spans main's gutters (the negative margin) so nothing scrolls past
   at the sides, and sits below the "acting as" bar when that is up. */
.topbar { min-height: 72px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 16px;
  position: sticky; top: 0; z-index: 5; background: var(--bg); margin: 0 -26px; padding: 0 26px; }
body[data-asbar] .topbar { top: var(--asbar, 40px); }
.crumb { color: var(--muted); font-size: 13px; }
.crumb b { color: var(--text); font-weight: 700; }
.page { max-width: 1520px; width: 100%; margin: 0 auto; padding: 30px 0 20px;
  flex: 1 0 auto; }
/* The pages that run their own full-height panes size themselves against
   the viewport and hide the footer; 20px of padding under them is 20px of
   scroll. */
body.ws-page .page, body.cal-page .page, body.ibx-page .page,
body.embed .page { padding-bottom: 0; }
.page-heading { display:flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
h1 { margin:0; font-size: clamp(23px, 3vw, 30px); letter-spacing: -.045em; }
.eyebrow { margin: 0 0 7px; color: var(--blue); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; }
.lede { margin: 8px 0 0; color: var(--muted); max-width: 680px; line-height: 1.55; }
.action-row { display:flex; gap: 8px; align-items:center; flex-wrap: wrap; }
/* EVERY PRIMARY BUTTON IS THE GRADIENT. It used to be a flat --blue-solid,
   with the gradient reserved for the handful carrying `brand` -- so the Forms
   button on Check-ins had it and View check-in beside it did not, which read
   as two tiers of button with no rule behind which was which. Stefan,
   10-12-2026: "some stuff uses the gradient color, which, to me, is way higher
   quality, and other buttons don't."

   The fill is --btn-grad, built from the accent the person chose and that
   accent's own partner, so all nine work and none of them mixes a hue that
   is not theirs. White text measures 4.60:1 at worst across every accent,
   both themes, at both ends and the middle.

   The quiet ones (secondary, ghost) and the ones that mean something
   (danger, is-danger, confirm) set `background` as a shorthand further down,
   which clears the image -- so they stay flat, which is the point of them. */
.button { border: 1px solid transparent; border-radius: 9px; padding: 9px 12px; color: white; background: var(--btn-grad); font-size: 12px; font-weight: 750; box-shadow: 0 1px 2px rgba(0,0,0,.14); transition: transform .14s ease, filter .14s ease, box-shadow .14s ease; }
.button:hover { filter: brightness(1.08); box-shadow: 0 3px 12px color-mix(in srgb, var(--blue-solid) 45%, transparent); }
.button:active { transform: scale(.97); }
/* One quiet button. `secondary` and `ghost` were the same idea written twice
   -- one defined, one asked for in thirty places and never defined -- and
   `small` and `sm` were two sizes of small. Each pair is one rule now, so the
   two names cannot drift apart again. Both stay valid at the call sites.

   They sit on the panel with a little lift: fully transparent, a row of them
   read as text with boxes drawn round it rather than as controls. The hover
   colour follows the theme -- it was literally "white", 1.16:1 on paper. */
.button.secondary, .button.ghost { border-color: var(--line-strong); background: var(--panel);
  color: var(--muted); font-weight: 700; box-shadow: var(--shadow-sm); }
.button.secondary:hover, .button.ghost:hover { background: var(--lift-2); color: var(--text);
  border-color: var(--blue); transform: translateY(-1px); box-shadow: var(--shadow); }
.button.danger { background: transparent; border-color: rgba(251,100,114,.35); color:var(--ink-bad); }
.button.danger:hover { background: var(--tint-red); color: var(--red); border-color: var(--red); }
.button.small, .button.sm { padding: 5px 11px; font-size: 11px; border-radius: 8px; }
.button:disabled { opacity:.5; cursor:not-allowed; }

.section { margin-top: 28px; }
.section-head { display:flex; justify-content:space-between; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.section-head h2 { margin:0; font-size: 15px; letter-spacing: -.015em; }
.section-head p { margin: 4px 0 0; color: var(--subtle); font-size: 12px; }
.chips { display:flex; gap:6px; flex-wrap:wrap; }
/* Call Log: the tab strip must not move between tabs. Pinned to the top of
   the header, with the subtitle holding two lines whatever it says. */
/* The filters always sit on their own line under the title. Beside a short
   subtitle they fit next to it, under a long one they wrapped below -- so the
   row jumped with the length of the sentence above it (Stefan, 09-23-2026:
   Upcoming against No close). One line of its own, and two lines held for
   the subtitle, and the row never moves. */
.calls-head { flex-direction:column; align-items:stretch; gap:10px; }
.calls-head #listSub { min-height:2.9em; max-width:92ch; margin-bottom:0; }
.calls-head > .action-row { justify-content:flex-start; }
/* The list keeps at least a screen of room under the filters. An empty
   filter used to shorten the page, the browser pulled the scroll back to fit,
   and the filter row jumped -- Stefan, 09-23-2026, cycling Everyone, Mine and
   Needs outcome: "I want it to just stay in one spot." With the room held,
   the row stays where the last click left it. Held by the area around the
   list and the nag under it, not the list itself, or the nag sat a whole
   screen below the last call (10-02-2026). */
#callArea { min-height:100vh; }
.chip { border: 1px solid var(--line); border-radius: 999px; background: transparent; padding: 6px 9px; color: var(--muted); font-size: 11px; font-weight: 650; }
.chip.active { background: color-mix(in srgb, var(--blue) 16%, transparent); border-color: color-mix(in srgb, var(--blue) 50%, transparent); color: var(--ink-info); }
/* A call's agreement, once it has come back signed (calls.js). */
.chip.is-signed { background: color-mix(in srgb, var(--ink-good) 12%, transparent); border-color: color-mix(in srgb, var(--ink-good) 45%, transparent); color: var(--ink-good); }
/* A row of chips is the same control as the tab strip -- pick one of these --
   and it had the same problem: flat, transparent, with the selected one only
   slightly bluer than the rest. Scoped to .chips so the dozens of chips used
   as small inline labels elsewhere stay quiet. */
.chips .chip { background:var(--panel); box-shadow:var(--shadow-sm); cursor:pointer;
  transition:background .14s ease, color .14s ease, border-color .14s ease,
             transform .14s ease, box-shadow .14s ease; }
.chips .chip:hover { background:var(--lift-2); color:var(--text);
  border-color:var(--line-strong); transform:translateY(-1px); box-shadow:var(--shadow); }
.chips .chip:active { transform:translateY(0); }
.chips .chip.active { background:var(--blue-solid); border-color:var(--blue-solid);
  color:#fff; box-shadow:0 2px 10px color-mix(in srgb, var(--blue-solid) 40%, transparent); }
.chips .chip.active:hover { background:var(--blue-solid); color:#fff;
  border-color:var(--blue-solid); }
.metric-grid { display:grid; grid-template-columns: repeat(5, minmax(0,1fr)); gap: 12px; }
/* Where the number of cards is decided by the data rather than fixed in the
   markup, let them fill the row. A hard five columns leaves the eleventh card
   sitting alone on a row of its own, which reads as a mistake. */
.metric-grid.flow { grid-template-columns: repeat(auto-fit, minmax(186px, 1fr)); }
.metric { position:relative; overflow:hidden; min-height: 108px;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 17px;
  background: linear-gradient(150deg, var(--panel-2), var(--panel));
  box-shadow:var(--shadow);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
.metric:hover { transform: translateY(-2px); border-color: var(--line-strong);
  box-shadow:var(--shadow); }
@media (prefers-reduced-motion: reduce) { .metric:hover { transform:none; } }
.metric::after { content:""; position:absolute; inset: auto -20px -46px auto; width:100px; height:100px; border-radius:50%; background: radial-gradient(circle, var(--metric-color, color-mix(in srgb, var(--blue) 13%, transparent)), transparent 68%); }
.metric-label { position:relative; z-index:1; color: var(--muted); font-size: 11px; font-weight: 800; letter-spacing:.08em; text-transform:uppercase; }
.metric-value { position:relative; z-index:1; margin-top: 11px; font-size: 23px; font-weight: 800; letter-spacing:-.035em; font-variant-numeric: tabular-nums; }
.metric-sub { position:relative; z-index:1; margin-top: 7px; color: var(--subtle); font-size: 11px; }
.positive { color: var(--green); } .warning { color: var(--amber); } .negative { color: var(--red); } .blue { color:var(--ink-info); }

/* Every card in the hub, lifted. Stefan's complaint was that the app reads as
   text pasted flat onto a background, and that the AI tool cards -- gradient,
   shadowed, floating slightly -- were the part that felt finished. This is
   that treatment, applied to the primitive rather than page by page, so it
   reaches everything at once. */
.card { border:1px solid var(--line); border-radius: var(--radius); padding:18px;
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  box-shadow:var(--shadow);
  transition: box-shadow .18s ease, border-color .18s ease; }
.card h3, .card h2.as-h3 { margin:0; font-size: 14px; letter-spacing:-.015em; }
/* A card heading that is a level-two in the document because the page
   heading is the level-one, and looks exactly like the level-three beside
   it. A screen reader reads the structure; everyone else reads the page. */
.as-h3 { font-size:14px; letter-spacing:-.015em; margin:0; font-weight:700; }
.card-sub { margin:5px 0 0; color:var(--subtle); font-size:12px; }
.main-grid { display:grid; grid-template-columns: minmax(0,1.5fr) minmax(300px,.9fr); gap: 12px; }
.two-col { display:grid; grid-template-columns: minmax(0,1fr) minmax(330px,.95fr); gap:12px; }

.chart { height: 260px; display:flex; align-items:flex-end; gap:8px; padding: 28px 4px 4px; border-bottom:1px solid var(--line); background: repeating-linear-gradient(to bottom, transparent 0, transparent 51px, var(--lift-1) 52px); overflow-x:auto; }
.bar-group { flex:1; min-width: 22px; height:100%; display:flex; align-items:flex-end; justify-content:center; gap:3px; position:relative; }
.bar { width: 42%; max-width:26px; min-height:3px; border-radius:6px 6px 0 0; transition:height .25s ease; }
.bar.ad { background:linear-gradient(color-mix(in srgb, var(--blue) 70%, white), var(--blue)); } .bar.cash { background:linear-gradient(#59e5ad, var(--green)); }
.bar-label { position:absolute; bottom:-22px; color:var(--subtle); font-size:11px; white-space:nowrap; }
.legend { display:flex; gap:12px; color:var(--subtle); font-size:11px; margin-top:28px; flex-wrap:wrap; }
.legend i { display:inline-block; width:8px; height:8px; border-radius:2px; margin-right:4px; }

.table-wrap { overflow:auto; border:1px solid var(--line); border-radius: 12px; }
table { width:100%; min-width:650px; border-collapse:collapse; }
th { padding:11px 12px; color:var(--subtle); background:var(--lift-1); border-bottom:1px solid var(--line); text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:.06em; white-space:nowrap; }
td { padding:12px; color:var(--muted); border-bottom:1px solid var(--line); font-size:12px; white-space:nowrap; }
/* The roster is back at the app's own table size. Shrinking the type was
   treating the symptom: what made Signals fall off the edge was 400px of
   DEAD COLUMN between End and Last contact -- see .next-term below, where one
   re-signed row was holding the End column open on a single unbreakable
   line. Stefan, 09-26-2026: "go back to original sizing ... just move all
   that stuff to the right", and on the gap itself: "close this blank space to
   make everything fit." Closing it is worth far more width than any zoom, and
   costs nothing to read.

   Signals stays on ONE line -- "the signals wrapping didn't go well either,
   they're cutting off. I don't want wrapping on the signals." What makes that
   possible is short chips with the sentence on hover, not a smaller font (see
   the "short" key in csm.py). */
.roster td.ros-signals { white-space: nowrap; }

/* The roster's own zoom control. Small and out of the way -- it is a setting,
   not an action -- and it sits outside the area it scales. */
.ros-zoom-bar { display:flex; justify-content:flex-end; margin:0 0 6px; }
.zoomer { display:inline-flex; align-items:center; gap:2px; padding:2px;
  border:1px solid var(--line); border-radius:999px; background:var(--lift-1); }
.zoomer button { min-width:24px; height:24px; padding:0 6px; border:0; cursor:pointer;
  border-radius:999px; background:transparent; color:var(--muted);
  font-size:13px; font-weight:700; line-height:1; }
.zoomer button:hover:not(:disabled) { background:var(--lift-2); color:var(--text); }
.zoomer button:disabled { opacity:.35; cursor:default; }
.zoomer b { min-width:38px; text-align:center; font-size:10.5px; font-weight:700;
  color:var(--subtle); font-variant-numeric:tabular-nums; }
.zoomer.zoomed b { color:var(--blue); }
.zoomer-reset { font-size:10px !important; font-weight:650 !important;
  letter-spacing:.03em; color:var(--subtle) !important; }
.zoomer:not(.zoomed) .zoomer-reset { display:none; }

/* Ad Labels: the whole row on one screen. Ad and ad-set names wrap instead
   of pushing the label box off the right edge. */
.al-wrap { overflow:visible; }
.al-table { table-layout:fixed; min-width:0; }
.al-table td { vertical-align:top; }
.al-table td.al-name { white-space:normal; overflow-wrap:anywhere; color:var(--text); }
.al-table td.al-ran { white-space:normal; font-variant-numeric:tabular-nums; }
/* "DM / Direct Offer" is wider than its column: wrap it rather than let it
   run under Spend. Every cell here is fixed-width, so nothing may overflow. */
.al-table td.al-type { white-space:normal; }
.al-table td.al-num { font-variant-numeric:tabular-nums; }
.al-table td { overflow:hidden; text-overflow:ellipsis; }
/* Headers wrap and are never clipped -- an ellipsis on "Leads" reads as
   broken, not as tight. */
.al-table th { white-space:normal; }
.al-table td .lbl { width:100%; }
@media (max-width:1100px) { .al-wrap { overflow:auto; } .al-table { min-width:900px; } }
td:first-child { color:var(--text); font-weight:650; }
tbody tr:last-child td { border-bottom:0; }
tbody tr:hover { background:var(--lift-1); }
.status { display:inline-flex; align-items:center; gap:5px; padding:4px 7px; border-radius:999px; font-size:11px; font-weight:800; }
.status.scale { color:var(--ink-good); background:rgba(53,201,143,.12); } .status.monitor { color:var(--ink-warn); background:rgba(245,174,78,.12); } .status.pause { color:var(--ink-bad); background:rgba(251,100,114,.12); }
.status.manual { color:var(--ink-info); background:color-mix(in srgb, var(--blue) 12%, transparent); } .status.meta_sync { color:var(--ink-violet); background:rgba(139,92,246,.12); } .status.import { color:var(--subtle); background:var(--lift-2); }

label { display:block; margin-bottom:5px; color:var(--muted); font-size:11px; font-weight:700; letter-spacing:.045em; }
label small { display:block; margin-top:3px; color:var(--subtle); font-size:11px; line-height:1.3; font-weight:500; letter-spacing:0; }
input, select, textarea { width:100%; border:1px solid var(--line); border-radius:8px; padding:9px 10px; color:var(--text); background:var(--field); font-size:12px; }
/* Dates need room the other boxes do not. "MM-DD-YYYY" and Chrome's own
   calendar button together are wider than a text box's content, so in any
   narrow cell the placeholder crowded the icon or ran past the edge --
   Stefan, 09-26-2026: "I see a common reoccurring theme with the date box
   throughout the app ... it ends up bleeding outside a lot of the boxes. We
   either need to resize the text or expand the boxes."

   NOTE: on a loaded page these are rare -- datepick.js replaces every date
   input with its own .dp control (see .dp-text below, which is where the fix
   that matters lives). This covers the moment before that swap, and anything
   the swap skips. */
input[type="date"], input[type="time"], input[type="datetime-local"],
input[type="month"], input[type="week"] {
  font-size: 11.5px; padding-left: 9px; padding-right: 4px;
  font-variant-numeric: tabular-nums; }
/* Every dropdown in the app was drawing the operating system's own control --
   appearance:auto -- so it carried macOS chrome and a grey native arrow while
   everything around it was ours. Same box as the other fields now, with a
   chevron that follows the theme. */
select { appearance:none; -webkit-appearance:none; -moz-appearance:none;
  padding-right:30px; background-image:var(--select-arrow);
  background-repeat:no-repeat; background-position:right 11px center;
  background-color:var(--panel); border-color:var(--line-strong);
  box-shadow:var(--shadow-sm); font-weight:600; cursor:pointer;
  transition:border-color .14s ease, box-shadow .14s ease, transform .14s ease; }
select:hover { border-color:var(--blue); transform:translateY(-1px);
  box-shadow:var(--shadow); }
select:focus-visible { outline:2px solid var(--blue); outline-offset:-1px; }
select::-ms-expand { display:none; }
textarea { min-height:64px; resize:vertical; line-height:1.4; }
/* The rule above stretches every input to 100%, which for a checkbox means a
   full-width box with its label shoved to the far edge. Pages have been
   working around it one at a time with inline width:auto; this fixes it for
   all of them. */
input[type=checkbox], input[type=radio] { width:auto; flex:none; margin:0; }
.field-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap: 10px; }
.field.full { grid-column: 1 / -1; }
.helper { color:var(--subtle); font-size:12px; line-height:1.45; }
/* Used in seventy-odd places -- table cells, secondary lines under a name --
   and never actually defined, so every one of them rendered in full ink.
   Quieter, as each of those call sites meant. */
.muted { color: var(--muted); }
.empty { padding:32px; text-align:center; color:var(--subtle); }
/* The toast lives in the top bar, in the gap between the search pill and
   the icons -- a stretch of screen that never holds anything, so a "Saved"
   or a "Could not send" is never text over text. Out of the flow until it
   shows, so the icons do not shift. */
.topbar-actions { position:relative; }
.toast { position:absolute; right:0; top:50%; transform:translateY(-50%) translateX(6px); z-index:10; order:-1; margin-right:8px;
  border:1px solid rgba(53,201,143,.5); border-left-width:4px; background:var(--panel); color:var(--green); border-radius:9px; padding:8px 12px;
  box-shadow:var(--shadow); font-size:12px; font-weight:600; line-height:1.35; opacity:0; pointer-events:none; transition:opacity .18s ease, transform .18s ease;
  white-space:normal; }
.toast.show { position:relative; top:auto; transform:none; opacity:1; }
/* Opaque, always: a see-through toast over a page is text on text. */
.toast.error { border-color: rgba(251,100,114,.55); background:var(--panel); color:var(--red); }
.toast { pointer-events:auto; display:flex; align-items:center; gap:10px; max-width:min(520px, 44vw); }
/* A phone has no empty stretch in its bar: the toast sits just under it,
   full width, over the top of the page rather than over the composer. */
@media (max-width:700px) {
  .toast, .toast.show { position:fixed; left:12px; right:12px; top:calc(var(--asbar, 0px) + var(--topbar-h, 64px) + 8px); max-width:none; margin:0; order:0; }
  .toast { transform:translateY(-6px); }
  .toast.show { transform:none; }
}
.toast:not(.show) { pointer-events:none; }
.toast-act { font:inherit; font-weight:700; padding:4px 9px; border-radius:7px; cursor:pointer;
  border:1px solid currentColor; background:transparent; color:inherit; white-space:nowrap; }
.toast-act:hover { background:rgba(255,255,255,.12); }
.toast-x { font:inherit; font-size:15px; line-height:1; padding:0 2px; border:0; background:none;
  color:inherit; opacity:.7; cursor:pointer; }
.toast-x:hover { opacity:1; }
.badge { border: 1px solid rgba(139,92,246,.35); color:var(--ink-violet); background: rgba(139,92,246,.10); padding: 4px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; }

@media (max-width:1200px) { .metric-grid { grid-template-columns:repeat(3,1fr); } .field-grid{grid-template-columns:repeat(2,1fr);} }
@media (max-width:940px) { .app-shell { grid-template-columns:68px minmax(0,1fr); } .sidebar { width:68px; padding-inline:10px; } .sidebar .brand-copy,.sidebar .nav-label,.sidebar .nav-item span:not(.nav-badge),.sidebar .sidebar-note { display:none; } .sidebar .brand { justify-content:center; } .sidebar .collapse { margin-left:0; position:absolute; right:-14px; background:var(--panel); } .sidebar .nav-item { justify-content:center; padding-inline:0; } .main-grid,.two-col { grid-template-columns:1fr; }
  /* The count rides the icon's corner on the narrow rail, as in the collapsed one. */
  .sidebar .nav-badge { position:absolute; top:2px; right:4px; min-width:16px; height:16px; line-height:16px; font-size:9px; padding:0 4px; margin:0; box-shadow:0 0 0 2px var(--sidebar); } }
@media (max-width:680px) { main { padding:0 14px 0; } .topbar { min-height:64px; margin:0 -14px; padding:0 14px; } .page { padding-top:22px; } .page-heading { align-items:flex-start; flex-direction:column; } .metric-grid { grid-template-columns:repeat(2,1fr); } .field-grid{grid-template-columns:1fr;} }

/* ============================================================
   Data visualization
   Categorical slots are the dataviz reference palette's *dark*
   steps, assigned to entities in a fixed order (never by rank).
   Our surfaces (#0b0f15 / #121822) are darker than the palette's
   reference dark surface (#1a1a19), so every slot's contrast
   against the surface is >= what was validated; CVD separation
   between slots is surface-independent.
   ============================================================ */
.viz-root {
  --series-1: #3987e5;  /* blue    */
  --series-2: #d95926;  /* orange  */
  --series-3: #199e70;  /* aqua    */
  --series-4: #c98500;  /* yellow  */
  --series-5: #d55181;  /* magenta */
  --series-6: #008300;  /* green   */
  --series-7: #9085e9;  /* violet  */
  --series-8: #e66767;  /* red     */
  /* Was #898781. Its chroma was 0.009 -- a grey wearing a series slot --
     and against Lead Magnet's magenta it measured OKLab dE 1.1 under
     deuteranopia: the same line, twice, to a red-green colourblind
     reader. This passes all six checks in both modes. */
  --series-other: #8a7cc4;
  --viz-grid: var(--lift-3);
  --viz-axis: var(--lift-1);
  --viz-ink-muted: #93a1b5;
}

.filter-bar { display:flex; flex-wrap:wrap; gap:14px; align-items:flex-end; justify-content:space-between; }
.filter-group { display:flex; flex-direction:column; gap:6px; }
.filter-group > .filter-label { color:var(--subtle); font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase; }
.filter-row-inner { display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
.filter-bar select { min-width:150px; }

.segmented { display:inline-flex; border:1px solid var(--line); border-radius:9px; overflow:hidden; }
.segmented button { padding:6px 12px; border:0; background:transparent; color:var(--muted); font-size:11px; font-weight:700; }
.segmented button + button { border-left:1px solid var(--line); }
.segmented button.active { background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--ink-info); }

.chart-head { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:6px; }
.chart-box { width:100%; overflow:hidden; }
.chart-box svg { display:block; width:100%; }
.viz-legend { display:flex; flex-wrap:wrap; gap:14px; margin-top:12px; }
.viz-legend-item { display:inline-flex; align-items:center; gap:7px; color:var(--muted); font-size:11px; }
.viz-legend-item i { width:10px; height:10px; border-radius:3px; flex:0 0 10px; }
.viz-legend-item.dim { opacity:.4; }
.viz-legend-item button { background:none; border:0; color:inherit; font:inherit; padding:0; }

.viz-tooltip {
  position:fixed; z-index:60; pointer-events:none; opacity:0; transform:translateY(4px);
  transition:opacity .12s ease, transform .12s ease;
  background:var(--field); border:1px solid var(--line-strong); border-radius:10px;
  padding:9px 11px; box-shadow:var(--shadow); font-size:11px; color:var(--text); min-width:150px;
}
.viz-tooltip.show { opacity:1; transform:translateY(0); }
.viz-tooltip .tt-title { color:var(--subtle); font-size:10px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; margin-bottom:6px; }
.viz-tooltip .tt-row { display:flex; align-items:center; gap:8px; justify-content:space-between; margin-top:3px; }
.viz-tooltip .tt-key { display:inline-flex; align-items:center; gap:6px; color:var(--muted); }
.viz-tooltip .tt-key i { width:8px; height:8px; border-radius:2px; flex:0 0 8px; }
.viz-tooltip .tt-val { color:var(--text); font-weight:700; font-variant-numeric:tabular-nums; }
.viz-tooltip .tt-total { margin-top:7px; padding-top:6px; border-top:1px solid var(--line); }
/* The columns either side of the one you are on step back rather than
   vanish. Transitioned, because an instant swap between two whole chart
   states reads as a flicker rather than as a response. */
.viz-seg { transition: opacity .12s ease; }
.viz-rail { transition: opacity .12s ease; }
@media (prefers-reduced-motion:reduce) {
  .viz-seg, .viz-rail { transition: none; } }

.bar-list { display:grid; gap:14px; margin-top:16px; }
.bar-item-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:6px; }
.bar-item-name { display:inline-flex; align-items:center; gap:8px; color:var(--text); font-size:12px; font-weight:650; }
.bar-item-name i { width:10px; height:10px; border-radius:3px; flex:0 0 10px; }
.bar-item-val { color:var(--text); font-size:12px; font-weight:700; font-variant-numeric:tabular-nums; }
.bar-item-sub { color:var(--subtle); font-size:11px; font-variant-numeric:tabular-nums; }
.bar-track { height:8px; border-radius:999px; background:var(--lift-2); overflow:hidden; }
.bar-fill { height:100%; border-radius:999px; }

.split-list { display:grid; gap:10px; margin-top:14px; }
.split-row { display:grid; grid-template-columns:1fr auto; gap:8px; align-items:baseline; padding:10px 12px; border:1px solid var(--line); border-radius:10px; background:var(--lift-1); }
.split-row b { color:var(--text); font-size:12px; }
.split-row span { color:var(--subtle); font-size:11px; }
.split-row .split-val { color:var(--text); font-size:13px; font-weight:750; font-variant-numeric:tabular-nums; text-align:right; }

.table-toggle { display:flex; justify-content:flex-end; margin-top:10px; }
.loading { opacity:.45; transition:opacity .15s ease; }
/* Proportional figures on large standalone numbers; tabular-nums is reserved
   for columns that align vertically (table rows, axis ticks). */
.viz-root .metric-value { font-variant-numeric: proportional-nums; }
/* Six stat tiles read as one row on wide screens, no orphan. */
.viz-root .metric-grid { grid-template-columns: repeat(6, minmax(0,1fr)); }
@media (max-width:1400px) { .viz-root .metric-grid { grid-template-columns: repeat(3,1fr); } }
@media (max-width:760px)  { .viz-root .metric-grid { grid-template-columns: repeat(2,1fr); } }
/* The roster's five, since Renewing went: a sixth column with nothing in it
   reads as a card that failed to load. The breakpoints are the ones above. */
.viz-root #tiles.metric-grid { grid-template-columns: repeat(5, minmax(0,1fr)); }
@media (max-width:1400px) { .viz-root #tiles.metric-grid { grid-template-columns: repeat(3,1fr); } }
@media (max-width:760px)  { .viz-root #tiles.metric-grid { grid-template-columns: repeat(2,1fr); } }
/* Command Center funnel */
.funnel-list { display:grid; gap:16px; margin-top:16px; }
.funnel-step-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:6px; }
.funnel-step-name { color:var(--text); font-size:12px; font-weight:650; }
.funnel-step-val { color:var(--text); font-size:12px; font-weight:700; font-variant-numeric:tabular-nums; }

/* Weekly scorecard */
.ad-card { margin-bottom: 12px; }
.ad-card-head { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; }
.decision { margin-top:14px; padding:12px 14px; border-radius:11px; border:1px solid var(--line); background:var(--lift-1); }
.decision b { display:block; font-size:12.5px; color:var(--text); }
.decision p { margin:5px 0 0; color:var(--muted); font-size:11.5px; line-height:1.5; }
.decision.kill { border-color:rgba(251,100,114,.35); background:rgba(251,100,114,.07); }
.decision.kill b { color:var(--ink-bad); }
.decision.fix_first { border-color:rgba(245,174,78,.32); background:rgba(245,174,78,.06); }
.decision.fix_first b { color:var(--ink-warn); }
.decision.scale { border-color:rgba(53,201,143,.32); background:rgba(53,201,143,.06); }
.decision.scale b { color:var(--ink-good); }
.decision.watch { border-color:color-mix(in srgb, var(--blue) 30%, transparent); background:color-mix(in srgb, var(--blue) 6%, transparent); }
.decision.watch b { color:var(--ink-info); }

.kpi-strip { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
.kpi-chip { padding:7px 10px; border:1px solid var(--line); border-radius:9px; background:var(--lift-1); min-width:104px; }
.kpi-chip-label { display:block; color:var(--subtle); font-size:9px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; }
.kpi-chip-val { display:block; margin-top:4px; color:var(--text); font-size:13px; font-weight:750; font-variant-numeric:tabular-nums; }

.modal-backdrop { position:fixed; inset:0; z-index:50; background:rgba(3,6,11,.72); display:grid; place-items:center; padding:20px; }
.modal-backdrop[hidden] { display:none; }
.modal { width:min(640px,100%); max-height:88vh; overflow:auto; border:1px solid var(--line-strong); border-radius:16px; background:var(--panel); padding:20px; box-shadow:var(--shadow); }

/* Call log entry row */
.entry-row { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:10px; margin-top:14px; align-items:end; }
.entry-row .span-2 { grid-column:span 2; }
.entry-actions { display:flex; flex-direction:column; justify-content:flex-end; }
@media (max-width:1400px){ .entry-row { grid-template-columns:repeat(4,minmax(0,1fr)); } }
@media (max-width:900px){ .entry-row { grid-template-columns:repeat(2,minmax(0,1fr)); } }

/* Monthly KPI matrix */
.kpi-matrix { min-width: 1080px; }
.kpi-matrix th, .kpi-matrix td { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.kpi-matrix th:first-child, .kpi-matrix td:first-child { text-align:left; }
.kpi-label-col { position:sticky; left:0; z-index:2; background:var(--panel); min-width:250px; }
.kpi-matrix thead .kpi-label-col { background:var(--panel-2); }
.kpi-year-col { background:var(--lift-2); font-weight:750; color:var(--text); }
.kpi-matrix thead th .kpi-src, .kpi-matrix thead th .kpi-wait { display:block; margin-top:2px; font-size:9px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--subtle); }
.kpi-matrix thead th .kpi-wait { color:var(--amber); }
.kpi-empty { color:var(--subtle); opacity:.45; }
.kpi-matrix tbody tr { border-left:3px solid transparent; }
.kpi-group-value  td:first-child { box-shadow: inset 3px 0 0 var(--series-5); }
.kpi-group-rates  td:first-child { box-shadow: inset 3px 0 0 var(--series-1); }
.kpi-group-fu     td:first-child { box-shadow: inset 3px 0 0 var(--series-7); }
.kpi-group-front  td:first-child { box-shadow: inset 3px 0 0 var(--series-3); }
.kpi-group-resign td:first-child { box-shadow: inset 3px 0 0 var(--series-2); }

/* OfferOps wordmark: "Offer" in primary ink, "Ops" carrying the brand gradient */
.brand-copy strong { font-size: 16px; letter-spacing: -.03em; }
.brand-copy strong em {
  font-style: normal;
  background: linear-gradient(100deg, var(--blue), var(--violet));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Fallback for engines that ignore background-clip on text */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .brand-copy strong em { color: var(--violet); background: none; }
}
.brand-mark { border-radius: 11px; font-size: 17px; }

/* Dashboard hub */
.metric-link { cursor: pointer; transition: border-color .15s ease, transform .15s ease; }
.metric-link:hover { border-color: var(--line-strong); transform: translateY(-1px); }
.metric-link-cue { position:relative; z-index:1; margin-top:10px; color:var(--blue); font-size:11px; font-weight:700; }
.dot { display:inline-block; width:7px; height:7px; border-radius:50%; margin-left:6px; vertical-align:middle; }
.dot.good { background:var(--green); } .dot.warn { background:var(--amber); } .dot.bad { background:var(--red); }

.module-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; }
/* One row of six on a wide screen, so the Dashboard is one screen: metrics,
   modules, and the re-sign list under them without scrolling. */
@media (min-width:1360px) {
  .module-grid { grid-template-columns:repeat(6,minmax(0,1fr)); gap:10px; }
  .module-card { padding:14px 14px 12px; gap:10px; }
  .module-card h3 { font-size:13px; }
  .module-head { flex-direction:column; gap:6px; }
  .module-head .badge { align-self:flex-start; }
  /* Stats as three short lines -- value then label -- instead of three
     columns that no longer fit side by side at this width. */
  .module-stats { display:grid; gap:3px; padding-top:8px; }
  .module-stats > div { display:flex; align-items:baseline; gap:6px; min-width:0; }
  .module-stat-val { display:inline; font-size:14px; }
  .module-stat-label { display:inline; margin:0; font-size:9.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .module-stats > div { line-height:1.25; }
  .module-open { font-size:11.5px; margin-top:6px; }
  .module-card .card-sub { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; font-size:11px; }
  /* The Dashboard as one screen: tighter sections and shorter metric cards
     when there is the width for the six-across modules row. */
  .viz-root .section { margin-top:18px; }
  .viz-root .page-heading { margin-bottom:14px; }
  .viz-root .page-heading .lede { display:none; }
  #keyMetrics .metric { min-height:0; padding:14px 16px; }
  #keyMetrics .metric-value { margin-top:6px; font-size:21px; }
  #keyMetrics .metric-sub { margin-top:4px; }
  #keyMetrics .metric-link-cue { margin-top:6px; }
  #keyMetrics .bar-track { margin-top:6px !important; }
}
.module-card { display:flex; flex-direction:column; gap:14px; }
.module-card.is-idle { opacity:.6; }
.module-head { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }
.badge-good { border-color:rgba(53,201,143,.35); color:var(--ink-good); background:rgba(53,201,143,.10); }
.badge-warn { border-color:rgba(245,174,78,.35); color:var(--ink-warn); background:rgba(245,174,78,.10); }
.badge-idle { border-color:var(--line); color:var(--subtle); background:var(--lift-1); }
.badge-bad { border-color:rgba(251,100,114,.4); color:var(--ink-bad); background:rgba(251,100,114,.10); }
.module-stats { display:flex; gap:20px; flex-wrap:wrap; padding-top:12px; border-top:1px solid var(--line); }
.module-stat-val { display:block; color:var(--text); font-size:19px; font-weight:800; letter-spacing:-.03em; }
.module-stat-label { display:block; margin-top:3px; color:var(--subtle); font-size:10px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; }
.module-open { margin-top:auto; color:var(--blue); font-size:12px; font-weight:750; }
.module-open.is-muted { color:var(--subtle); }
@media (max-width:1200px){ .module-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:760px){ .module-grid { grid-template-columns:1fr; } }
.nav-group + .nav-group { margin-top:10px; }


/* ---- customisable sidebar ---------------------------------------------- */
.nav-label { display:flex; align-items:center; gap:6px; cursor:default; user-select:none; }
.nav-caret { font-size:9px; color:var(--subtle); width:10px; }
.nav-label-text { flex:1; }
.nav-grip { display:none; color:var(--subtle); cursor:grab; fill:currentColor;
  flex:0 0 10px; opacity:.55; }
.nav-grip:active { cursor:grabbing; }
.nav-section.collapsed .nav-items { display:none; }
.nav-label[data-toggle] { cursor:pointer; }
.nav-edit { width:100%; background:transparent; border:1px dashed var(--line);
  color:var(--subtle); border-radius:8px; padding:7px; font-size:10px; font-weight:700;
  letter-spacing:.06em; text-transform:uppercase; cursor:pointer; }
.nav-edit:hover { border-color:var(--line-strong); color:var(--muted); }
body.nav-editing .nav-grip { display:inline; }
body.nav-editing .nav-item { cursor:grab; }
body.nav-editing .nav-section { outline:1px dashed var(--line); border-radius:8px;
  padding:4px; margin-bottom:6px; }
.nav-drag-over { outline:1.5px solid var(--blue,#4387f5) !important; }
.nav-dragging { opacity:.4; }


/* ---- sidebar: scrolling nav, pinned footer ----------------------------- */
/* The menu is longer than a laptop viewport. Without this the Admin section
   is simply unreachable -- which is how Settings became invisible. */
#sidebarNav {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  /* Never sideways. The nav was 2px wider than its box, so it scrolled
     horizontally and drew a scrollbar along its foot -- faint until
     --line-strong got its proper strength in the dark theme, 10-02-2026. */
  overflow-x: hidden;
  display: flex; flex-direction: column; gap: 13px; padding-right: 2px;
}
#sidebarNav::-webkit-scrollbar { width: 6px; }
#sidebarNav::-webkit-scrollbar-thumb { background: var(--line-strong,var(--line-strong)); border-radius: 3px; }
#sidebarNav::-webkit-scrollbar-track { background: transparent; }

.sidebar-foot {
  flex: 0 0 auto; display: grid; gap: 6px; padding-top: 12px;
  border-top: 1px solid var(--line);
}
.account-chip {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 9px;
  text-decoration: none; color: var(--muted);
}
.account-chip:hover { background: var(--lift-2); }
.account-avatar {
  width: 26px; height: 26px; flex: 0 0 26px; border-radius: 8px; display: grid;
  place-items: center; font-size: 12px; font-weight: 800; color: #fff;
  background: linear-gradient(135deg, var(--blue), var(--violet));
}
.account-copy { min-width: 0; line-height: 1.15; }
.account-copy strong { display: block; font-size: 11px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-copy em { display: block; font-style: normal; font-size: 9px; color: var(--subtle);
  margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sign-out { font-size: 10px; color: var(--subtle); text-decoration: none; padding: 0 10px 4px; }
.sign-out:hover { color: var(--muted); }

/* Chevron points down when open, right when collapsed. */
.nav-caret { display: inline-flex; align-items: center; transition: transform .16s ease;
  color: var(--subtle); }
.nav-section.collapsed .nav-caret { transform: rotate(-90deg); }
.nav-label:hover .nav-caret { color: var(--muted); }
.sidebar.collapsed .sidebar-foot, .sidebar.collapsed .nav-caret { display: none; }


/* ---- rearranging the menu --------------------------------------------- */
/* Edit mode has to look different from normal, or a drag handle just reads as
   decoration and nobody discovers it. */
/* Handles stay hidden until you hover the row they belong to -- present when
   wanted, invisible the rest of the time, the way ClickUp does it. */
.nav-item, .nav-label { position: relative; }
.nav-grip { position:absolute; right:6px; top:50%; transform:translateY(-50%);
  opacity:0; transition:opacity .12s ease; }
.nav-item:hover .nav-grip, .nav-label:hover .nav-grip { display:inline-flex;
  align-items:center; opacity:.6; }
.nav-grip:hover { opacity:1 !important; color:var(--blue,#4387f5); }
.nav-label { cursor:pointer; user-select:none; border-radius:7px; padding-block:2px; font-size:10.5px; }
.nav-label:hover { background:var(--lift-2); }

/* While a drag is in flight, show every drop target. */
body.nav-rearranging .nav-grip { display:inline-flex; align-items:center; opacity:.45; }
body.nav-rearranging .nav-item { transition:transform .12s ease; }
.nav-dragging { opacity:.35; }
.nav-drag-over { background:color-mix(in srgb, var(--blue) 8%, transparent); border-radius:9px; }

/* ---- top bar actions --------------------------------------------------- */
.topbar-actions { display:flex; align-items:center; gap:6px; }

/* The clocks (common.js teamClock, app/clock.py). Folded: your own time, and
   Arizona's beside it when you are somewhere else. The arrow at the right end
   opens the rest -- the team's other zones into the same pill, the US zones
   in a pill of their own to its left -- and the clock you live by does not
   move when it does. Stefan, 10-02-2026: "I don't always need to see the
   other time zones just screaming in my face."

   The border is the logo's blue running into violet, kept light: it says the
   pills belong together without competing with the page. Quiet enough to sit
   there all day -- the place small and muted, the time in figures that do not
   jiggle as the minutes turn. */
.tb-clocks { display:flex; align-items:center; gap:6px; margin-right:6px; }
.tb-clocks[hidden], .tb-clock:empty { display:none; }
.tb-clock { display:flex; align-items:stretch; height:38px; border-radius:10px;
  border:1px solid transparent; box-shadow:var(--shadow-sm); cursor:default;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(100deg, color-mix(in srgb, var(--blue) 50%, var(--line)),
                    color-mix(in srgb, var(--violet) 55%, var(--line))) border-box; }
.tbc-cells { display:flex; align-items:stretch; }
.tbc-zone { display:flex; flex-direction:column; justify-content:center; gap:1px;
  padding:0 9px; line-height:1.1; }
.tbc-zone + .tbc-zone { border-left:1px solid var(--line); }
.tbc-place { font-style:normal; font-size:10px; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:var(--muted); white-space:nowrap; }
.tbc-zone b { font-size:13px; font-weight:650; color:var(--text);
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.tbc-day { font-weight:600; color:var(--muted); margin-right:3px; }
.tbc-head b { color:var(--muted); font-weight:600; }
/* Folded, the US pill waits behind the arrow. */
.tb-clocks:not(.is-open) .tb-clock-us { display:none; }
/* The arrow: the Brain's light wash, filling with the gradient on hover. It
   points at where the other zones come from, and turns round once they are
   out. */
.tbc-toggle { display:grid; place-items:center; width:26px; padding:0; border:0;
  border-left:1px solid var(--line); border-radius:0 9px 9px 0; cursor:pointer;
  color:var(--violet); font:inherit;
  background:linear-gradient(100deg, color-mix(in srgb, var(--blue) 10%, transparent),
                             color-mix(in srgb, var(--violet) 13%, transparent));
  transition:background .14s ease, color .14s ease; }
.tbc-toggle svg { transition:transform .2s ease; }
.tb-clocks.is-open .tbc-toggle svg { transform:rotate(180deg); }
.tbc-toggle:hover { color:#fff; background:linear-gradient(100deg, var(--blue-solid), var(--violet)); }
.tbc-toggle:focus-visible { outline:2px solid var(--blue); outline-offset:-3px; }
/* What the arrow opens slides in from its side -- once, on opening; the
   minute ticking over redraws the cells and must not replay it. */
@keyframes tbcIn { from { opacity:0; transform:translateX(8px); } to { opacity:1; transform:none; } }
.tb-clocks.tbc-opening .tb-clock-us, .tb-clocks.tbc-opening .tbc-extra { animation:tbcIn .22s ease both; }
@media (prefers-reduced-motion: reduce) {
  .tbc-toggle svg { transition:none; }
  .tb-clocks.tbc-opening .tb-clock-us, .tb-clocks.tbc-opening .tbc-extra { animation:none; }
}
/* Where the bar runs out of room the US pill goes first (common.js measures
   it, because the room depends on the rail, the guide and the page, not just
   the window), and a phone never has the room. */
.tb-clock-us.tbc-nofit { display:none; }
@media (max-width:680px) { .tb-clock-us { display:none; } }
/* A toast takes the clocks' place while it is up, rather than shoving the
   bar onto a second line; on a phone it sits under the bar instead. */
@media (min-width:701px) { .toast.show ~ .tb-clocks { display:none; } }

.campaign-chip { display:inline-flex; align-items:center; gap:8px; margin-right:12px;
  padding:5px 6px 5px 11px; border-radius:999px; font-size:11.5px; color:var(--muted);
  background:color-mix(in srgb, var(--blue) 10%, transparent); border:1px solid color-mix(in srgb, var(--blue) 32%, transparent); }
.campaign-chip b { color:var(--ink-info); font-weight:700; }
.campaign-chip button { width:20px; height:20px; border-radius:50%; border:0; padding:0;
  background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--ink-info); font-size:14px; line-height:1;
  cursor:pointer; }
.campaign-chip button:hover { background:var(--blue-solid); color:#fff; }

/* ---- the search box at the top of every page ------------------------- */
/* The pill is the .gsearch box itself; the input inside it has no border of
   its own. The magnifying glass sits in the pill's left padding, the Brain
   segment closes the right end, and the whole thing lights up as one when
   the box has focus. */
.gsearch { position:relative; flex:1 1 auto; max-width:520px; min-width:200px;
  display:flex; align-items:center; gap:4px; height:38px; padding:0 4px 0 12px;
  border:1px solid var(--line); border-radius:999px; background:var(--lift-1);
  transition:border-color .14s ease, background .14s ease, box-shadow .14s ease; }
.gsearch:focus-within, .gsearch.open { border-color:var(--blue); background:var(--panel);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.gsearch .gs-icon { flex:0 0 auto; color:var(--subtle); pointer-events:none;
  transition:color .14s ease; }
.gsearch.open .gs-icon, .gsearch:focus-within .gs-icon { color:var(--blue); }
#gsInput { flex:1 1 auto; min-width:0; width:auto; height:100%; padding:0 6px 0 8px;
  border:0; border-radius:0; background:transparent; color:var(--text);
  font-size:13px; box-shadow:none; }
#gsInput::placeholder { color:var(--subtle); }
#gsInput:focus { outline:none; box-shadow:none; }
#gsInput::-webkit-search-cancel-button { -webkit-appearance:none; }
.gs-kbd { flex:0 0 auto; padding:2px 6px; border-radius:5px;
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:10px;
  color:var(--subtle); border:1px solid var(--line); background:var(--panel);
  pointer-events:none; transition:opacity .14s ease; }
.gsearch:focus-within .gs-kbd { opacity:0; }
/* The segment at the right end of the pill. A hairline divides it from the
   box, it keeps the pill's curve on its outer side only, and it fills solid
   while the panel is open, so the bar says whether the Brain is up. */
.gs-brain { display:inline-flex; align-items:center; gap:6px; flex:0 0 auto;
  height:28px; margin-left:4px; padding:0 12px 0 11px; border:0;
  border-left:1px solid var(--line); border-radius:0 999px 999px 0;
  font:inherit; font-size:12px; font-weight:700; letter-spacing:.01em;
  color:var(--blue); cursor:pointer; white-space:nowrap;
  background:linear-gradient(100deg, color-mix(in srgb, var(--blue) 13%, transparent), color-mix(in srgb, var(--violet) 15%, transparent));
  transition:background .14s ease, color .14s ease; }
.gs-brain-ico { width:15px; height:15px; }
/* The wordmark's own gradient, on the Brain: the icon holds the blue end and
   the word runs into the violet, so the pill reads as the "Ops" in the logo
   rather than as one more blue chip. */
.gs-brain-label {
  background:linear-gradient(100deg, var(--blue), var(--violet));
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .gs-brain-label { color:var(--violet); background:none; }
}
.gs-brain:hover, .gs-brain[aria-expanded="true"] { color:#fff;
  background:linear-gradient(100deg, var(--blue-solid), var(--violet)); }
.gs-brain:hover .gs-brain-label, .gs-brain[aria-expanded="true"] .gs-brain-label {
  background:none; color:#fff; -webkit-text-fill-color:#fff; }
.gs-brain:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
@media (max-width:900px) { .gs-brain-label { display:none; } .gs-brain { padding:0 9px; } }

.gs-panel { position:absolute; top:calc(100% + 6px); left:0; right:0; z-index:40;
  max-height:min(60vh, 520px); overflow:auto; padding:6px; border-radius:13px;
  background:var(--panel); border:1px solid var(--line-strong);
  box-shadow:0 18px 44px -14px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.18); }
.gs-panel[hidden] { display:none !important; }
.gs-group + .gs-group { margin-top:4px; padding-top:4px; border-top:1px solid var(--line); }
.gs-group-label { padding:7px 10px 4px; font-size:9.5px; font-weight:800;
  letter-spacing:.11em; text-transform:uppercase; color:var(--subtle); }
.gs-item { display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  padding:8px 10px; border-radius:9px; border:0; background:transparent;
  color:var(--text); cursor:pointer; }
.gs-item.on { background:var(--lift-2); }
.gs-item.on .gs-label { color:var(--ink-info); }
.gs-kind { flex:0 0 auto; width:24px; height:24px; border-radius:7px; display:grid;
  place-items:center; font-size:10px; font-weight:800; letter-spacing:.02em;
  background:var(--lift-2); color:var(--muted); }
.gs-kind-client { background:rgba(53,201,143,.14); color:var(--ink-good); }
.gs-kind-campaign { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--ink-info); }
.gs-kind-page { background:var(--lift-2); color:var(--muted); }
.gs-kind-resource { background:rgba(139,92,246,.14); color:var(--ink-violet); }
.gs-kind-brain { background:var(--tint-blue); color:var(--blue); }
.gs-kind-brain svg { width:14px; height:14px; }
.gs-item.gs-ask { border-top:1px solid var(--line); margin-top:4px; padding-top:9px; border-radius:0 0 8px 8px; }
.gs-item.gs-ask .gs-label { color:var(--blue); }
.gs-body { display:flex; flex-direction:column; gap:1px; min-width:0; flex:1 1 auto; }
.gs-label { font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; }
.gs-label mark { background:transparent; color:var(--ink-info); font-weight:800; }
.gs-past { font-size:9.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--subtle); margin-left:4px; }
.gs-sub { font-size:11px; color:var(--subtle); white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; }
.gs-ext { color:var(--subtle); font-size:12px; flex:0 0 auto; }
.gs-empty { padding:16px 12px; font-size:12.5px; color:var(--subtle); text-align:center; }

@media (max-width:940px) {
  .gsearch { max-width:none; }
  .gs-kbd { display:none; }
}
@media (max-width:680px) {
  .topbar { flex-wrap:wrap; }
  .gsearch { order:3; flex-basis:100%; }
}
/* A pill you can see. These were transparent with a hairline border, so a row
   of them read as text with boxes drawn round it rather than as controls --
   and the selected one differed only by a pale wash. They now sit on the panel
   surface with a little lift, and the selected one is properly blue. */
.tb-btn { height:38px; min-width:38px; padding:0 12px; border:1px solid var(--line);
  border-radius:9px; background:var(--panel); color:var(--muted); display:inline-flex;
  align-items:center; justify-content:center; gap:7px; text-decoration:none;
  font-size:12px; font-weight:600; box-shadow:var(--shadow-sm);
  transition:background .14s ease, color .14s ease, border-color .14s ease,
             transform .14s ease, box-shadow .14s ease; }
.tb-btn:hover { background:var(--lift-2); color:var(--text);
  border-color:var(--line-strong); transform:translateY(-1px);
  box-shadow:var(--shadow); }
.tb-btn:active { transform:translateY(0); }
.tb-icon { font-size:17px; line-height:1; }
.tb-account { position:relative; margin-left:4px; }
.tb-account .account-avatar { width:38px; height:38px; flex:0 0 38px; border:0;
  cursor:pointer; font-size:14px; }
.tb-menu { position:absolute; right:0; top:42px; width:230px; z-index:30; padding:6px;
  background:var(--panel,#141b26); border:1px solid var(--line); border-radius:12px;
  box-shadow:var(--shadow-lg); display:grid; gap:2px; }
.tb-menu[hidden] { display:none; }
.tb-menu-head { padding:10px; border-bottom:1px solid var(--line); margin-bottom:4px;
  display:grid; gap:2px; }
.tb-menu-head strong { font-size:12px; color:var(--text); }
.tb-menu-head span { font-size:10px; color:var(--subtle); overflow:hidden;
  text-overflow:ellipsis; }
.tb-menu-head em { font-style:normal; font-size:10px; color:var(--blue,#4387f5); }
.tb-menu a, .tb-menu-btn { padding:8px 10px; border-radius:8px; font-size:12px; color:var(--muted);
  text-decoration:none; }
.tb-menu-btn { display:block; width:100%; text-align:left; border:0; background:transparent; font:inherit; font-size:12px; cursor:pointer; }
.tb-menu a:hover, .tb-menu-btn:hover { background:var(--lift-2); color:var(--text); }
.tb-menu a.danger:hover { color:var(--ink-bad); }

/* ---- the modal dialog ----------------------------------------------------
   Named for the Brain, which was the first thing to use it; now the frame
   behind every centred dialog in the app (client profile, save-a-document,
   end-of-day report). The Brain itself moved to a side panel below. */
.brain-overlay { position:fixed; inset:0; z-index:60; background:rgba(4,7,12,.62);
  backdrop-filter:blur(3px); display:flex; align-items:flex-start;
  justify-content:center; padding:78px 20px 24px; }
.brain-overlay[hidden] { display:none; }
.brain-panel { width:min(760px,100%); max-height:calc(100vh - 110px); display:flex;
  flex-direction:column; background:var(--panel,#141b26); border:1px solid var(--line);
  border-radius:15px; box-shadow:var(--shadow-lg); overflow:hidden; }
.brain-head { display:flex; align-items:center; gap:10px; padding:14px 16px;
  border-bottom:1px solid var(--line); }
.brain-head input { flex:1; background:transparent; border:0; color:var(--text);
  font-size:15px; padding:4px 0; }
.brain-head input:focus { outline:none; }
.brain-body { padding:0 16px 18px; overflow-y:auto; }

/* ---- the Brain ---------------------------------------------------------
   A panel on the right, over the page rather than instead of it. Styled as a
   conversation: your messages on the right in the accent, the Brain's on the
   left in the panel colour, the way every messaging app has taught everybody
   to read a thread. */
.bside { position:fixed; top:0; right:0; bottom:0; width:min(440px,100vw);
  z-index:65; display:flex; flex-direction:column; background:var(--panel);
  border-left:1px solid var(--line); box-shadow:-12px 0 40px -18px rgba(0,0,0,.35); }
.bside[hidden] { display:none; }
.bside-head { display:flex; align-items:center; gap:8px; padding:12px 14px;
  border-bottom:1px solid var(--line); flex:0 0 auto; }
.bside-head > .bside-ico { width:18px; height:18px; color:var(--blue); }
.bside-head b { font-size:14px; }
.bside-scope { font-size:11px; color:var(--subtle); }
.bside-spacer { flex:1 1 auto; }
.bside-close { border:0; background:none; font-size:22px; line-height:1; color:var(--subtle);
  cursor:pointer; padding:2px 6px; border-radius:7px; }
.bside-close:hover { color:var(--text); background:var(--lift-2); }
.bside-thread { flex:1 1 auto; min-height:0; overflow-y:auto; padding:16px 14px;
  display:flex; flex-direction:column; gap:10px; }
.bside-empty { margin:auto 0; text-align:center; color:var(--muted); display:flex;
  flex-direction:column; align-items:center; gap:12px; }
.bside-empty[hidden] { display:none; }
.bside-empty .bside-ico.big { width:34px; height:34px; color:var(--blue); }
.bside-empty p { margin:0; font-size:13px; max-width:34ch; line-height:1.5; }
.bside-hints { display:flex; flex-direction:column; gap:6px; width:100%; margin-top:4px; }
.bside-hint { font:inherit; font-size:12.5px; text-align:left; padding:9px 12px;
  border:1px solid var(--line); border-radius:10px; background:var(--lift-1);
  color:var(--muted); cursor:pointer; transition:border-color .14s ease, color .14s ease; }
.bside-hint:hover { border-color:var(--blue-line); color:var(--blue); }
.bside-msg { max-width:88%; padding:10px 13px; font-size:13.5px; line-height:1.5;
  border-radius:16px; overflow-wrap:anywhere; }
.bside-msg.user { align-self:flex-end; background:var(--blue-solid); color:var(--on-accent);
  border-bottom-right-radius:5px; }
.bside-msg.assistant { align-self:flex-start; background:var(--lift-1); color:var(--text);
  border:1px solid var(--line); border-bottom-left-radius:5px; }
.bside-msg p { margin:0 0 7px; } .bside-msg p:last-child { margin-bottom:0; }
.bside-msg ul, .bside-msg ol { margin:4px 0 6px; padding-left:18px; } .bside-msg li { margin:2px 0; }
.bside-msg.error { color:var(--ink-bad); }
/* Links in an answer: a guide section, a client card, a booking page to send. */
.bside-msg.assistant a { color:var(--blue); font-weight:600; text-decoration:underline;
  text-decoration-color:var(--blue-line); text-underline-offset:2px; }
.bside-msg.assistant a:hover { text-decoration-color:currentColor; }
/* A walkthrough the answer links to, playable in the bubble from its second. */
.bside-film { margin:10px 0 2px; border:1px solid var(--line); border-radius:10px;
  overflow:hidden; background:var(--sink-1); }
.bside-film video { display:block; width:100%; height:auto; background:#07090a; }
.bside-film-foot { display:flex; align-items:center; gap:8px; padding:6px 10px;
  font-size:11px; color:var(--muted); }
.bside-film-foot .gap { flex:1; }
.bside-looked { margin-top:8px; padding-top:6px; border-top:1px dashed var(--line);
  font-size:10.5px; color:var(--subtle); }
/* Three dots while it reads. */
.bside-msg.thinking { display:flex; gap:4px; padding:13px 15px; }
.bside-msg.thinking span { width:6px; height:6px; border-radius:50%; background:var(--subtle);
  animation:brainpulse 1.1s infinite ease-in-out; }
.bside-msg.thinking span:nth-child(2) { animation-delay:.15s; }
.bside-msg.thinking span:nth-child(3) { animation-delay:.3s; }
@keyframes brainpulse { 0%,80%,100% { opacity:.3; transform:translateY(0); }
  40% { opacity:1; transform:translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .bside-msg.thinking span { animation:none; opacity:.6; } }
.bside-compose { flex:0 0 auto; display:flex; align-items:flex-end; gap:8px;
  padding:10px 12px 12px; border-top:1px solid var(--line); background:var(--panel); }
.bside-compose textarea { flex:1 1 auto; margin:0; resize:none; min-height:40px; max-height:160px;
  padding:10px 14px; border-radius:20px; font:inherit; font-size:13.5px; line-height:1.4;
  border:1px solid var(--line); background:var(--field); color:var(--text); }
.bside-compose textarea:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.bside-send { flex:0 0 auto; width:40px; height:40px; border-radius:50%; border:0;
  background:var(--blue-solid); color:var(--on-accent); cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; }
.bside-send:disabled { opacity:.5; cursor:default; }
.bside-send:hover:not(:disabled) { filter:brightness(1.08); }
/* With the panel open, the page keeps a right margin so nothing important
   sits under it on a wide screen. On a narrow one the panel is the screen. */
@media (min-width:1100px) { body.brain-open main { padding-right:466px; } }
/* The brief panel moves the page over instead of sitting on top of it --
   the same arrangement as the Brain. Stefan, 09-23-2026: "it needs to push
   everything to the left, just like when we open up the brain." */
@media (min-width:1100px) { body.brief-open main { padding-right:466px; } }
.avatar.avatar-brain { color:var(--blue); display:inline-grid; place-items:center; }
.avatar.avatar-brain svg { width:60%; height:60%; }


/* Tooltips. The browser's own title= tooltip takes a second to appear and cannot
   be styled, which makes an icon-only toolbar feel like a guessing game. */
[data-tip] { position:relative; }
[data-tip]::after {
  content: attr(data-tip); position:absolute; top:calc(100% + 8px); left:50%;
  transform:translateX(-50%) translateY(-3px); white-space:nowrap;
  background:var(--field); color:var(--text); border:1px solid var(--line-strong,var(--line-strong));
  border-radius:7px; padding:5px 9px; font-size:11px; font-weight:600; line-height:1;
  opacity:0; pointer-events:none; transition:opacity .13s ease, transform .13s ease;
  box-shadow:var(--shadow); z-index:40;
}
[data-tip]::before {
  content:""; position:absolute; top:calc(100% + 3px); left:50%;
  transform:translateX(-50%); border:5px solid transparent;
  border-bottom-color:var(--line-strong,var(--line-strong)); opacity:0;
  transition:opacity .13s ease; pointer-events:none; z-index:41;
}
/* The call's own outcome, beside the status somebody set. */
.ap-outcome { padding:3px 9px; border-radius:999px; font-size:11px; font-weight:750;
  letter-spacing:.04em; text-transform:uppercase; background:var(--lift-2);
  color:var(--muted); white-space:nowrap; }
.ap-outcome.bad { background:color-mix(in srgb, var(--red) 15%, transparent);
  color:var(--ink-bad, var(--red)); }

/* A reaction's tooltip opens UPWARDS: the chips sit at the bottom of a
   bubble inside a pane that scrolls, and below them is the pane's edge. */
.msg-react[data-tip]::after { top:auto; bottom:calc(100% + 8px);
  transform:translateX(-50%) translateY(3px); }
.msg-react[data-tip]:hover::after, .msg-react[data-tip]:focus-visible::after {
  transform:translateX(-50%) translateY(0); }
.msg-react[data-tip]::before { display:none; }

[data-tip]:hover::after, [data-tip]:focus-visible::after {
  opacity:1; transform:translateX(-50%) translateY(0);
}
[data-tip]:hover::before, [data-tip]:focus-visible::before { opacity:1; }
/* AND LIFT THE ELEMENT ITSELF, or the tip is painted over by whatever comes
   after it on the page. Our buttons rise 1px on hover, and a transform makes
   a stacking context -- so the ::after's z-index:40 is scoped to the button
   and cannot climb over a later `position:relative` sibling. On the check-ins
   page that sibling is the row of metric tiles, and the tooltip was sliced in
   half by the first one. Raising the hovered element fixes it for every tip
   on the app at once; it only ever applies while the pointer is on it. */
[data-tip]:hover, [data-tip]:focus-visible { z-index:42; }
/* AND WHEN THE TIP IS INSIDE SOMETHING THAT SCROLLS, z-index cannot save it:
   the ancestor CLIPS. `.table-wrap` is overflow-x:auto, and a box with one
   axis scrollable has the other computed to auto too, so every tooltip in
   every table in this app is cut off at the edge of the table. On the
   check-ins list there were 8px of room below the button and the tip wanted
   64. common.js moves those tips to this layer, which hangs off <body> and is
   clipped by nothing. */
.tip-layer { position:fixed; z-index:90; max-width:min(300px, 78vw);
  background:var(--field); color:var(--text); border:1px solid var(--line-strong);
  border-radius:7px; padding:5px 9px; font-size:11px; font-weight:600;
  line-height:1.4; box-shadow:var(--shadow); pointer-events:none;
  white-space:nowrap; }
.tip-layer.wide { white-space:normal; text-align:left; line-height:1.45;
  font-weight:500; padding:8px 10px; }
.tip-layer[hidden] { display:none; }
/* The element's own ::after stands down while the layer has it. */
[data-tip].tip-off::after, [data-tip].tip-off::before { display:none; }
/* The rightmost buttons would otherwise push their tooltip off-screen. */
.topbar-actions > *:last-child [data-tip]::after,
.tb-account [data-tip]::after { left:auto; right:0; transform:translateY(-3px); }
.tb-account [data-tip]:hover::after { transform:translateY(0); }
.tb-account [data-tip]::before { left:auto; right:14px; transform:none; }
/* A TIP THAT IS AN EXPLANATION, not a label. nowrap is right for an icon in a
   toolbar and wrong for a sentence, which has to wrap or it runs off the
   window -- so this one wraps, is left-aligned like prose, and is capped at a
   readable measure rather than the viewport. */
[data-tip].tip-wide::after { white-space:normal; width:max-content;
  max-width:min(300px, 78vw); text-align:left; line-height:1.45;
  font-weight:500; padding:8px 10px; }
/* Hung from the button's right edge, for a tip on a right-aligned row where a
   centred one would hang off the page. */
[data-tip].tip-wide.tip-left::after { left:auto; right:0; transform:translateY(-3px); }
[data-tip].tip-wide.tip-left:hover::after,
[data-tip].tip-wide.tip-left:focus-visible::after { transform:translateY(0); }
[data-tip].tip-wide.tip-left::before { left:auto; right:16px; transform:none; }

/* The Brain's tip is a sentence. Centred under a 76px button it hung 160px
   past the pill on each side, and the right-hand overhang gave a 1024px
   window a horizontal scrollbar even with the tip invisible. Hung from the
   pill's right end it runs back under the search box instead. */
.gs-brain[data-tip]::after { left:auto; right:0; transform:translateY(-3px); }
.gs-brain[data-tip]:hover::after, .gs-brain[data-tip]:focus-visible::after { transform:translateY(0); }
.gs-brain[data-tip]::before { left:auto; right:30px; transform:none; }


/* ---- reveal toggles on secret fields ----------------------------------- */
.secret-wrap { position:relative; display:block; }
.secret-wrap input { width:100%; padding-right:38px; }
.secret-eye {
  position:absolute; right:6px; top:50%; transform:translateY(-50%);
  width:28px; height:28px; display:grid; place-items:center; cursor:pointer;
  border:0; background:transparent; color:var(--subtle); border-radius:7px;
}
.secret-eye:hover { color:var(--text); background:var(--lift-2); }
.secret-eye svg { width:16px; height:16px; fill:none; stroke:currentColor;
  stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }

/* ---- client money panel + finance page ---- */
.money-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:10px; }
.money-grid label { display:flex; flex-direction:column; gap:4px; font-size:11px;
  color:var(--muted); text-transform:uppercase; letter-spacing:.04em; }
.money-grid input, .money-grid select { width:100%; }
.tab-row { display:flex; gap:7px; border-bottom:1px solid var(--line);
  padding-bottom:10px; flex-wrap:wrap; }
/* The one you are on is filled, not tinted — at a glance the row should have an
   obvious current tab rather than one slightly bluer rectangle. */
.tab-row .tb-btn.active { background:var(--blue-solid); color:#fff;
  border-color:var(--blue-solid);
  box-shadow:0 2px 10px color-mix(in srgb, var(--blue-solid) 40%, transparent); }
.tab-row .tb-btn.active:hover { background:var(--blue-solid); color:#fff;
  border-color:var(--blue-solid); transform:translateY(-1px); }
.tab-pane { margin-top:12px; }
/* The two inputs and the button sit on one baseline. They did not: the button
   carried a class this app does not define, so it rendered at the browser's
   own height and looked out of line with fields it is meant to sit beside. */
.fin-top { display:flex; gap:10px; align-items:flex-end; margin-bottom:14px;
  flex-wrap:wrap; }
.fin-top .button { height:38px; padding-inline:16px; }
/* align-items:flex-end lines up MARGIN boxes, and the generic label rule puts
   5px under every label — so the fields sat five pixels above the button they
   are meant to share a baseline with. */
.fin-top .cash-input { margin-bottom:0; }
.cash-input { display:flex; flex-direction:column; gap:4px; font-size:11px;
  color:var(--muted); text-transform:uppercase; letter-spacing:.04em;
  font-weight:700; }
.cash-input input { height:38px; }
.caveat { border-left:3px solid var(--warn, #d9a441); background:rgba(217,164,65,.08);
  padding:10px 14px; border-radius:4px; font-size:12.5px; margin-bottom:16px; }
.grid { width:100%; border-collapse:collapse; font-size:12.5px; }
.grid th, .grid td { padding:7px 10px; border-bottom:1px solid var(--line); text-align:left; }
.grid th { font-size:10.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); }
.grid .num, .grid th.num { text-align:right; font-variant-numeric:tabular-nums; }
.grid tr.neg td { background:rgba(220,80,80,.07); }
/* A table inside the client card is narrower than the card, and a wide one
   was being clipped by it rather than scrolling -- the last column lost its
   heading. */
.pay-wrap { overflow-x:auto; max-width:100%; }
.pay-wrap .grid th, .pay-wrap .grid td { white-space:nowrap; }
.pay-wrap .grid td.muted { white-space:normal; }

/* ---- the client card's Payments pane (csm.html, 10-02-2026) --------------
   Money only, top to bottom: where it stands, one summary, the schedule with
   its tick, the terms in a sentence, the lifetime figure in a line. It was
   a lifetime headline, a table with a totals footer, an "owed" box saying
   the same totals, a second client card's worth of notes and contact
   details, and a standing box with three buttons at the bottom. */
.pay-top { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:12px; }
.pay-standing { position:relative; }
.pay-pill { display:inline-flex; align-items:center; gap:6px; height:28px; padding:0 11px;
  border-radius:999px; font:inherit; font-size:12px; font-weight:700; cursor:pointer;
  border:1px solid var(--line); background:var(--panel); color:var(--subtle); }
.pay-pill-dot { width:7px; height:7px; border-radius:50%; background:currentColor; }
.pay-pill.ok { color:var(--ink-good); background:var(--tint-green);
  border-color:color-mix(in srgb, var(--ink-good) 35%, transparent); }
.pay-pill.bad { color:var(--ink-bad); background:var(--tint-red);
  border-color:color-mix(in srgb, var(--ink-bad) 35%, transparent); }
.pay-pill:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
/* The payment bar's gradient as an outline -- the same as the Contract &
   dates actions. Stefan, 10-02-2026: "do the same outline on the payment
   pills and record a payment." On the standing pill, + Record a payment
   and Edit -- the pane's three controls you press, so they read as buttons
   -- and on Send an agreement and Upload a signed copy on Contract & dates.
   --go-fill is the inside, so the pill keeps its green or red when there is
   an answer. A second background clipped to the border box is how a
   gradient becomes a border. */
.button.grad-outline, .pay-pill.grad-outline, .chip.grad-outline { --go-fill:var(--panel);
  border:1.5px solid transparent; color:var(--text);
  background:linear-gradient(var(--go-fill), var(--go-fill)) padding-box,
             linear-gradient(90deg, var(--blue), var(--violet)) border-box; }
.button.grad-outline:hover, .pay-pill.grad-outline:hover, .chip.grad-outline:hover {
  --go-fill:color-mix(in srgb, var(--blue) 7%, var(--panel)); color:var(--text); }
.button.grad-outline[aria-expanded="true"], .pay-pill.grad-outline[aria-expanded="true"],
.chip.grad-outline[aria-expanded="true"] {
  --go-fill:color-mix(in srgb, var(--violet) 13%, var(--panel)); color:var(--blue-on-tint, var(--blue)); }
.pay-pill.grad-outline.unknown { color:var(--muted); }
.pay-pill.grad-outline.ok { --go-fill:color-mix(in srgb, var(--ink-good) 13%, var(--panel)); color:var(--ink-good); }
.pay-pill.grad-outline.bad { --go-fill:color-mix(in srgb, var(--ink-bad) 13%, var(--panel)); color:var(--ink-bad); }
.pay-menu { position:absolute; top:calc(100% + 6px); left:0; z-index:30; min-width:270px;
  padding:5px; border-radius:12px; border:1px solid var(--line-strong); background:var(--panel);
  box-shadow:var(--shadow); display:grid; gap:2px; }
.pay-menu[hidden] { display:none; }
.pay-menu button { font:inherit; font-size:12.5px; font-weight:600; text-align:left; padding:8px 10px;
  border:0; border-radius:8px; background:none; color:var(--text); cursor:pointer; }
.pay-menu button small { display:block; font-size:11px; font-weight:400; color:var(--muted); margin-top:2px; }
.pay-menu button:hover, .pay-menu button:focus-visible { background:var(--panel-2); outline:none; }
.pay-menu button.on::after { content:" ✓"; color:var(--blue); }

.pay-sum { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr));
  border:1px solid var(--line); border-radius:12px; overflow:hidden; }
/* Four with Coaching left -- the months still owed and what is already paid
   for them, from the per-term ledger (10-03-2026). */
.pay-sum.four { grid-template-columns:repeat(4, minmax(0, 1fr)); }
.pay-sum > div { padding:11px 14px; min-width:0; }
.pay-sum > div + div { border-left:1px solid var(--line); }
.pay-sum-k { margin:0; font-size:9.5px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }
.pay-sum-v { margin:2px 0 0; font-size:18px; font-weight:750; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums; }
.pay-sum-s { margin:1px 0 0; font-size:11.5px; color:var(--muted); }
.pay-sum .is-late { color:var(--ink-bad); }
.pay-sum > .pay-bar { grid-column:1 / -1; height:5px; padding:0; border-left:0; background:var(--line); }
.pay-bar i { display:block; height:100%; background:linear-gradient(90deg, var(--blue), var(--violet));
  transition:width .3s ease; }
@media (prefers-reduced-motion: reduce) { .pay-bar i { transition:none; } }

.pay-sched-h { display:flex; align-items:center; justify-content:space-between; gap:10px; margin:18px 0 6px; }
.pay-sched-h h4, .pay-terms h4 { margin:0; font-size:11px; font-weight:750; letter-spacing:.06em;
  text-transform:uppercase; color:var(--text); }
.pay-rec { margin:0 0 10px; padding:10px 12px; border:1px solid var(--line); border-radius:10px;
  background:var(--panel-2); }
.pay-rec .money-grid { margin-top:0; }
.pay-got { color:var(--ink-good); font-weight:650; }
.pay-mark { height:26px; padding:0 10px; font-size:11.5px; }
.pay-terms { margin-top:20px; padding-top:18px; border-top:1px solid var(--line-strong); }
.pay-terms-row { display:flex; align-items:flex-start; gap:12px; }
.pay-terms-text { flex:1; min-width:0; }
.pay-terms-text p { margin:4px 0 0; }
.pay-terms-plan { color:var(--text); font-weight:600; }
/* The onboarding text sits under the agreement and reads the same way: a
   heading, a line saying where it stands, and the one action on the right.
   A thinner rule above it than the agreement's, because the two belong to
   the same end-of-close block rather than being separate pages. */
.pay-onb { margin-top:16px; padding-top:14px; border-top:1px solid var(--line); }
.pay-onb .confirm-row { display:flex; align-items:center; gap:8px;
  margin-top:10px; font-size:12.5px; }
.pay-onb .confirm-row[hidden] { display:none; }
.pay-onb .confirm-label { flex:1 1 auto; min-width:0; color:var(--text); }
#csmContractForm { margin-top:12px; }
.pay-ltv { margin:16px 0 0; font-size:12px; color:var(--muted); }
.pay-ltv b { color:var(--text); font-weight:650; }
.pay-ltv a { color:var(--blue); }
@media (max-width:520px) {
  .pay-sum, .pay-sum.four { grid-template-columns:minmax(0, 1fr); }
  .pay-sum > div + div { border-left:0; border-top:1px solid var(--line); }
}

/* Message client: whether we may reach them, and the calls we have had,
   above the thread (moved from Payments, 10-02-2026). */
.reach-top { display:grid; gap:8px; margin-bottom:12px; }
/* Plans & files and Coach on the client card: a one-line lead with the
   pane's one action beside it, then the list. Stefan, 10-02-2026. */
.cf-top { display:flex; align-items:center; gap:10px; margin:0 0 10px; }
.cf-top .cc-lead { flex:1 1 auto; min-width:0; }
.cf-top .button { flex:0 0 auto; }
.cf-top .button[hidden] { display:none; }
.cf-msg { margin:0 0 8px; }
.cf-msg[hidden] { display:none; }
.cf-list { border-top:1px solid var(--line); }
.cf-row { position:relative; border-radius:0; padding:9px 6px; }
.cf-type { flex:0 0 34px; height:24px; display:grid; place-items:center; border-radius:6px;
  font-size:9.5px; font-weight:800; letter-spacing:.04em; color:var(--muted);
  background:var(--lift-2, rgba(0,0,0,.05)); }
.cf-type.pdf { color:var(--ink-bad); background:color-mix(in srgb, var(--ink-bad) 13%, transparent); }
.cf-type.img { color:var(--ink-good); background:color-mix(in srgb, var(--ink-good) 13%, transparent); }
.cf-type.doc { color:var(--blue); background:color-mix(in srgb, var(--blue) 13%, transparent); }
.cf-type.xls { color:var(--ink-good); background:color-mix(in srgb, var(--ink-good) 9%, transparent); }
.cf-type.ai { color:var(--ink-violet); background:color-mix(in srgb, var(--violet) 15%, transparent); }
.cf-more { flex:0 0 auto; width:30px; height:28px; border-radius:7px; border:0; background:none;
  color:var(--muted); font-size:14px; font-weight:800; letter-spacing:1px; cursor:pointer; }
.cf-more:hover, .cf-more[aria-expanded="true"] { background:var(--lift-2, rgba(0,0,0,.05)); color:var(--text); }
.cf-menu { position:absolute; right:6px; top:calc(100% - 4px); z-index:5; min-width:170px; padding:5px;
  display:grid; background:var(--panel); border:1px solid var(--line); border-radius:10px; box-shadow:var(--shadow-sm); }
.cf-menu a, .cf-menu button { display:block; width:100%; padding:7px 10px; border:0; border-radius:7px;
  background:none; color:var(--text); font:inherit; font-size:12.5px; text-align:left; text-decoration:none; cursor:pointer; }
.cf-menu a:hover, .cf-menu button:hover { background:var(--lift-2, rgba(0,0,0,.05)); }
.cf-menu .is-danger { color:var(--ink-bad); }
.cf-foot { display:flex; align-items:center; gap:10px; margin-top:10px; font-size:12px; }
.cf-foot:empty { display:none; }
.cf-trash-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:0 0 8px; }
.cf-empty { display:block; width:100%; padding:26px 14px; border:1.5px dashed var(--line);
  border-radius:12px; background:none; color:var(--muted); font:inherit; font-size:12.5px; cursor:pointer; }
.cf-empty:hover { border-color:color-mix(in srgb, var(--blue) 45%, transparent); color:var(--text); }
/* While a file is dragged over the pane: the whole pane is the target. */
.cf-pane { position:relative; }
.cf-drop { display:none; }
.cf-pane.over .cf-drop { display:grid; place-items:center; position:absolute; inset:-6px; z-index:6;
  border:2px dashed var(--blue); border-radius:14px; pointer-events:none;
  background:color-mix(in srgb, var(--blue) 8%, var(--panel)); color:var(--blue); font-weight:700; font-size:13px; }
#tfForm { margin-top:12px; }

/* The composer, one shape everywhere on the client card -- the Prospects
   Inbox's: the box across the width, then one row with what you need to
   know on the left and the tools and Send on the right. Internal notes, a
   thread reply and Message client all use it. Stefan, 10-02-2026. */
.cc-box .msg-box-row { width:100%; gap:6px; }
.cc-hint { flex:1 1 auto; min-width:0; margin:0; font-size:11px; color:var(--subtle);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cc-hint.ink-bad { color:var(--ink-bad); white-space:normal; }
.cc-tool { display:inline-flex; align-items:center; gap:5px; flex:0 0 auto; height:32px;
  padding:0 11px; border-radius:9px; background:var(--panel); color:var(--muted);
  border:1px solid color-mix(in srgb, var(--subtle) 35%, transparent);
  font-size:12px; font-weight:650; cursor:pointer; }
.cc-tool:hover { color:var(--text); border-color:color-mix(in srgb, var(--subtle) 60%, transparent); }
.cc-tool[hidden] { display:none; }
.cc-box .msg-box-row .button { height:32px; padding:0 14px; }
.cc-box .att-chips { margin:0; }
.cc-lead { flex:1; margin:0; }

/* THE LAST TWELVE WEEKS, on the client card between the count and the list of
   check-ins: one dot a week, oldest on the left, in the same three colours
   the review screen uses. A run of ambers is the thing a coach needs before
   a call and the one thing a single week cannot show.

   Two kinds of empty, deliberately not the same: a solid hollow ring is a
   week that came in and nobody has coloured yet (something to do), a dashed
   one is a week nothing came in at all (silence, not a bad week).

   No transform on hover. The lift elsewhere on the card sliced tooltips in
   half, so the hover state is a ring. */
.cc-weeks { margin:2px 0 14px; }
.cc-weeks-h { display:flex; align-items:baseline; gap:8px; margin-bottom:7px; }
.cc-weeks-h b { font-size:9.5px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }
.cc-weeks-h .helper { font-size:11px; }
.cc-weeks-row { display:flex; flex-wrap:wrap; gap:6px; }
.cc-week { width:18px; height:18px; border-radius:50%; flex:0 0 auto;
  border:2px solid var(--c, var(--subtle)); background:none; }
.cc-week:hover { box-shadow:0 0 0 3px color-mix(in srgb, var(--c, var(--subtle)) 24%, transparent); }
.cc-week:focus-visible { outline:2px solid var(--c, var(--subtle)); outline-offset:2px; }
.cc-week.c-green  { --c:var(--sw-green); background:var(--c); }
.cc-week.c-yellow { --c:var(--sw-amber); background:var(--c); }
.cc-week.c-red    { --c:var(--sw-red);   background:var(--c); }
.cc-week.c-none   { --c:color-mix(in srgb, var(--subtle) 70%, transparent); }
.cc-week.c-grey   { --c:color-mix(in srgb, var(--subtle) 45%, transparent);
  border-style:dashed; }

#reachMemo .ws-memo { margin:0; }
.reach-subject { width:100%; margin-bottom:6px; padding:8px 10px; border-radius:9px;
  border:1px solid var(--line); background:var(--panel); color:var(--text); font:inherit; font-size:12.5px; }
/* The coach on the agreement editor once one is assigned: shown, not picked. */
.cp-fixed { display:flex; align-items:center; gap:10px; padding:8px 0 2px; font-size:12.5px; }
.grid td.pos { color:var(--good, #4caf7d); }
.grid td.neg { color:var(--bad, #e2635f); }
.missing-note { font-size:11.5px; color:var(--muted); line-height:1.5; padding:10px; }
.two-col { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
@media (max-width:900px){ .two-col, .money-grid { grid-template-columns:1fr; } }
.cost-add { display:flex; gap:8px; margin-top:10px; flex-wrap:wrap; }
.cost-add input { flex:1; min-width:110px; }
.stat-row { display:flex; gap:14px; flex-wrap:wrap; margin-bottom:16px; }
.stat { border:1px solid var(--line); border-radius:11px;
  padding:12px 15px; min-width:150px;
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  box-shadow:var(--shadow); }
.stat .k { display:block; font-size:10.5px; text-transform:uppercase;
  letter-spacing:.05em; color:var(--muted); }
.stat .v { display:block; font-size:19px; font-weight:700; margin-top:3px; }
.pill { font-size:10px; padding:1px 6px; border-radius:9px; background:var(--tint-blue);
  color:var(--blue); vertical-align:middle; }
.link-btn { background:none; border:none; color:var(--muted); cursor:pointer;
  font-size:11px; text-decoration:underline; padding:0; }
.link-btn.danger:hover { color:var(--bad, #e2635f); }

/* It sits inside main now (base.html), so it lines up with the content
   column instead of being centred under the sidebar as well. Stefan,
   10-13-2026: "a lot of blank space here and wasted space ... make the
   terms and privacy bar smaller". It was 136px of mostly air -- 36 under
   the page, 34 of margin, 67 of padding around one 15px line. */
.app-footer { display:flex; flex-wrap:wrap; gap:2px 14px; align-items:center; justify-content:center;
  width:100%; max-width:1520px; margin:0 auto; padding:10px 0 12px;
  font-size:11px; color:var(--muted); border-top:1px solid var(--line); }
/* Three short things, each either on the line or not. Left to wrap inside
   themselves on a phone they came out as "Terms &" / "privacy" over two
   lines and the whole bar grew to 90px. */
.app-footer > * { white-space:nowrap; }
@media (max-width:680px) { .app-footer { font-size:10.5px; gap:2px 12px; padding:9px 0 10px; } }
.app-footer a { color:var(--muted); text-decoration:underline; }
.app-footer a:hover { color:var(--blue); }

/* Settings cards are short; stacking them one per row left most of the page
   empty and pushed the useful controls below the fold. */
.settings-grid { display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  gap:14px; align-items:start; }
.settings-grid > .card { margin-top:0 !important; }
@media (max-width:1100px){ .settings-grid { grid-template-columns:1fr; } }

#build-notice { position:fixed; left:50%; transform:translateX(-50%); bottom:22px;
  z-index:9999; display:flex; align-items:center; gap:14px; padding:12px 14px 12px 18px;
  border-radius:12px; background:var(--panel,var(--panel-2)); color:var(--text,#eef1f6);
  border:1px solid var(--line,var(--line)); box-shadow:var(--shadow-lg);
  font-size:13px; max-width:min(560px, calc(100vw - 32px)); }
#build-notice .bn-body { display:flex; flex-direction:column; gap:2px; min-width:0; }
#build-notice .bn-meta { font-size:11px; color:var(--muted,#8a93a6); }
#build-notice .bn-go { border:0; border-radius:8px; padding:8px 14px; cursor:pointer;
  background:var(--blue-solid); color:#fff; font-weight:700; font-size:12px;
  white-space:nowrap; }
#build-notice .bn-later { border:0; background:transparent; color:var(--muted,#8a93a6);
  font-size:18px; line-height:1; cursor:pointer; padding:0 4px; }
#build-notice .bn-later:hover { color:var(--text,#eef1f6); }

/* build marker */

/* Favourite stars: invisible until the row is hovered, so the sidebar stays
   quiet, but always visible once something IS a favourite. */
.nav-star { margin-left:auto; width:20px; height:20px; padding:0; border:0;
  background:transparent; color:var(--subtle,#6b7280); opacity:0; cursor:pointer;
  display:grid; place-items:center; border-radius:5px; flex:0 0 auto;
  transition:opacity .13s ease, color .13s ease; }
.nav-item:hover .nav-star { opacity:.55; }
.nav-star:hover { opacity:1 !important; color:var(--warn,#f5ae4e); }
.nav-star.on { opacity:1; color:var(--warn,#f5ae4e); }
/* The star follows the label (the row's right padding keeps it clear of the
   drag handle), a touch inside the row's own gap so it reads as part of the
   word rather than a separate control. */
.nav-star { margin-left:-4px; margin-right:0; }

/* A red count on a menu entry: Tasks carries the tasks handed to you that
   you have not looked at. Sits before the star; the star only shows on
   hover, and the count must not jump when it does. */
.nav-badge { flex:0 0 auto; min-width:18px; height:18px; padding:0 5px; border-radius:9px;
  background:var(--red,#fb6472); color:#fff; font-size:10px; font-weight:800; line-height:18px;
  text-align:center; font-variant-numeric:tabular-nums; margin-left:-3px; }
/* In the 64px rail there is no row to sit in, so it rides the icon's corner. */
.sidebar.collapsed .nav-badge { position:absolute; top:2px; right:2px; min-width:16px; height:16px;
  line-height:16px; font-size:9px; padding:0 4px; margin:0; box-shadow:0 0 0 2px var(--sidebar); }

/* Icon picker */
.nav-icon-btn { border:0; background:transparent; padding:0; cursor:pointer;
  font-size:15px; line-height:1; color:inherit; border-radius:5px; }
.nav-icon-btn:hover { background:var(--lift-1); }
#icon-picker { position:fixed; z-index:10000; width:330px; padding:10px;
  border-radius:12px; background:var(--panel,var(--panel-2)); color:var(--text,#eef1f6);
  border:1px solid var(--line,var(--line)); box-shadow:var(--shadow-lg);
  display:flex; flex-direction:column; gap:8px; }
.ip-search { width:100%; font-size:13px; padding:7px 10px; }
.ip-tabs { display:flex; gap:4px; overflow-x:auto; padding-bottom:2px;
  scrollbar-width:none; }
.ip-tabs::-webkit-scrollbar { display:none; }
.ip-tab { flex:0 0 auto; border:0; background:transparent; cursor:pointer;
  font-size:10.5px; font-weight:700; letter-spacing:.02em; padding:4px 8px;
  border-radius:6px; color:var(--muted,#8a93a6); white-space:nowrap; }
.ip-tab:hover { background:var(--lift-3); color:var(--text,#eef1f6); }
.ip-tab.on { background:var(--accent-soft,color-mix(in srgb, var(--blue) 18%, transparent)); color:var(--blue,#4387f5); }
.ip-scroll { height:250px; overflow-y:auto; overscroll-behavior:contain;
  padding-right:2px; }
.ip-scroll h5 { position:sticky; top:0; margin:6px 0 4px; padding:4px 2px;
  font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--subtle,#6b7280); background:var(--panel,var(--panel-2)); }
.ip-grid { display:grid; grid-template-columns:repeat(8,1fr); gap:2px; }
.ip-one { border:0; background:transparent; font-size:19px; padding:4px 0;
  border-radius:7px; cursor:pointer; line-height:1.15; }
.ip-one:hover { background:var(--lift-1); transform:scale(1.15); }
.ip-none { font-size:12px; color:var(--muted,#8a93a6); padding:14px 4px; }
.ip-reset { width:100%; border:0; background:transparent; cursor:pointer;
  color:var(--muted,#8a93a6); font-size:11px; padding:5px;
  text-decoration:underline; }
.ip-reset:hover { color:var(--text,#eef1f6); }

/* ---- AI tools ---- */
.tool-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:14px; }
.tool-card { display:flex; gap:14px; align-items:flex-start; padding:16px;
  border-radius:12px; background:var(--panel); border:1px solid var(--line);
  text-decoration:none; color:inherit; transition:border-color .15s, transform .15s; }
.tool-card:hover { border-color:var(--blue,#4387f5); transform:translateY(-2px); }
.tool-avatar { width:42px; height:42px; flex:0 0 42px; border-radius:12px;
  display:grid; place-items:center; font-size:20px;
  background:linear-gradient(135deg, var(--blue,#4387f5), var(--violet,#8b5cf6));
  box-shadow:0 8px 20px rgba(79,70,229,.28); }
.tool-body { display:flex; flex-direction:column; gap:3px; min-width:0; }
.tool-body strong { font-size:14.5px; }
.tool-blurb { font-size:12px; color:var(--muted); }
.tool-meta { font-size:10.5px; color:var(--subtle); margin-top:4px; }
.dropzone { border:1.5px dashed var(--line); border-radius:12px; padding:26px;
  text-align:center; display:flex; flex-direction:column; gap:5px;
  transition:border-color .15s, background .15s; margin-top:12px; }
.dropzone.over { border-color:var(--blue,#4387f5); background:color-mix(in srgb, var(--blue) 6%, transparent); }

/* AI tools ------------------------------------------------------------------
   Built to feel like sitting in front of Claude rather than filling in a form:
   a rail of past chats grouped by when you had them, a thread that scrolls on
   its own, and a composer pinned to the bottom that grows with what you type.
   It borrows the Messages bubbles deliberately -- a conversation with an
   assistant should read like every other conversation in here. */
.ai-shell { display:grid; grid-template-columns:262px minmax(0,1fr); gap:16px;
  align-items:stretch; height:calc(100vh - 196px); min-height:480px; }
@media (max-width:900px){
  .ai-shell { grid-template-columns:1fr; height:auto; }
  .ai-rail { max-height:220px; }
  .ai-log { min-height:50vh; }
}

.ai-rail { background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); display:flex; flex-direction:column;
  min-height:0; overflow:hidden; }
.ai-rail-top { padding:10px 10px 6px; }
.ai-rail-list { flex:1; overflow-y:auto; padding:4px 8px 8px;
  scrollbar-width:thin; }
.ai-rail-foot { border-top:1px solid var(--line); padding:8px; }
.ai-rail-empty { padding:10px 10px 0; }

.ai-new { width:100%; display:flex; align-items:center; gap:8px;
  padding:9px 12px; border-radius:11px; cursor:pointer; font-size:12.5px;
  font-weight:600; color:#fff; border:1px solid transparent;
  background:linear-gradient(135deg, var(--accent-fill-2), var(--blue-solid));
  box-shadow:0 4px 14px color-mix(in srgb, var(--blue) 28%, transparent);
  transition:transform .12s ease, box-shadow .15s ease; }
.ai-new:hover { box-shadow:0 6px 18px color-mix(in srgb, var(--blue) 40%, transparent); }
.ai-new:active { transform:scale(.985); }

.ai-rail-group { font-size:9.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--subtle); padding:12px 10px 5px; }
.ai-rail-group:first-child { padding-top:4px; }
.ai-proj { width:100%; display:flex; flex-direction:column; gap:2px;
  padding:8px 10px; border:0; background:transparent; color:inherit;
  cursor:pointer; border-radius:10px; text-align:left; position:relative;
  transition:background .15s ease, transform .12s ease; }
.ai-proj:hover { background:var(--lift-2); }
.ai-proj:active { transform:scale(.985); }
.ai-proj.on { background:linear-gradient(90deg,
  color-mix(in srgb, var(--blue) 20%, transparent), color-mix(in srgb, var(--blue) 5%, transparent)); }
.ai-proj.on::before { content:""; position:absolute; left:0; top:8px; bottom:8px;
  width:2.5px; border-radius:0 3px 3px 0; background:var(--blue); }
.ai-proj-title { font-size:12.5px; font-weight:600; letter-spacing:-.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ai-proj-meta { font-size:10px; color:var(--subtle); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.ai-rail-link { width:100%; display:flex; align-items:center; gap:8px;
  padding:8px 10px; border:0; background:transparent; color:var(--muted);
  cursor:pointer; border-radius:10px; text-align:left; font-size:12px;
  transition:background .15s ease, color .15s ease; }
.ai-rail-link:hover { background:var(--lift-2); color:var(--text); }
.ai-shell.training .ai-rail-link { background:color-mix(in srgb, var(--blue) 16%, transparent);
  color:var(--text); }

.ai-main { background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); display:flex; flex-direction:column;
  overflow:hidden; min-height:0; }
.ai-main[hidden] { display:none !important; }

.ai-head { display:flex; align-items:center; gap:12px; padding:12px 16px;
  border-bottom:1px solid var(--line); background:var(--panel-2); }
.ai-head-title { display:flex; align-items:center; gap:11px; flex:1; min-width:0; }
.ai-head-title > div { min-width:0; flex:1; }
.ai-head-actions { display:flex; align-items:center; gap:8px; flex:0 0 auto; }
.ai-mark { width:30px; height:30px; flex:0 0 30px; border-radius:10px;
  display:grid; place-items:center; font-size:15px;
  background:linear-gradient(135deg, var(--blue,#4387f5), var(--violet,#8b5cf6));
  box-shadow:0 6px 16px rgba(79,70,229,.3); }
/* The title is the field. No border until you are in it, so it reads as a
   heading you happen to be able to rename rather than as a form. */
.ai-title, .ai-title-static { width:100%; font-size:14px; font-weight:600;
  letter-spacing:-.01em; color:var(--text); }
.ai-title-wrap { display:flex; align-items:center; gap:2px; margin-left:-6px; }
.ai-title { flex:1; min-width:0; border:1px solid transparent;
  background:transparent; padding:2px 6px; border-radius:7px;
  transition:background .14s, border-color .14s; }
.ai-title:hover { background:var(--lift-2); }
.ai-title:focus { background:var(--panel); border-color:var(--line);
  outline:none; }
/* The pencil only shows on hover, and gets out of the way once you are
   actually typing -- the field itself is the affordance by then. */
.ai-title-pen { flex:0 0 auto; color:var(--subtle); opacity:0;
  transition:opacity .14s ease; }
.ai-title-wrap:hover .ai-title-pen { opacity:1; }
.ai-title-wrap:focus-within .ai-title-pen { opacity:0; }
.ai-title:disabled { color:var(--text); }
.ai-title:disabled:hover { background:transparent; }
.ai-title-wrap:has(.ai-title:disabled):hover .ai-title-pen { opacity:0; }
.ai-sub { font-size:11px; color:var(--subtle); margin-top:1px; padding-left:1px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.ai-log { flex:1; overflow-y:auto; padding:24px 20px 6px; display:flex;
  flex-direction:column; gap:16px; scrollbar-width:thin; }
.ai-turn { max-width:74%; }
.ai-turn.theirs { max-width:88%; }
.ai-who { display:flex; align-items:center; gap:6px; font-size:10px;
  font-weight:800; letter-spacing:.07em; text-transform:uppercase;
  color:var(--subtle); margin-bottom:5px; }
.ai-who-mark { font-size:11px; }
/* The Brain's mark is a drawing, not a character: sized to the box it sits in. */
.ai-mark-svg { width:58%; height:58%; color:#fff; }
.ai-who-mark .ai-mark-svg { width:13px; height:13px; color:var(--blue); vertical-align:-2px; }
/* The assistant's side is plain text, not a bubble -- your question is the
   thing in a bubble, its answer is the page. A two-thousand-word meal plan
   inside a chat bubble reads like a wall; the same words plain read like a
   document. Written against .msg-bubble.ai-answer because the Messages bubble
   rules are defined further down the file and would otherwise win. */
.msg-bubble.ai-answer { background:transparent; border-color:transparent;
  padding:0 2px; border-radius:0; box-shadow:none; }
.ai-answer p { margin:0 0 9px; }
.ai-answer p:last-child, .ai-answer ul:last-child, .ai-answer ol:last-child { margin-bottom:0; }
.ai-answer h4 { font-size:12.5px; font-weight:700; letter-spacing:.01em;
  margin:14px 0 6px; }
.ai-answer h4:first-child { margin-top:0; }
.ai-answer ul, .ai-answer ol { margin:0 0 9px; padding-left:19px; }
.ai-answer li { margin-bottom:4px; }
.ai-answer code { font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:12px; padding:1px 5px; border-radius:5px;
  background:var(--lift-3); }
.ai-failed { color:var(--bad,#e2635f); }
/* A caret on the last line while the answer is still being written. */
.ai-answer.streaming > :last-child::after { content:""; display:inline-block;
  width:2px; height:1em; margin-left:2px; vertical-align:-2px;
  background:var(--blue); animation:aiCaret .9s step-end infinite; }
@keyframes aiCaret { 50% { opacity:0; } }
@media (prefers-reduced-motion:reduce){
  .ai-answer.streaming > :last-child::after { animation:none; } }
/* Said when an answer stopped short of what was asked for. */
.ai-cut { margin-top:10px; padding-top:8px; border-top:1px solid var(--line);
  font-size:11.5px; color:var(--subtle); }

/* A model takes seconds to answer. Without this the send button just went
   quiet, which is indistinguishable from a broken button. */
.ai-thinking { display:inline-flex; align-items:center; gap:4px; }
.ai-thinking i { width:5px; height:5px; border-radius:50%;
  background:var(--blue); animation:aiPulse 1.15s ease-in-out infinite; }
.ai-thinking i:nth-child(2) { animation-delay:.16s; }
.ai-thinking i:nth-child(3) { animation-delay:.32s; }
.ai-thinking em { font-style:normal; font-size:12.5px; color:var(--subtle);
  margin-left:5px; }
@keyframes aiPulse { 0%, 70%, 100% { opacity:.25; transform:scale(.8); }
                     35% { opacity:1; transform:scale(1); } }
@media (prefers-reduced-motion:reduce){ .ai-thinking i { animation:none; opacity:.6; } }

.ai-empty { margin:auto; text-align:center; max-width:420px; padding:20px; }
.ai-empty-mark { width:46px; height:46px; margin:0 auto 14px; border-radius:15px;
  display:grid; place-items:center; font-size:22px;
  background:linear-gradient(135deg, var(--blue,#4387f5), var(--violet,#8b5cf6));
  box-shadow:0 10px 26px rgba(79,70,229,.32); }
.ai-empty h2 { font-size:19px; letter-spacing:-.02em; margin-bottom:6px; }

/* Pinned to the bottom, grows with the text, and is the whole target -- the
   click lands on the box even when it misses the textarea. */
.ai-composer { display:flex; align-items:flex-end; gap:8px; margin:10px 16px 0;
  padding:8px 8px 8px 14px; border:1px solid var(--line); border-radius:16px;
  background:var(--panel-2); transition:border-color .15s ease,
  box-shadow .15s ease; }
.ai-composer:focus-within { border-color:color-mix(in srgb, var(--blue) 60%, transparent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.ai-composer textarea { flex:1; border:0; background:transparent; resize:none;
  padding:7px 0; font-size:13.5px; line-height:1.5; color:var(--text);
  /* Overriding the app-wide textarea min-height on purpose: this one starts at
     a single line and grows, so a 64px floor makes an empty box look like a
     form field. */
  min-height:0; height:34px; max-height:180px; overflow-y:auto; }
.ai-composer textarea:focus { outline:none; box-shadow:none; }
.ai-send { width:32px; height:32px; flex:0 0 32px; border-radius:50%;
  display:grid; place-items:center; cursor:pointer; color:#fff; border:0;
  background:linear-gradient(135deg, var(--accent-fill-2), var(--blue-solid));
  box-shadow:0 4px 12px color-mix(in srgb, var(--blue) 32%, transparent);
  transition:transform .12s ease, opacity .15s ease; }
.ai-send:hover:not(:disabled) { transform:scale(1.06); }
.ai-send:disabled { opacity:.45; cursor:default; }
.ai-note { margin:7px 16px 12px; font-size:10.5px; }

.ai-train { flex:1; overflow-y:auto; padding:16px; display:flex;
  flex-direction:column; gap:16px; scrollbar-width:thin; }

/* Inbox */
.tb-btn { position:relative; }
.tb-badge { position:absolute; top:1px; right:1px; min-width:16px; height:16px;
  padding:0 4px; border-radius:9px; background:var(--red,#fb6472); color:#fff;
  font-size:9.5px; font-weight:800; display:grid; place-items:center;
  line-height:1; font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 2px var(--panel,#121822); }
.inbox-row { display:flex; gap:12px; align-items:flex-start; padding:12px 11px;
  border-bottom:1px solid var(--line); cursor:pointer; border-radius:10px;
  transition:background .15s ease, transform .12s ease; position:relative; }
.inbox-row:hover { background:var(--lift-2); }
.inbox-row:active { transform:scale(.99); }
.inbox-row.unread { background:linear-gradient(90deg,
  color-mix(in srgb, var(--blue) 14%, transparent), color-mix(in srgb, var(--blue) 3%, transparent)); }
.inbox-row.unread::before { content:""; position:absolute; left:2px; top:16px;
  width:5px; height:5px; border-radius:50%; background:var(--blue); }
.inbox-row.unread .inbox-main { padding-left:8px; }
.inbox-main { flex:1; min-width:0; font-size:12.5px; }
.inbox-body { color:var(--muted); font-size:11.5px; margin-top:3px;
  overflow:hidden; text-overflow:ellipsis; }
.inbox-meta { color:var(--subtle); font-size:10px; margin-top:4px; }

/* Sharing: avatars you click, no dropdowns */
.share-wrap { position:relative; display:flex; align-items:center; gap:6px; }
.avatars { display:flex; }
.avatars .avatar:not(:first-child) { margin-left:-7px; }
.avatar { width:25px; height:25px; border-radius:50%; display:grid;
  place-items:center; font-size:9.5px; font-weight:800; color:#fff;
  border:2px solid var(--panel,var(--panel-2)); letter-spacing:.02em; flex:0 0 auto; }
.share-add { width:25px; height:25px; border-radius:50%; display:grid;
  place-items:center; cursor:pointer; color:var(--muted);
  border:1.5px dashed var(--line); background:transparent; padding:0;
  transition:color .14s, border-color .14s; }
.share-add:hover { color:var(--blue,#4387f5); border-color:var(--blue,#4387f5); }
.share-pop { position:absolute; top:32px; left:0; z-index:60; width:236px;
  padding:6px; border-radius:11px; background:var(--panel,var(--panel-2));
  border:1px solid var(--line); box-shadow:var(--shadow-lg);
  animation:sharePop .12s ease-out; }
@keyframes sharePop { from { opacity:0; transform:translateY(-4px); }
                      to { opacity:1; transform:none; } }
.share-head, .share-foot { font-size:10px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); padding:7px 9px 5px; }
.share-foot { text-transform:none; letter-spacing:0; font-weight:600;
  border-top:1px solid var(--line); margin-top:4px; }
.share-row { width:100%; display:flex; align-items:center; gap:9px; padding:6px 9px;
  border:0; background:transparent; color:inherit; cursor:pointer;
  border-radius:8px; text-align:left; }
.share-row:hover { background:var(--lift-3); }
.share-name { flex:1; font-size:12.5px; }
.share-tick { opacity:0; color:var(--blue,#4387f5); transition:opacity .12s; }
.share-row.on .share-tick { opacity:1; }

/* Messages -----------------------------------------------------------------
   Held to the same standard as the AI tool cards, because this is the page
   that gets opened most: real motion on anything that reacts, a type scale
   with three clear levels, and colour used to separate speakers rather than
   to decorate. Every transition is short enough to feel like response rather
   than animation. */
/* minmax(0,1fr) rather than 1fr: a plain 1fr refuses to shrink below its
   content, so opening the thread panel squeezed the conversation to a column
   too narrow to read instead of taking the space it needed. */
.msg-shell { display:grid; grid-template-columns:264px minmax(0,1fr); gap:16px;
  align-items:stretch; height:calc(100vh - 196px); min-height:460px; }
.msg-shell.with-panel { grid-template-columns:264px minmax(300px,1fr) 340px; }
/* Not enough width for three columns: the people list goes, not the
   conversation. You already know who you are talking to. */
@media (max-width:1300px){
  .msg-shell.with-panel { grid-template-columns:minmax(280px,1fr) 330px; }
  .msg-shell.with-panel .msg-rail { display:none; }
}
@media (max-width:900px){
  .msg-shell, .msg-shell.with-panel { grid-template-columns:1fr; height:auto; }
  .msg-shell.with-panel .msg-rail { display:none; }
  .msg-panel { max-height:70vh; }
}

.msg-rail { background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); padding:10px 8px; overflow-y:auto;
  scrollbar-width:thin; }
.msg-rail-head { font-size:10px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--subtle); padding:6px 10px 10px; }

.ibx-rail .msg-person { border-radius:10px; }
.msg-person { width:100%; display:flex; align-items:center; gap:11px;
  padding:9px 10px; border:0; background:transparent; color:inherit;
  cursor:pointer; border-radius:11px; text-align:left; position:relative;
  transition:background .15s ease, transform .12s ease; }
.msg-person:hover { background:var(--lift-2); }
.msg-person:active { transform:scale(.985); }
.msg-person.on { background:linear-gradient(90deg,
  color-mix(in srgb, var(--blue) 20%, transparent), color-mix(in srgb, var(--blue) 5%, transparent)); }
.msg-person.on::before { content:""; position:absolute; left:0; top:9px;
  bottom:9px; width:2.5px; border-radius:0 3px 3px 0; background:var(--blue); }
.msg-person-body { flex:1; min-width:0; display:flex; flex-direction:column;
  gap:2px; }
.msg-person-top { display:flex; align-items:baseline; justify-content:space-between;
  gap:8px; }
.msg-person-name { font-size:13px; font-weight:600; letter-spacing:-.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.msg-person-when { font-size:10px; color:var(--subtle); flex:0 0 auto; }
/* Mark read / unread, on hover, where the dot sits. */
.msg-person-act { position:absolute; right:8px; top:50%; transform:translateY(-50%); width:26px; height:26px; border-radius:7px;
  border:1px solid transparent; background:var(--panel); color:var(--muted); display:grid; place-items:center; padding:0; cursor:pointer;
  opacity:0; transition:opacity .12s ease; }
.msg-person-act i { position:absolute; top:5px; right:5px; width:6px; height:6px; border-radius:50%; background:var(--blue); }
.msg-person:hover .msg-person-act, .msg-person-act:focus-visible { opacity:1; }
.msg-person:hover .msg-dot { opacity:0; }
.msg-person-act:hover { border-color:var(--line); color:var(--text); }
/* No hover on a phone, so the control is simply always there -- with a lane
   of its own, because it used to sit on top of the last line said. */
@media (hover: none) {
  .msg-person { padding-right:42px; }
  .msg-person-act { opacity:.55; right:6px; width:30px; height:30px; }
  .msg-person:hover .msg-dot { opacity:1; }
}
.msg-person-last { display:block; font-size:11.5px; color:var(--subtle); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }   /* block, or the ellipsis never applies and a pasted link runs off the rail */
.msg-person.on .msg-person-last { color:var(--muted); }
.msg-dot { min-width:18px; height:18px; padding:0 5px; border-radius:9px;
  background:var(--blue-solid); color:#fff; font-size:9.5px; font-weight:800;
  display:grid; place-items:center; box-shadow:0 2px 8px color-mix(in srgb, var(--blue) 45%, transparent); }

.msg-main { background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); display:flex; flex-direction:column;
  overflow:hidden; min-height:0; }
.msg-empty { padding:40px 34px; max-width:440px; }

#msgThread { display:flex; flex-direction:column; height:100%; min-height:0; }
/* A display rule beats the hidden attribute, so anything given `display` here
   has to say so explicitly -- without this the empty state and the open
   conversation render on top of each other, which is exactly what they did. */
#msgThread[hidden], .msg-panel[hidden], .msg-empty[hidden] { display:none !important; }
.msg-head { display:flex; align-items:center; gap:11px; padding:14px 18px;
  border-bottom:1px solid var(--line); background:var(--panel-2); }
.msg-head strong { font-size:14px; letter-spacing:-.01em; }

.msg-log { flex:1; overflow-y:auto; padding:22px 18px; display:flex;
  flex-direction:column; gap:14px; scrollbar-width:thin; }
/* An attachment and the button that saves it. The button sits in the corner
   of the picture rather than under it, so a row of pictures stays a row. */
.msg-att { position:relative; display:inline-flex; }
.msg-dl { position:absolute; top:6px; right:6px; display:grid; place-items:center;
  width:28px; height:28px; border-radius:8px; color:#fff;
  background:rgba(8,12,20,.62); backdrop-filter:blur(4px);
  opacity:0; transition:opacity .12s ease; text-decoration:none; }
.msg-att:hover .msg-dl, .msg-dl:focus-visible { opacity:1; }
/* Saving a picture somebody sent you is not a desktop-only wish. */
@media (hover:none) { .msg-dl { opacity:.8; } }
.msg-dl:hover { background:rgba(8,12,20,.82); }
/* No hover on a touch screen, so it is simply there. */
@media (hover: none) { .msg-dl { opacity:1; } }
.msg-att .msg-file { padding-right:36px; }

.msg-turn { display:flex; flex-direction:column; max-width:64%; }
/* Only a turn that has just arrived. Every bubble used to carry this, and the
   log is rebuilt on every paint, so the whole conversation re-animated each
   time -- twice on send. convo.js decides which ones are genuinely new. */
.msg-turn.is-new { animation:msgIn .18s ease-out; }
@keyframes msgIn { from { opacity:0; transform:translateY(4px); }
                   to { opacity:1; transform:none; } }
.msg-turn.mine { align-self:flex-end; align-items:flex-end; }
.msg-turn.theirs { align-self:flex-start; }
.msg-bubble { padding:10px 14px; border-radius:16px; font-size:13.5px;
  line-height:1.55; background:var(--panel-2); border:1px solid var(--line);
  overflow-wrap:anywhere; }
.msg-turn.mine .msg-bubble { background:linear-gradient(135deg,
  var(--accent-fill-2), var(--blue-solid)); border-color:transparent; color:#fff;
  border-bottom-right-radius:6px; box-shadow:0 4px 14px color-mix(in srgb, var(--blue) 28%, transparent); }
.msg-turn.theirs .msg-bubble { border-bottom-left-radius:6px; }
/* A huddle in the conversation: a call card, not a bubble (convo.js
   huddleCard). Missed is red like a missed call on a phone; one that happened
   is the quiet panel colour. Same card whoever started it. */
.msg-huddle-card { display:flex; align-items:center; gap:12px; padding:12px 14px; border-radius:14px;
  border:1px solid var(--line); background:var(--panel-2); min-width:260px; max-width:420px; }
.msg-huddle-card.missed { border-color:color-mix(in srgb, var(--red) 40%, var(--line));
  background:color-mix(in srgb, var(--red) 8%, var(--panel-2)); }
.msg-huddle-ico { flex:none; width:38px; height:38px; border-radius:50%; display:grid; place-items:center;
  background:var(--tint-blue); color:var(--blue); }
.msg-huddle-card.missed .msg-huddle-ico { background:var(--tint-red); color:var(--ink-bad); }
.msg-huddle-text { flex:1; min-width:0; display:grid; gap:2px; }
.msg-huddle-text b { font-size:14px; color:var(--text); }
.msg-huddle-card.missed .msg-huddle-text b { color:var(--ink-bad); }
.msg-huddle-text > span { font-size:12.5px; color:var(--muted); line-height:1.4; }
.msg-huddle-text .msg-huddle-when { font-size:11.5px; color:var(--subtle); font-variant-numeric:tabular-nums; }
.msg-huddle-back { flex:none; padding:7px 12px; border-radius:999px; border:0; cursor:pointer; font:inherit;
  font-size:12.5px; font-weight:700; color:#fff; background:linear-gradient(135deg, var(--accent-fill-2), var(--blue-solid)); }
@media (max-width: 520px) { .msg-huddle-card { min-width:0; flex-wrap:wrap; } .msg-huddle-back { width:100%; } }
/* A held huddle's Brief: a quiet button on the card, and the brief opening
   under it in the same card (messages.js briefHtml). */
.msg-huddle-card { flex-wrap:wrap; }
.msg-huddle-open { flex:none; padding:7px 12px; border-radius:999px; cursor:pointer; font:inherit;
  font-size:12.5px; font-weight:650; color:var(--text); background:var(--panel);
  border:1px solid var(--line-strong); }
.msg-huddle-open:hover, .msg-huddle-open[aria-expanded="true"] { border-color:var(--subtle); background:var(--lift-2); }
.msg-huddle-brief { flex:1 1 100%; border-top:1px solid var(--line); padding-top:10px; margin-top:2px;
  font-size:13px; line-height:1.5; color:var(--text); }
.msg-huddle-brief[hidden] { display:none; }
.msg-huddle-brief p { margin:0 0 8px; }
.msg-huddle-brief .hb-head { font-weight:650; }
.msg-huddle-brief .hb-sec { margin:0 0 8px; }
.msg-huddle-brief .hb-sec > b { display:block; font-size:11px; letter-spacing:.04em; text-transform:uppercase;
  color:var(--muted); margin-bottom:3px; }
.msg-huddle-brief ul { margin:0; padding-left:18px; }
.msg-huddle-brief li { margin:2px 0; }
.msg-huddle-brief .hb-script summary { cursor:pointer; font-size:12.5px; color:var(--muted); }
.msg-huddle-brief .hb-script p { margin:6px 0 0; font-size:12.5px; }
.msg-huddle-card:has(.msg-huddle-brief:not([hidden])) { max-width:560px; }
.msg-subject { display:inline-block; margin-top:7px; padding:2px 8px;
  border-radius:20px; font-size:10.5px; font-weight:600; text-decoration:none;
  background:var(--lift-1); color:inherit; opacity:.9;
  transition:opacity .14s ease; }
.msg-subject:hover { opacity:1; }
.msg-turn.theirs .msg-subject { background:color-mix(in srgb, var(--blue) 16%, transparent);
  color:var(--blue); }
.msg-time { font-size:9.5px; color:var(--subtle); margin-top:5px; padding:0 5px;
  letter-spacing:.02em; }
.msg-edited { font-style:italic; opacity:.85; }
/* "Seen 9:14am" under the last message they have opened. Quieter than the
   timestamp above it: it answers a question you only ask about the last
   thing you said, and should not compete with the conversation. */
.msg-seen { font-size:9.5px; color:var(--subtle); opacity:.8; margin-top:2px;
  padding:0 5px; letter-spacing:.02em; }

/* Day dividers and grouping.

   Consecutive messages from one person inside a few minutes are one utterance,
   so they are drawn as one: tight spacing, a single timestamp, softened inner
   corners. This is most of the difference between a chat that looks designed
   and one that looks like a table of rows. */
.msg-day { align-self:center; display:flex; align-items:center; gap:10px;
  width:100%; margin:6px 0 2px; }
.msg-day::before, .msg-day::after { content:""; flex:1; height:1px;
  background:var(--line); }
.msg-day span { font-size:10px; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; color:var(--subtle); white-space:nowrap; }
.msg-day.thin { margin:12px 0 4px; }
.msg-turn.grouped { margin-top:-10px; }
.msg-turn.grouped.mine .msg-bubble { border-top-right-radius:7px; }
.msg-turn.grouped.theirs .msg-bubble { border-top-left-radius:7px; }
.msg-bubble.pending { opacity:.62; }
.msg-bubble a { color:var(--blue); text-decoration:underline; overflow-wrap:anywhere; }
.msg-turn.mine .msg-bubble a { color:#fff; }
.msg-bubble.memo { display:flex; flex-direction:column; gap:5px; }
.msg-cap { font-size:11.5px; font-weight:650; opacity:.85; }
.msg-audio { display:block; height:34px; width:260px; max-width:100%; border-radius:17px; }
.msg-memo { padding:10px 18px 0; }
.msg-memo .ws-memo { margin-bottom:0; }
.msg-mic { align-self:flex-end; width:40px; padding:0; display:grid; place-items:center; }
.msg-bubble.gone { font-style:italic; color:var(--subtle); background:transparent;
  border:1px dashed var(--line); box-shadow:none; }
.msg-turn.mine .msg-bubble.gone { background:transparent; color:var(--subtle); }

/* The ⋯ menu.

   One affordance instead of a row of icons: a message has four things you can
   do to it and a bubble is not a toolbar. Hidden until the pointer is on the
   message, because these are used a few times a week and read every day. */
.msg-row { display:flex; align-items:center; gap:4px; max-width:100%; }
.msg-more { display:grid; place-items:center; width:26px; height:26px; padding:0;
  flex:0 0 auto; border-radius:8px; border:1px solid transparent;
  background:transparent; color:var(--subtle); cursor:pointer; opacity:0;
  pointer-events:none; fill:currentColor;
  transition:opacity .14s ease, background .14s ease, color .14s ease; }
.msg-turn:hover .msg-more, .msg-more:focus-visible { opacity:1; pointer-events:auto; }
/* pointer-events:none as well as opacity:0, so on a phone this was not merely
   invisible, it refused the tap. Everything behind it -- every per-message
   action there is -- was unreachable. Quiet but present and tappable. */
@media (hover:none) { .msg-more { opacity:.5; pointer-events:auto; } }
.msg-more:hover { background:var(--panel-2); color:var(--text); }
.msg-more:focus-visible { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
@media (hover: none) { .msg-more { opacity:.45; pointer-events:auto; } }

/* Reactions. Chips under the bubble, the way ClickUp and Slack draw them:
   the emoji and a count, lit when one of them is yours, the names on hover.
   The smiley-plus that adds one behaves like the ⋯ -- there when the
   pointer is on the message, out of the way when it is not. */
.msg-reacts { display:flex; flex-wrap:wrap; align-items:center; gap:4px; margin-top:4px; padding:0 4px; }
.msg-turn.mine .msg-reacts { justify-content:flex-end; }
.msg-react { display:inline-flex; align-items:center; gap:4px; height:24px; padding:0 8px 0 6px;
  border-radius:12px; border:1px solid var(--line); background:var(--panel); color:var(--muted);
  font-size:13px; line-height:1; cursor:pointer; transition:border-color .12s ease, background .12s ease; }
.msg-react b { font-size:10.5px; font-weight:750; font-variant-numeric:tabular-nums; }
.msg-react:hover { border-color:var(--line-strong); background:var(--lift-2); }
.msg-react.on { border-color:var(--blue); background:var(--tint-blue); color:var(--blue); }
.msg-react-add { display:grid; place-items:center; width:26px; height:24px; padding:0; border-radius:12px;
  border:1px solid transparent; background:transparent; color:var(--subtle); cursor:pointer; opacity:0;
  transition:opacity .14s ease, background .14s ease, color .14s ease; }
.msg-turn:hover .msg-react-add, .msg-react-add:focus-visible { opacity:1; }
@media (hover:none) { .msg-react-add { opacity:.5; } }
.msg-react-add:hover { background:var(--lift-2); color:var(--text); border-color:var(--line); }
@media (hover: none) { .msg-react-add { opacity:.55; } }

/* The reaction picker: the icon picker's grid on the ⋯ menu's footing. */
.epick { position:fixed; z-index:10000; width:300px; padding:8px; border-radius:12px;
  background:var(--panel); color:var(--text); border:1px solid var(--line); box-shadow:var(--shadow-lg);
  display:flex; flex-direction:column; gap:6px; animation:menuIn .12s cubic-bezier(.2,.8,.3,1); }
.epick-quick { display:grid; grid-template-columns:repeat(8,1fr); gap:2px; padding-bottom:6px; border-bottom:1px solid var(--line); }
.epick-quick .ip-one { font-size:21px; padding:5px 0; }
.epick .epick-scroll { height:190px; }
.epick .ip-scroll h5 { background:var(--panel); }
@media (max-width:700px) { .epick { width:min(300px, calc(100vw - 16px)); } }

.msg-menu { position:fixed; z-index:80; min-width:186px; padding:5px;
  border-radius:12px; background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--shadow);
  transform-origin:top center;
  /* An animation rather than a class toggled on the next frame: the toggle
     leaves the menu invisible if anything delays that frame, which is a menu
     that silently does not open. */
  animation:menuIn .12s cubic-bezier(.2,.8,.3,1); }
@keyframes menuIn { from { opacity:0; transform:scale(.97) translateY(-3px); }
                    to { opacity:1; transform:none; } }
.msg-menu-item { display:block; width:100%; text-align:left; padding:7px 10px;
  border:0; border-radius:8px; background:transparent; color:var(--text);
  font-size:12.5px; font-weight:500; cursor:pointer; letter-spacing:-.01em;
  transition:background .1s ease, color .1s ease; }
.msg-menu-item:hover { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue); }
.msg-menu-item.danger:hover { background:rgba(226,86,75,.14); color:var(--ink-bad); }

/* The reply count under a message: the whole of the thread affordance. */
.msg-replies { display:inline-flex; align-items:center; gap:6px; margin-top:6px;
  /* The turn is a stretching flex column, so without this the chip spans the
     whole width and stops reading as a small control. */
  align-self:flex-start; width:fit-content;
  padding:3px 9px 3px 7px; border-radius:20px; cursor:pointer;
  border:1px solid transparent; background:color-mix(in srgb, var(--blue) 12%, transparent);
  color:var(--blue); font-size:11px; font-weight:650; letter-spacing:-.005em;
  transition:background .14s ease, border-color .14s ease, transform .1s ease; }
.msg-replies:hover { background:color-mix(in srgb, var(--blue) 20%, transparent);
  border-color:color-mix(in srgb, var(--blue) 36%, transparent); }
.msg-replies:active { transform:scale(.97); }
.msg-turn.mine .msg-replies { align-self:flex-end; }
.msg-replies-when { color:var(--subtle); font-weight:500; }
.msg-replies.fresh { background:color-mix(in srgb, var(--blue) 20%, transparent); }
.msg-replies.fresh::after { content:""; width:6px; height:6px; border-radius:50%;
  background:var(--blue); box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 20%, transparent); }

/* An open editor drops the gradient: you are working on the text, not reading
   it, and white-on-blue in a textarea is unreadable. */
.msg-turn.mine .msg-bubble.editing,
.msg-turn.mine .msg-bubble.confirming { background:var(--panel-2);
  border:1px solid var(--line); color:var(--text); box-shadow:none; }
.msg-bubble.editing, .msg-bubble.confirming { padding:9px 10px; min-width:236px; }
.msg-edit-input { width:100%; resize:none; font:inherit; font-size:13.5px;
  line-height:1.55; padding:7px 9px; border-radius:9px; background:var(--panel);
  border:1px solid var(--line); color:var(--text);
  transition:border-color .15s ease, box-shadow .15s ease; }
.msg-edit-input:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.msg-confirm-q { display:block; font-size:12.5px; padding:2px 2px 0;
  color:var(--text); }
.msg-inline-actions { display:flex; justify-content:flex-end; gap:6px;
  margin-top:8px; }
/* The same size and shape as .button.sm, so a small button is one thing. */
.msg-mini { padding:5px 11px; border-radius:8px; font-size:11px;
  font-weight:700; cursor:pointer; border:1px solid var(--line-strong);
  background:var(--panel); color:var(--muted); box-shadow:var(--shadow-sm);
  transition:background .14s ease, border-color .14s ease, color .14s ease; }
.msg-mini:hover { border-color:var(--blue); color:var(--text); background:var(--lift-2); }
.msg-mini.primary { background:var(--blue-solid); border-color:transparent; color:#fff; }
.msg-mini.primary:hover { filter:brightness(1.07); }
.msg-mini.danger { background:#e2564b; border-color:transparent; color:#fff; }
.msg-mini.danger:hover { filter:brightness(1.07); }

/* The thread panel. Beside the log, not over it -- you never lose your place
   in the conversation to read one reply. */
.msg-panel { background:var(--panel); border:1px solid var(--line);
  border-radius:16px; display:flex; flex-direction:column; overflow:hidden;
  animation:panelIn .18s cubic-bezier(.2,.8,.3,1); }
@keyframes panelIn { from { opacity:0; transform:translateX(10px); }
                     to { opacity:1; transform:none; } }
.msg-panel-head { display:flex; align-items:flex-start; justify-content:space-between;
  gap:10px; padding:13px 14px; border-bottom:1px solid var(--line);
  background:var(--panel-2); }
.msg-panel-head strong { display:block; font-size:13px; letter-spacing:-.01em; }
.msg-panel-sub { font-size:10.5px; }
.msg-x { display:grid; place-items:center; width:26px; height:26px; padding:0;
  border-radius:8px; border:1px solid transparent; background:transparent;
  color:var(--subtle); cursor:pointer;
  transition:background .14s ease, color .14s ease; }
.msg-x:hover { background:var(--panel); color:var(--text); }
.msg-panel-body { flex:1; overflow-y:auto; padding:16px 14px; display:flex;
  flex-direction:column; gap:14px; scrollbar-width:thin; }
.msg-panel-body .msg-turn { max-width:88%; }
.msg-panel .msg-box { padding:12px 14px 8px; }

.msg-toast { position:fixed; left:50%; bottom:26px; transform:translate(-50%,10px);
  z-index:90; padding:8px 16px; border-radius:22px; font-size:12px;
  font-weight:600; color:var(--text); background:var(--panel);
  border:1px solid var(--line); box-shadow:var(--shadow);
  opacity:0; pointer-events:none;
  transition:opacity .16s ease, transform .16s cubic-bezier(.2,.8,.3,1); }
.msg-toast.on { opacity:1; transform:translate(-50%,0); }

/* The prospect composer's shape: the box across the full width, the tools in
   a row under it, Send on the right. A message box that shares its line with
   three buttons is a message box nobody wants to write in. */
.msg-box { display:flex; flex-direction:column; gap:8px; padding:12px 16px 12px;
  border-top:1px solid var(--line); background:var(--panel-2); }
.msg-box textarea { width:100%; resize:none; font-size:13.5px; line-height:1.5;
  min-height:44px; padding:11px 13px; border-radius:12px; background:var(--panel);
  border:1px solid var(--line); color:var(--text);
  transition:border-color .15s ease, box-shadow .15s ease; }
.msg-box textarea:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.msg-box-row { display:flex; align-items:center; gap:8px; }
.msg-box-row .msg-note { padding:0; margin:0 0 0 auto; background:none; }
.msg-box-row .button { flex:0 0 auto; }
.msg-tool { flex:0 0 auto; }
.msg-note { padding:0 18px 13px; margin:0; font-size:10.5px;
  color:var(--subtle); background:var(--panel-2); }

/* An SVG icon in the top bar needs the same optical size as its glyph
   siblings, which are text and sit on a baseline. */
.tb-icon.tb-svg { display:grid; place-items:center; line-height:0; }
.msg-empty { display:flex; flex-direction:column; align-items:flex-start; }
.msg-empty-mark { width:52px; height:52px; border-radius:15px; display:grid;
  place-items:center; color:var(--blue);
  background:linear-gradient(135deg, color-mix(in srgb, var(--blue) 18%, transparent), rgba(139,92,246,.14));
  border:1px solid color-mix(in srgb, var(--blue) 22%, transparent); }

/* Starting a message from the header panel */
.quick-msg { margin-bottom:14px; padding-bottom:12px;
  border-bottom:1px solid var(--line); }
.quick-msg-head { font-size:10px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:8px; }
.quick-people { display:flex; flex-wrap:wrap; gap:7px; }
.quick-person { display:flex; align-items:center; gap:8px; padding:6px 11px 6px 6px;
  border-radius:22px; background:var(--panel-2); border:1px solid var(--line);
  text-decoration:none; color:inherit; font-size:12.5px;
  transition:border-color .15s ease, transform .12s ease; }
.quick-person:hover { border-color:var(--blue); transform:translateY(-1px); }
.quick-person .avatar { width:22px; height:22px; font-size:9px; border-width:0; }

/* Inbox ---------------------------------------------------------------------
   A place, not a popover. Two sections down the side, read state across the
   top, and every row carrying the one control that matters on it -- keeping
   something unread. Rows are quiet by default and only the unread ones carry
   weight, so a full inbox still reads as a short list of things to do. */
/* Same three-column frame as the conversation view, because they are the same
   page now: rail, main, and the thread panel when one is open. */
.ibx-shell { display:grid; grid-template-columns:244px minmax(0,1fr); gap:16px;
  align-items:stretch; height:calc(100vh - 196px); min-height:460px; }
/* The inbox is a screen, not a document, so it takes the window the way the
   Calendar does. 196px was reserving room for a footer this page does not
   need and then some: the panels stopped ~90px short of the bottom and the
   conversation gave that space up for nothing. 102px is what is genuinely
   above the shell (the 72px sticky bar and the page's 30px), and the rest is
   main's own bottom padding. dvh so a phone's address bar cannot cut it off. */
body.ibx-page .app-footer { display:none; }
body.ibx-page main { padding-bottom:12px; }
body.ibx-page .ibx-shell { height:calc(100vh - 114px); height:calc(100dvh - 114px); }
.ibx-shell.with-panel { grid-template-columns:244px minmax(300px,1fr) 340px; }
/* Not enough width for three, so the rail FOLDS rather than going.

   It used to vanish outright, and with the app's own sidebar taking width a
   laptop is usually under 1300px -- so opening one reply took away the list of
   people you were moving between. Now it is a strip of avatars: you can still
   move, you just cannot read the names. The conversation still wins the room.

   The name is the button that opens a conversation, so it stays -- stretched
   over the avatar with no size to its text. Hiding it would leave the row
   reachable with a mouse and with nothing at all from the keyboard. */
@media (max-width:1300px){
  .ibx-shell.with-panel { grid-template-columns:60px minmax(280px,1fr) 330px; }
  .ibx-shell.with-panel .ibx-rail { padding:8px 7px; overflow-x:hidden; }
  /* A search box 46px wide is not a search box. It comes back with the
     names when the thread closes. */
  .ibx-shell.with-panel .ibx-find { display:none; }
  .ibx-shell.with-panel .ibx-rail-head { display:none; }
  .ibx-shell.with-panel .ibx-nav { position:relative; justify-content:center; padding-left:0; padding-right:0; }
  .ibx-shell.with-panel .ibx-nav > span:first-of-type { display:none; }
  .ibx-shell.with-panel .ibx-count { position:absolute; top:1px; right:1px; margin:0; }
  .ibx-shell.with-panel .msg-person { position:relative; justify-content:center; padding:6px 0; }
  .ibx-shell.with-panel .msg-person-body { position:absolute; inset:0; font-size:0; }
  .ibx-shell.with-panel .msg-person-when,
  .ibx-shell.with-panel .msg-person-last,
  .ibx-shell.with-panel .msg-person-act { display:none; }
  /* Absolute inside the body above, which is itself inset over the row: its
     height comes from the row rather than from the flex boxes between, which
     font-size:0 has collapsed to nothing. Percentage height against a
     zero-height parent is zero, and a button with no height is one nobody can
     click -- only tab to. */
  .ibx-shell.with-panel .msg-person-name { position:absolute; inset:0; width:100%; height:auto; font-size:0; padding:0; }
  .ibx-shell.with-panel .msg-dot { position:absolute; top:1px; right:2px; }
}
@media (max-width:900px){
  .ibx-shell, .ibx-shell.with-panel { grid-template-columns:1fr; height:auto; }
  .ibx-shell.with-panel .ibx-rail { display:none; }
  .msg-panel { max-height:70vh; }
}
/* The phone tab bar is fixed to the bottom and nothing was making room for
   it, so the box you type in sat underneath it -- Stefan, 09-27-2026: "I
   tried to message MJ ... I can't find a place to message her." The composer
   was there; it was behind Prospects / Team / Clients / Calendar / Menu.
   (The bar hides itself once the textarea has focus, which is no help at all
   when the textarea is the thing you cannot reach to tap.) */
@media (max-width:700px){
  body.ibx-page .ibx-shell {
    height:calc(100vh - 114px - var(--tabbar-h));
    height:calc(100dvh - 114px - var(--tabbar-h));
  }
  body.ibx-page #msgMain { max-height:100%; }
}
#notifPane[hidden], #msgMain[hidden] { display:none !important; }

.ibx-rail { background:var(--panel); border:1px solid var(--line);
  border-radius:16px; padding:8px; display:flex; flex-direction:column; gap:2px;
  overflow-y:auto; scrollbar-width:thin; }
.ibx-rail > * { flex:0 0 auto; }
.ibx-nav-row { display:flex; flex-direction:column; gap:2px; }
/* One chip per kind on a phone's landing screen; a desk has the filter row. */
.ibx-nav-phone { display:none !important; }
@media (max-width:700px) { .ibx-nav-phone { display:flex !important; } }
/* The heading that separates the queue above from the people below. */
.ibx-rail-head { font-size:10px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--subtle); padding:14px 11px 7px;
  margin-top:6px; border-top:1px solid var(--line); }
/* Channels: a heading with its own + (admins), and a # where a person has a
   face. A rounded square rather than a circle, so a room never reads as a
   person at a glance. */
.ibx-rail-head-row { display:flex; align-items:center; justify-content:space-between; }
.ibx-rail-add { width:20px; height:20px; border-radius:6px; border:1px solid transparent;
  background:transparent; color:var(--subtle); display:grid; place-items:center;
  padding:0; cursor:pointer; margin:-4px -2px -4px 0; }
.ibx-rail-add:hover { color:var(--text); border-color:var(--line); background:var(--lift-2); }
.avatar.ch-avatar { border-radius:8px; background:var(--lift-2); color:var(--muted);
  font-size:13px; font-weight:700; border-color:var(--line); }
.msg-person.on .avatar.ch-avatar { color:var(--blue); }
.msg-head-sub { font-size:11.5px; cursor:default; white-space:nowrap;
  min-width:0; overflow:hidden; text-overflow:ellipsis; }
.msg-head .button { white-space:nowrap; }
.ch-modal { width:min(460px,100%); }
.ch-everyone { display:flex; gap:10px; align-items:flex-start; padding:10px 12px;
  border:1px solid var(--line); border-radius:10px; margin:4px 0 14px; cursor:pointer; }
.ch-everyone input { margin-top:3px; }
.ch-everyone span { display:flex; flex-direction:column; gap:2px; font-size:13px; }
.ch-everyone em { font-style:normal; font-size:11.5px; color:var(--subtle); }
.ch-people { display:grid; grid-template-columns:repeat(auto-fill, minmax(170px,1fr));
  gap:4px; margin-top:6px; }
.ch-person { display:flex; align-items:center; gap:8px; padding:7px 9px; border-radius:8px;
  font-size:13px; cursor:pointer; }
.ch-person:hover { background:var(--lift-2); }
/* Who can get in: three choices stacked; the chosen one carries the accent. */
.ch-access { display:grid; gap:6px; margin:6px 0 14px; }
.ch-access .ch-everyone { margin:0; }
.ch-access .ch-everyone:has(input:checked) { border-color:var(--blue);
  background:color-mix(in srgb, var(--blue) 7%, transparent); }
.avatar.ch-avatar svg { display:block; }
/* "Browse channels": a quiet line under the list, not another heading. */
.ch-browse-link { display:flex; align-items:center; gap:8px; width:100%; padding:7px 11px;
  border:0; border-radius:10px; background:transparent; color:var(--subtle);
  font-size:12.5px; font-weight:600; cursor:pointer; text-align:left; }
.ch-browse-link:hover { color:var(--text); background:var(--lift-2); }
.ch-browse { display:grid; gap:4px; max-height:min(420px,60vh); overflow:auto; }
.ch-browse-row { display:flex; align-items:center; gap:10px; padding:8px 10px;
  border-radius:10px; }
.ch-browse-row:hover { background:var(--lift-2); }
.ch-browse-row .grow { display:flex; flex-direction:column; gap:1px; min-width:0; }
.ch-browse-row b { font-size:13.5px; }
.ch-browse-row em { font-style:normal; font-size:11.5px; color:var(--subtle); }
.ibx-nav { display:flex; align-items:center; gap:10px; width:100%;
  padding:9px 11px; border:0; border-radius:10px; background:transparent;
  color:var(--muted); font-size:13px; font-weight:600; letter-spacing:-.01em;
  cursor:pointer; text-align:left; text-decoration:none;
  transition:background .14s ease, color .14s ease; }
.ibx-nav span:first-of-type { flex:1; }
.ibx-nav:hover { background:var(--lift-2); color:var(--text); }
.ibx-nav.on { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue-on-tint); }
.ibx-count { min-width:19px; height:19px; padding:0 6px; border-radius:10px;
  display:grid; place-items:center; font-size:10.5px; font-weight:800;
  background:var(--blue-solid); color:#fff; }
.ibx-count[hidden] { display:none !important; }

.ibx-main { background:var(--panel); border:1px solid var(--line);
  border-radius:16px; overflow:hidden; display:flex; flex-direction:column;
  min-height:0; }
.ibx-head { display:flex; align-items:center; gap:14px; padding:14px 18px;
  border-bottom:1px solid var(--line); background:var(--panel-2); }
.ibx-head h1 { font-size:15px; margin:0; letter-spacing:-.015em; }
.ibx-tabs { display:flex; gap:2px; padding:3px; border-radius:10px;
  background:var(--panel); border:1px solid var(--line); margin-left:auto; }
.ibx-tab { padding:5px 13px; border:0; border-radius:8px; background:transparent;
  color:var(--subtle); font-size:11.5px; font-weight:650; cursor:pointer;
  transition:background .14s ease, color .14s ease; }
.ibx-tab:hover { color:var(--text); }
.ibx-tab.on { background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--blue-on-tint); }

.ibx-list { flex:1; min-height:0; overflow-y:auto; display:flex;
  flex-direction:column; scrollbar-width:thin; }
/* The search box, at the top of the rail: one box over messages and mentions
   at once, because nobody remembers which of the two a sentence was in. */
.ibx-find { padding:2px 2px 8px; flex:0 0 auto; }
.ibx-find input { width:100%; font:inherit; font-size:12.5px; padding:7px 11px;
  border:1px solid var(--line); border-radius:10px; background:var(--lift-1);
  color:var(--text); }
.ibx-find input:focus { outline:none; border-color:var(--blue); background:var(--panel); }

/* A hit. Flat rows under a heading per kind, rather than cards: you are
   scanning for one line, not working a queue. */
.ibx-hit-head { display:flex; align-items:center; justify-content:space-between;
  font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
  color:var(--subtle); padding:14px 18px 6px; }
.ibx-hit-head span { font-size:10.5px; letter-spacing:0; font-variant-numeric:tabular-nums; }
.ibx-hit { display:flex; align-items:flex-start; gap:11px; width:100%; text-align:left;
  padding:11px 18px; border:0; border-bottom:1px solid var(--line);
  background:transparent; color:inherit; font:inherit; cursor:pointer; }
.ibx-hit:hover { background:var(--lift-1); }
.ibx-hit-body { flex:1 1 auto; min-width:0; }
.ibx-hit-top { display:flex; align-items:center; gap:7px; flex-wrap:wrap; font-size:12.5px; }
.ibx-hit-text { display:block; margin-top:3px; font-size:12.5px; color:var(--muted);
  overflow-wrap:anywhere; }

/* The message a search landed on, so it can be found in the log it opened. */
.msg-found { animation:msgFound 2.6s ease-out; border-radius:12px; }
@keyframes msgFound {
  0%, 55% { background:color-mix(in srgb, var(--blue) 20%, transparent); }
  100% { background:transparent; }
}

/* One queue, three kinds. The chips are a way of looking at the list, not
   three places to go -- so they sit over it rather than in the rail. */
.ibx-filters { display:flex; flex-wrap:wrap; gap:6px; padding:10px 18px;
  border-bottom:1px solid var(--line); flex:0 0 auto; }
.ibx-chip { display:inline-flex; align-items:center; gap:6px; font:inherit;
  font-size:11.5px; font-weight:650; padding:5px 11px; border-radius:999px;
  border:1px solid var(--line); background:transparent; color:var(--muted);
  cursor:pointer; }
.ibx-chip:hover { color:var(--text); border-color:var(--blue-line); }
.ibx-chip.on { background:color-mix(in srgb, var(--blue) 14%, transparent);
  border-color:color-mix(in srgb, var(--blue) 45%, transparent); color:var(--blue-on-tint); }
.ibx-chip-n { font-size:10.5px; font-weight:800; font-variant-numeric:tabular-nums;
  padding:0 5px; border-radius:20px; background:var(--lift-2); color:var(--muted); }
.ibx-chip.on .ibx-chip-n { background:color-mix(in srgb, var(--blue) 22%, transparent);
  color:var(--blue-on-tint); }
.ibx-chip-n[hidden] { display:none; }

/* What kind of thing a card is, where knowing matters. A reply and a question
   about a lead must not read as an internal mention -- that is why they were
   separate queues in the first place. */
.ibx-kind { font-size:9.5px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; padding:1px 6px; border-radius:20px;
  background:var(--lift-2); color:var(--muted); flex:0 0 auto; }
.ibx-kind-prospects { background:var(--tint-amber); color:var(--ink-warn); }
/* An alert is the app saying something broke, so it reads as a problem rather
   than as another kind of message -- the one badge in this list that is not
   about who sent it. */
.ibx-kind-alerts { background:var(--tint-red); color:var(--ink-bad); }
/* A client writing in from the hub. Green, the colour coaching already wears
   everywhere else in here, so under Everything it is obvious at a glance
   which lines came from the people we coach. */
.ibx-kind-hub { background:var(--tint-green); color:var(--ink-good); }

.ibx-card { display:flex; flex-wrap:wrap; align-items:flex-start; gap:12px; padding:14px 18px;
  border-bottom:1px solid var(--line); position:relative;
  transition:background .14s ease; }
.ibx-card:last-child { border-bottom:0; }
.ibx-card:hover { background:var(--lift-1); }
/* Unread carries an accent bar and full-weight text; read is the same row with
   the volume turned down, which is what makes a mixed list scannable. */
.ibx-card.unread::before { content:""; position:absolute; left:0; top:12px;
  bottom:12px; width:3px; border-radius:0 3px 3px 0; background:var(--blue); }
.ibx-card:not(.unread) .avatar,
.ibx-card:not(.unread) .ibx-text { opacity:.72; }
.ibx-card:not(.unread) .ibx-line strong { font-weight:600; color:var(--muted); }
.ibx-card-body { flex:1; min-width:0; }
.ibx-line { display:flex; align-items:baseline; gap:5px; flex-wrap:wrap;
  font-size:12.5px; color:var(--muted); }
.ibx-line strong { font-size:13px; font-weight:700; color:var(--text);
  letter-spacing:-.01em; }
.ibx-subject { font-weight:650; color:var(--blue); }
.ibx-when { color:var(--subtle); font-size:10.5px; margin-left:2px; }
.ibx-text { margin:5px 0 0; font-size:12.5px; line-height:1.5; color:var(--muted);
  overflow-wrap:anywhere; }
/* A person named in an alert (inbox_page.js bodyHtml): the subject's blue,
   underlined like every link in a paragraph. */
.ibx-text a { font-weight:650; color:var(--blue); }
.ibx-actions { display:flex; align-items:center; gap:7px; flex:0 0 auto;
  opacity:0; transition:opacity .14s ease; }
.ibx-card:hover .ibx-actions, .ibx-actions:focus-within { opacity:1; }
/* A reach-out card is a job, not a message: "Text them" and "They confirmed"
   are the whole card, so they sit under the words and do not wait for a
   hover to appear. */
.ibx-reach-do { display:flex; flex-wrap:wrap; gap:7px; margin-top:9px; }
/* Where the call stands since the card was filed -- the line that stops two
   people texting the same lead. Amber while nobody has, quiet once somebody
   has, green on the yes. */
.ibx-reach { margin:6px 0 0; font-size:12px; font-weight:650; color:var(--ink-warn); }
.ibx-reach.on { color:var(--text); font-weight:600; }
.ibx-reach.good { color:var(--ink-good); }
.ibx-card:not(.unread) .ibx-reach { opacity:.72; }
/* ON TOUCH THEY ARE JUST THERE. These are Reply and Open -- the whole point
   of the card -- and hidden behind a hover they did not exist on a phone at
   all: nothing to see and nothing to tap. Full opacity rather than the quiet
   treatment the secondary controls get, because this is the action. */
@media (hover:none) { .ibx-actions { opacity:1; } }
@media (hover: none){ .ibx-actions { opacity:1; } }
/* Marking one notification read -- and back again. It sits outside
   .ibx-actions deliberately: those fade in on hover, and this is the control
   somebody has to be able to SEE, because it is now the only thing that
   changes a card's state. The dot is the card's own unread mark, moved onto
   the button that clears it, so the thing you press and the thing that
   changes are the same thing. */
.ibx-mark { display:inline-flex; align-items:center; gap:7px; flex:0 0 auto;
  padding:5px 11px; border-radius:8px; font-size:11.5px; white-space:nowrap;
  font-weight:650; cursor:pointer; border:1px solid var(--line);
  background:var(--panel); color:var(--muted);
  transition:border-color .14s ease, color .14s ease, background .14s ease; }
.ibx-mark:hover { color:var(--text); border-color:var(--subtle); background:var(--lift-2); }
.ibx-mark:disabled { opacity:.55; cursor:default; }
.ibx-mark-dot { width:8px; height:8px; border-radius:50%; flex:0 0 auto;
  background:var(--blue); transition:background .14s ease, box-shadow .14s ease; }
.ibx-mark.done .ibx-mark-dot { background:transparent;
  box-shadow:inset 0 0 0 1.5px var(--subtle); }

/* Their number and the button that rings it, on a client card, a prospect
   card and the pre-call brief -- the same row in all three. Sits with the
   do-not-contact toggle because they are one question: may we reach this
   person, and reach them. */
.cp-reach { display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:9px 12px; margin-bottom:8px; border:1px solid var(--line);
  border-radius:10px; background:var(--lift-1); }
.cp-reach .helper { margin:0; }
.cp-reach b { font-size:13px; font-variant-numeric:tabular-nums; letter-spacing:-.01em; }
.cp-reach .ws-dial { margin-left:auto; }
/* The icon form, in the thread header beside the star. Square rather than a
   pill so it sits in a row of icons, and it keeps its shape when armed --
   the header cannot have one control grow into a sentence. */
.ws-dial-ico { display:inline-grid; place-items:center; width:28px; height:28px;
  padding:0; border-radius:8px; }
.ws-dial-ico svg { display:block; }

/* Do-not-contact, one line per channel. Mostly NOT decisions anybody here
   made: a texted STOP ticks its own box, and the line under it says so --
   "they asked us to stop · 29 Sep" reads very differently from somebody
   having sat and ticked it. */
.dnd-head { font-size:11px; font-weight:700; letter-spacing:.04em;
  text-transform:uppercase; color:var(--muted); margin-bottom:7px; }
.dnd-list { display:flex; flex-direction:column; gap:2px; }
/* Between the blanket switch and the specific ones. It is a real word doing
   real work: these are alternatives, not a list of five equal things. */
.dnd-or { display:flex; align-items:center; gap:8px; margin:3px 0;
  font-size:10px; font-weight:700; letter-spacing:.09em; text-transform:uppercase;
  color:var(--subtle); }
.dnd-or::before, .dnd-or::after { content:""; flex:1 1 0; height:1px; background:var(--line); }
/* Each line is a <label>, so it was picking up the app's form-label look --
   bold, letter-spaced, muted, with a margin under it -- and every option
   read like a heading (10-02-2026). Plain text here; the one that is set
   is the bold one. */
.dnd-line { display:flex; align-items:flex-start; gap:9px; padding:5px 7px; margin:0;
  border-radius:8px; cursor:pointer; font-size:12.5px; font-weight:400; letter-spacing:0;
  color:var(--text); }
.dnd-line:hover { background:var(--lift-1); }
.dnd-line input { margin:2px 0 0; flex:0 0 auto; }
.dnd-what { display:flex; flex-direction:column; gap:1px; font-size:12.5px; font-weight:500; min-width:0; }
.dnd-when { font-size:11px; font-weight:400; color:var(--muted); }
.dnd-line.is-set .dnd-what { font-weight:650; }

/* Covered by "Everything": ticked, but not a separate fact to argue with. */
.dnd-line.is-covered { opacity:.6; cursor:default; }
.dnd-line.is-covered:hover { background:transparent; }
.dnd-why { display:block; color:var(--subtle); }
/* Armed: one more press and somebody's phone rings. Deliberately not the
   house .chip.pending green -- nothing is pending, and this is the click you
   want people to notice they are about to make. */
.ws-dial.armed { background:color-mix(in srgb, var(--red) 15%, transparent);
  border-color:color-mix(in srgb, var(--red) 45%, transparent);
  color:var(--red); font-weight:700; }
/* THE ICON ONE HAS TO SHOUT. The worded button says "Call now" when it is
   armed, which is the whole signal; the icon beside their name keeps its
   glyph on purpose -- a header of icons cannot have one grow into a sentence
   -- so a 15% tint was all it had, and a 28px square going slightly pink is
   not a state anybody notices. Stefan, 10-14-2026, mid-call-attempt:
   "when I click on the call button next to her name, nothing's happening."
   It had armed; it just had no way of saying so. Solid red and a ring, same
   footprint, impossible to miss. */
/* The question itself. Fixed, and above everything, because it is anchored
   to a button that can sit inside a dock, a card or a panel -- each of which
   clips its children, and one of which transforms them. */
.ws-dial-ask { position:fixed; z-index:9500; max-width:min(260px, calc(100vw - 16px));
  padding:9px 12px; border-radius:10px; background:var(--panel, #1b1f22);
  border:1px solid color-mix(in srgb, var(--red) 45%, transparent);
  box-shadow:0 10px 28px rgba(0,0,0,.28); pointer-events:none; }
.ws-dial-ask b { display:block; font-size:13px; font-weight:650; color:var(--text);
  line-height:1.3; }
.ws-dial-ask span { display:block; margin-top:2px; font-size:11.5px; color:var(--muted); }
.ws-dial-ico.armed { background:var(--red); border-color:var(--red); color:#fff;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--red) 28%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .ws-dial-ico.armed { animation:dial-armed 1s ease-in-out infinite; }
}
@keyframes dial-armed {
  0%, 100% { box-shadow:0 0 0 3px color-mix(in srgb, var(--red) 28%, transparent); }
  50% { box-shadow:0 0 0 5px color-mix(in srgb, var(--red) 14%, transparent); }
}

/* The desk phone's call bar.

   Bottom-right, over everything, and deliberately the highest thing on the
   page: a call you cannot find the hang-up for is worse than no dialler at
   all. It is the one piece of UI that must never end up behind a panel. */
.sph-bar { position:fixed; right:18px; bottom:18px; z-index:9000;
  display:flex; align-items:center; gap:10px;
  padding:10px 12px; border-radius:14px;
  background:var(--panel-2); border:1px solid var(--line-strong);
  box-shadow:0 10px 30px rgba(0,0,0,.28);
  max-width:min(92vw, 420px); }
.sph-bar[hidden] { display:none; }
.sph-who { display:flex; flex-direction:column; gap:1px; min-width:0; flex:1 1 auto; }
.sph-who b { font-size:12.5px; letter-spacing:-.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sph-who span { font-size:11px; color:var(--muted); font-variant-numeric:tabular-nums; }
.sph-timer { font-size:12px; font-variant-numeric:tabular-nums; color:var(--muted);
  flex:0 0 auto; }
.sph-btn { flex:0 0 auto; padding:7px 12px; border-radius:9px; font-size:11.5px;
  font-weight:650; cursor:pointer; border:1px solid var(--line);
  background:var(--panel); color:var(--text); }
.sph-btn:hover { border-color:var(--subtle); }
.sph-btn.on { background:color-mix(in srgb, var(--blue) 16%, transparent);
  border-color:transparent; color:var(--blue); }
.sph-btn.sph-yes { background:var(--green); border-color:transparent; color:#06251a; }
.sph-btn.sph-no { background:var(--red); border-color:transparent; color:#fff; }
/* Live: a slow pulse, because a call still running in a background tab is
   the thing people forget. */
.sph-dot { width:9px; height:9px; border-radius:50%; background:var(--green);
  flex:0 0 auto; animation:sphPulse 1.6s ease-in-out infinite; }
.sph-in { border-color:var(--green); }
@keyframes sphPulse { 0%,100% { opacity:1; } 50% { opacity:.35; } }
@media (prefers-reduced-motion: reduce) { .sph-dot { animation:none; } }
/* Expanded: the panel Stefan sent from GoHighLevel -- who you are talking
   to, which number they see, and a keypad, because "press 1 for sales" is
   otherwise a dead end on a browser call. Collapsed is the default and the
   choice is remembered; a dialler that reopens big every call is one people
   close every call. */
.sph-bar.sph-big { flex-direction:column; align-items:stretch; gap:12px;
  width:290px; padding:14px; }
.sph-top { display:flex; align-items:center; gap:8px; }
.sph-kind { display:flex; align-items:center; gap:7px; font-size:11.5px;
  font-weight:650; color:var(--muted); flex:1 1 auto; }
.sph-x { margin-left:auto; border:0; background:transparent; color:var(--subtle);
  cursor:pointer; font-size:11px; padding:2px 6px; border-radius:6px; }
.sph-x:hover { color:var(--text); background:var(--lift-2); }
.sph-face { display:flex; flex-direction:column; align-items:center; gap:3px;
  text-align:center; padding:4px 0 2px; }
.sph-face b { font-size:15px; letter-spacing:-.015em; }
.avatar.sph-av { width:52px; height:52px; font-size:17px; margin-bottom:5px;
  background:var(--lift-2); color:var(--text); }
.sph-num { font-size:12.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.sph-pad { display:grid; grid-template-columns:repeat(3, 1fr); gap:6px; }
.sph-key { padding:9px 0; border-radius:9px; border:1px solid var(--line);
  background:var(--panel); color:var(--text); font-size:14px; font-weight:600;
  cursor:pointer; font-variant-numeric:tabular-nums; }
.sph-key:hover { background:var(--lift-2); border-color:var(--subtle); }
.sph-key:active { background:var(--lift-1); }
.sph-acts { display:flex; gap:6px; }
.sph-acts .sph-btn { flex:1 1 0; text-align:center; text-decoration:none; }
.sph-end { width:100%; padding:11px; font-size:13px; }
/* Ringing in: the same pulse as the live dot, in the accent that says answer. */
.sph-ring { width:9px; height:9px; border-radius:50%; background:var(--green);
  flex:0 0 auto; animation:sphPulse .9s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .sph-ring { animation:none; } }

@media (max-width: 560px) {
  .sph-bar { right:10px; left:10px; bottom:10px; max-width:none; }
  .sph-timer { display:none; }
}

/* Huddles (huddle.js). Docked at the foot of the sidebar while there is
   one open (the desk phone's bottom-right corner is the Send button, and a
   huddle stays open while people type); floating at the top of the screen
   on a phone or with the sidebar folded. Two rows either way: who and how
   long, then the faces and the controls. */
.hud-bar { display:flex; flex-direction:column; gap:8px; padding:10px;
  border-radius:12px; background:var(--panel-2); border:1px solid var(--line-strong);
  position:relative; }
.hud-bar[hidden] { display:none; }
.hud-bar[data-where="docked"] { margin-bottom:10px;
  border-color:color-mix(in srgb, var(--green) 45%, var(--line-strong)); }
.hud-bar[data-where="float"] { position:fixed; z-index:9000; top:10px; left:50%;
  transform:translateX(-50%); width:min(94vw, 360px);
  box-shadow:0 10px 30px rgba(0,0,0,.28); }
.hud-bar.hud-ringing { border-color:var(--green); }
.hud-row { display:flex; align-items:center; gap:8px; min-width:0; }
.hud-acts { gap:6px; }
.hud-grow { flex:1 1 0; text-align:center; }
/* Join: white on a green deep enough to carry it in both themes (about
   5:1). The desk phone's dark-on-green Answer is under 3:1 in light mode. */
.hud-bar .sph-yes { background:color-mix(in srgb, var(--green) 62%, black); color:var(--on-accent); }
.hud-ico { display:inline-grid; place-items:center; width:28px; height:28px;
  border-radius:50%; flex:0 0 auto; color:var(--ink-good);
  background:color-mix(in srgb, var(--green) 16%, transparent); }
.hud-ico-ring { animation:sphPulse .9s ease-in-out infinite; }
.hud-faces { display:flex; flex:1 1 auto; min-width:0; }
.avatar.hud-av { width:26px; height:26px; font-size:9.5px; margin-left:-6px;
  border:2px solid var(--panel-2); }
.avatar.hud-av:first-child { margin-left:0; }
.avatar.hud-ringing { opacity:.5; animation:sphPulse 1.4s ease-in-out infinite; }
.sph-btn.hud-sq { display:inline-grid; place-items:center; padding:7px 9px; }
.hud-pick { position:absolute; left:0; right:0; bottom:calc(100% + 6px);
  max-height:300px; overflow:auto; padding:6px; border-radius:12px;
  background:var(--panel-2); border:1px solid var(--line-strong);
  box-shadow:0 10px 30px rgba(0,0,0,.28); z-index:2; }
.hud-bar[data-where="float"] .hud-pick { bottom:auto; top:calc(100% + 6px); }
/* Over a panel's backdrop: the sidebar is under it, so the bar sits on top
   of the dimmed page, bottom-left, where nothing in the panel is. */
.hud-bar[data-where="overlay"] { position:fixed; z-index:9000; left:16px; bottom:16px;
  width:240px; box-shadow:0 10px 30px rgba(0,0,0,.28); }
.hud-pick[hidden] { display:none; }
.hud-pick-h { font-size:11px; font-weight:650; color:var(--muted); padding:6px 8px 4px; }
.hud-pick-row { display:flex; align-items:center; gap:8px; width:100%; padding:7px 8px;
  border:0; border-radius:8px; background:transparent; color:var(--text);
  font-size:12.5px; text-align:left; cursor:pointer; }
.hud-pick-row:hover { background:var(--lift-2); }
@media (prefers-reduced-motion: reduce) { .hud-ico-ring, .avatar.hud-ringing { animation:none; } }
/* The Huddle button, in the composer's tool row beside Voice memo: quiet
   until there is a huddle to join, then green so it reads across the room. */
.msg-huddle[hidden] { display:none; }
.msg-huddle.hud-live { color:var(--ink-good); border-color:color-mix(in srgb, var(--green) 55%, transparent);
  background:color-mix(in srgb, var(--green) 12%, transparent); }
/* The strip: across the top of the conversation the huddle is in. It is the
   answer to "am I actually calling her?", so it is green, full width, and
   says it in words. */
.hud-strip { display:flex; align-items:center; gap:10px; flex-wrap:wrap; position:relative;
  padding:10px 16px; border-bottom:1px solid var(--line);
  background:color-mix(in srgb, var(--green) 11%, var(--panel)); }
.hud-strip[hidden] { display:none; }
.hud-strip.hud-tone-ring { background:color-mix(in srgb, var(--green) 16%, var(--panel)); }
.hud-strip.hud-tone-off { background:color-mix(in srgb, var(--ink-warn) 10%, var(--panel)); }
.hud-strip.hud-tone-off .hud-ico { color:var(--ink-warn);
  background:color-mix(in srgb, var(--ink-warn) 16%, transparent); }
.hud-say { display:flex; flex-direction:column; gap:1px; min-width:0; flex:1 1 160px; }
.hud-say b { font-size:13.5px; letter-spacing:-.01em; }
.hud-say span { font-size:11.5px; color:var(--muted); }
.hud-strip .hud-faces { flex:0 0 auto; }
/* "Notes on": said out loud on screen, because the room is being written
   down for a brief (huddle.read_huddle). */
.hud-rec { display:inline-flex; align-items:center; gap:5px; flex:0 0 auto; font-size:11px; font-weight:650;
  color:var(--muted); padding:3px 8px; border-radius:999px; background:var(--lift-2); }
.hud-rec::before { content:""; width:7px; height:7px; border-radius:50%; background:var(--red); }
.hud-strip .hud-pick { left:auto; right:16px; width:240px; bottom:auto; top:calc(100% + 4px); }
/* On its way out: marked read while you are looking at Unread, the row no
   longer belongs to the list under it, so it folds rather than sitting there
   contradicting the filter. Height is set in JS from the row's own size --
   there is nothing to animate to from `auto`. */
.ibx-card.going { pointer-events:none; opacity:0; overflow:hidden;
  padding-top:0; padding-bottom:0; border-bottom-color:transparent;
  transition:height .18s ease, opacity .13s ease, padding .18s ease; }
@media (prefers-reduced-motion: reduce) { .ibx-card.going { transition:none; } }
.ibx-empty { padding:52px 24px; text-align:center; }
.ibx-empty strong { display:block; font-size:14px; margin-bottom:5px; }
.ibx-empty .helper { max-width:380px; margin:0 auto; }
@keyframes hpopIn { from { opacity:0; transform:translateY(-6px) scale(.985); }
                    to { opacity:1; transform:none; } }
.avatar.sm { width:26px; height:26px; font-size:10px; flex:0 0 auto; }
.ibx-push-x { border:0; background:transparent; color:var(--subtle); font-size:15px; line-height:1;
  padding:0 2px; cursor:pointer; }
.ibx-push-x:hover { color:var(--text); }
.tb-badge.is-hot { background:var(--ink-warn) !important; }

/* Shared conversation ---------------------------------------------------------
   The same frame wherever people talk: direct messages, thread panels, and the
   running conversation under a client. A note on a client is a message that
   happens to be about that client, and it should not have to be recognised as
   a different kind of object. */
.convo { display:flex; flex-direction:column; border:1px solid var(--line);
  border-radius:14px; overflow:hidden; background:var(--panel-2);
  min-height:320px; }
.convo .msg-log { background:var(--panel); max-height:min(560px, 60vh); }
/* Inside the client overlay there is a header, tabs and a composer to fit
   around, so the log takes less. Without this the composer is pushed past the
   bottom of the dialog and the conversation is readable but not writable --
   which is most of the way to useless. */
.brain-panel .convo .msg-log { max-height:min(360px, 40vh); }
.convo .msg-box { border-top:1px solid var(--line); }
.convo-head { display:flex; align-items:center; gap:12px; margin:10px 0 12px; }

/* Who said it, once per run rather than on every bubble. With two people it is
   noise; with a whole team on a client record it is the first thing you need. */
.msg-who { display:flex; align-items:center; gap:6px; margin:0 0 5px 2px;
  font-size:11px; font-weight:700; color:var(--muted); letter-spacing:-.01em; }
.avatar.xs { width:18px; height:18px; font-size:8px; flex:0 0 auto; }
/* Where a line came from, when that is not obvious. Deliberately quiet: it is
   provenance, not a label anybody is looking for. */
.msg-badge { padding:1px 6px; border-radius:20px; font-size:9px; font-weight:700;
  letter-spacing:.03em; text-transform:uppercase; color:var(--subtle);
  border:1px solid var(--line); }
.msg-blank { padding:34px 20px; text-align:center; margin:0; }

/* A client conversation is many voices, so the bubbles get more room than the
   two-person case needs. */
.convo .msg-turn { max-width:82%; }

/* What you are replying to, named above the composer. Quiet, dismissible, and
   impossible to miss -- a reply that silently lands on the wrong message is
   worse than no threading at all. */
.reply-to { display:flex; align-items:center; gap:8px; padding:7px 14px;
  background:color-mix(in srgb, var(--blue) 10%, transparent); border-top:1px solid var(--line);
  font-size:11.5px; color:var(--blue); font-weight:600; }
.reply-to[hidden] { display:none !important; }
.reply-to span { flex:1; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.reply-to button { display:grid; place-items:center; width:20px; height:20px;
  border:0; border-radius:6px; background:transparent; color:inherit;
  cursor:pointer; flex:0 0 auto; transition:background .13s ease; }
.reply-to button:hover { background:color-mix(in srgb, var(--blue) 20%, transparent); }

/* Threads open beside the conversation, never inside it -------------------
   Slack and ClickUp both push the conversation left and put the thread in its
   own column, and they are right: the point of a thread is that you have gone
   somewhere. A few indented bubbles do not read as somewhere, and an overlay
   that covers the conversation loses the message you are replying to. */
.convo-split { display:grid; grid-template-columns:minmax(0,1fr); gap:14px;
  align-items:stretch; }
.convo-split.open { grid-template-columns:minmax(0,1fr) 320px; }
@media (max-width:820px){
  .convo-split.open { grid-template-columns:minmax(0,1fr); }
}
.convo-panel { display:flex; flex-direction:column; overflow:hidden;
  background:var(--panel-2); border:1px solid var(--line); border-radius:14px;
  animation:panelIn .18s cubic-bezier(.2,.8,.3,1); }
.convo-panel[hidden] { display:none !important; }
.convo-panel .msg-panel-body { background:var(--panel);
  max-height:min(360px, 40vh); }
.brain-panel .convo-panel .msg-panel-body { max-height:min(320px, 36vh); }

/* The dialog grows to make room rather than squeezing the conversation into a
   column too narrow to read. */
/* .brain-panel sets `width`, and max-width cannot grow a box past its own
   width -- so opening a thread has to raise both or the dialog silently stays
   narrow and the two columns fight over 760px. */
/* ONE CARD, AND IT IS THIS WIDE EVERYWHERE.

   Stefan, 09-30-2026: "if we could just make it to one client card ... even
   if we have to make the client card under the clients larger to fit all the
   data we need." It was 600px because it was once a note dialog; it now
   carries the contract, the dates, the payments and the team's thread, and
   600 was making tables wrap that had no business wrapping. */
.note-dialog { width:min(760px, 100%); max-width:none;
  transition:width .22s cubic-bezier(.2,.8,.3,1); }
.note-dialog.wide { width:min(1040px, calc(100vw - 48px)); }

/* The card's body: one column of content with the dock down its right-hand
   edge, the way the Prospects Inbox card is built. Grid rather than a
   wrapper, so the dock spans every row without the panes having to be
   boxed inside something. */
/* The dock is a SIBLING of the scrolling body, not inside it, so it runs the
   full height of the card and its icons never scroll out of reach. */
.cc-split { display:flex; min-height:0; flex:1 1 auto; }
.cc-body { flex:1 1 auto; min-width:0; padding:0; }
.cc-body > #noteMeta { padding:16px 16px 0; }
.cc-body > .tab-pane { padding:0 16px 16px; }
.cc-dock { flex:0 0 49px; padding:12px 0; }

/* The agreements heading, with the button to make the next one beside it. */
.docs-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:4px; }
.docs-head h4 { margin:0; }

/* The mailing address: a block you paste onto a label, with its buttons
   beside it rather than under it so the box keeps its shape. */
.addr-row { display:flex; gap:10px; align-items:flex-start; }
.addr-row textarea { flex:1 1 auto; min-width:0; resize:vertical; line-height:1.45; }
.addr-side { display:flex; flex-direction:column; gap:6px; align-items:flex-start; }
.addr-side .helper { font-size:11px; }
/* A SAVED ADDRESS IS A LINE OF TEXT, not a box. An editable box under a
   ticked step reads as unfinished work and invites a stray keystroke into
   the one field nothing else on the card can rebuild. Stefan, 10-14-2026. */
.addr-read { display:flex; gap:10px; align-items:flex-start; }
.addr-text { flex:1 1 auto; min-width:0; margin:0; padding:7px 0; line-height:1.45;
  white-space:pre-wrap; overflow-wrap:anywhere; }
#addrBox > .helper { display:block; font-size:11px; margin-top:4px; }
#addrBox > .helper:empty { display:none; }
@media (max-width:640px) { .addr-row { flex-direction:column; }
  .addr-side { flex-direction:row; align-items:center; } }

/* Ringing them, on the end of the row that says how else to reach them. */
.reach-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.reach-row .reach-ways { margin:0; }
.reach-dial { display:inline-flex; align-items:center; }
.reach-dial:empty { display:none; }
/* The dock carries no words, so the pane names itself. */
.cc-pane-title { margin:14px 16px 10px; font-size:15px; letter-spacing:-.01em; }
/* The tick and the cross keep their colours from .tab-count; here they sit on
   the icon instead of beside a label, so the row's margin has to go. */
.cc-dot { position:absolute; top:2px; right:-1px; margin-left:0; }

/* ---- the dock, the first time somebody sees it -------------------------
   The labels are already on every button as data-label (the heading above
   the pane reads from them), so this is the same dock with its own words
   turned on -- not a second set to keep in step. It lasts one visit: the
   person closes it and the rail is icons from then on. */
/* `.ws-dock` sets the 48px rail and is declared 5,000 lines further down
   this file, so a bare `.cc-dock-learn` loses to it at equal specificity --
   the same trap the phone rule below already carries a note about. Two
   classes, and source order stops mattering. */
.ws-dock.cc-dock-learn { flex:0 0 184px; align-items:stretch; padding:10px 8px; gap:2px; }
.cc-dock-learn .ws-dock-btn { width:100%; height:auto; min-height:34px;
  display:flex; align-items:center; justify-content:flex-start; gap:10px;
  padding:6px 8px; }
/* A real element, not ::after on the button: the tooltip owns that pseudo
   already (data-tip renders through it at opacity 0 until hover), so a label
   drawn there is invisible and looks like the CSS simply did not apply. */
.cc-dock-word { order:1; flex:1 1 auto; min-width:0; text-align:left; font-size:12px;
  font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* The badge is markup-order BEFORE the label, so flex order puts it back on
   the end where it reads as a count OF that section rather than as something
   between the icon and its name. */
.cc-dock-learn .cc-dot, .cc-dock-learn .ws-dock-badge { position:static; order:2;
  margin-left:0; flex:0 0 auto; }
.cc-dock-hint { padding:2px 8px 10px; margin-bottom:4px;
  border-bottom:1px solid var(--line); }
.cc-dock-hint p { margin:0 0 8px; font-size:11.5px; line-height:1.45; color:var(--muted); }
.cc-dock-hint .button { width:100%; }
@media (max-width: 640px) {
  /* The dock is already lying down as a row of icons here, and 184px of
     labels would be the whole screen, so the tour waits for a bigger one.
     `.ws-dock-btn::after` is NOT touched: that pseudo is the tooltip, and
     hiding it here would take the hover name away on the one screen where
     the labels are not shown either. */
  .cc-split .ws-dock.cc-dock-learn { flex:0 0 auto; }
  .cc-dock-learn .ws-dock-btn { width:36px; }
  .cc-dock-word, .cc-dock-hint { display:none; }
  .cc-dock-learn .cc-dot { position:absolute; margin-left:0; }
}
@media (max-width: 640px) {
  /* A 48px rail down the side of a phone is most of the screen's width. */
  /* A 48px rail down the side of a phone is most of the screen's width, so
     the dock lies down above the pane instead.

     Written as `.cc-split .cc-dock` on purpose: `.ws-dock` sets
     flex-direction:column and is declared 5,000 lines further down this file,
     so at equal specificity it won -- inside the media query as well -- and
     the dock stayed a vertical rail on a phone. Found while writing the page
     guide, 09-30-2026, hours after shipping it. */
  .cc-split { flex-direction:column-reverse; }
  .cc-split .cc-dock { flex:0 0 auto; flex-direction:row; flex-wrap:wrap;
    justify-content:flex-start; border-left:0;
    border-bottom:1px solid var(--line); padding:8px 10px; }
}


/* Adding a client by hand. Two columns of short fields with the name across
   the top, because the name is the only one that is required. */
.nc-panel { width:min(560px, 100%); }
.nc-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px 14px; }
.nc-grid label { display:flex; flex-direction:column; gap:5px; font-size:11px;
  font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); }
.nc-grid label.nc-wide { grid-column:1 / -1; }
.nc-grid input, .nc-grid select { width:100%; font-size:13px; font-weight:400;
  letter-spacing:normal; text-transform:none; }
@media (max-width: 560px) { .nc-grid { grid-template-columns:1fr; } }

/* Which message the open thread belongs to. Without this the panel is a box of
   text floating next to a log, and you cannot tell what it is attached to. */
.msg-turn.threaded .msg-bubble { box-shadow:0 0 0 2px color-mix(in srgb, var(--blue) 45%, transparent); }
.msg-turn.threaded.mine .msg-bubble { box-shadow:0 0 0 2px rgba(255,255,255,.5),
  0 4px 14px color-mix(in srgb, var(--blue) 28%, transparent); }
.msg-replies.open { background:color-mix(in srgb, var(--blue) 22%, transparent);
  border-color:color-mix(in srgb, var(--blue) 45%, transparent); }

/* Contract dates -------------------------------------------------------------
   Changed through what happened -- froze, re-signed -- rather than by typing
   two dates, because the typing is what people forgot. */
.term-now { margin-bottom:16px; }
.term-dates { display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  padding:14px 16px; border:1px solid var(--line); border-radius:12px;
  background:var(--panel-2); }
.term-dates label { display:block; font-size:9.5px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle);
  margin-bottom:3px; }
.term-dates strong { font-size:15px; letter-spacing:-.01em;
  font-variant-numeric:tabular-nums; }
.term-arrow { color:var(--subtle); font-size:16px; }
.term-meta { margin-left:auto; font-size:11.5px; color:var(--muted); }
.term-over { color:var(--ink-bad); font-weight:650; }

/* Finished: the row of actions goes and this says why. Calm on purpose --
   it is an ending, not a warning. Re-sign and Correct the dates stay here. */
.tab-pane.is-finished .term-acts { display:none; }
.term-done { display:flex; align-items:center; gap:12px; margin-bottom:10px;
  padding:12px 14px; border-radius:12px; font-size:12.5px;
  background:color-mix(in srgb, var(--ink-good) 9%, transparent);
  border:1px solid color-mix(in srgb, var(--ink-good) 28%, transparent); }
.term-done-i { flex:none; width:18px; height:18px; fill:none; stroke:var(--ink-good);
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.term-done-t { flex:1; min-width:0; }
.term-done-t b { color:var(--ink-good); }
.term-done-t span { display:block; font-size:11.5px; color:var(--muted); margin-top:2px; }
.term-done-t .term-done-why { color:var(--text); font-style:italic; }
.term-done-acts { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
.term-err { margin:8px 0 0; padding:9px 11px; border-radius:9px; font-size:12.5px;
  background:var(--tint-red); color:var(--ink-bad); }

/* A contract agreed but not yet started. Given real weight on purpose: this is
   the thing that used to be invisible until it was too late. */
.term-queued { display:flex; align-items:center; gap:12px; margin-top:10px;
  padding:11px 14px; border-radius:12px; font-size:12.5px;
  background:color-mix(in srgb, var(--blue) 12%, transparent); border:1px solid color-mix(in srgb, var(--blue) 30%, transparent); }
.term-queued > div { flex:1; }
.term-queued .helper { display:block; font-size:10.5px; margin-top:2px; }

.term-actions { display:grid; gap:0; }
/* SECTIONS, NOT TILES.

   Stefan, 09-30-2026: "think hard about maybe changing the layout of the
   card. That way, it's not just these big, blocky cards within the client
   card." A filled, bordered, rounded box says "separate object" -- which is
   exactly wrong for Program, Freeze, Extension and Re-sign, four parts of
   one subject sitting inside a card that is already a box. A hairline above
   and a label does the same job with none of the weight. */
.term-card { padding:20px 0 0; margin-top:22px; border:0;
  border-top:1px solid var(--line-strong); border-radius:0; background:none; }
/* The rule needs air on BOTH sides. With none above it, it sat flush under
   the last row of the section before and read as that row's underline, not
   as the edge of the next section -- Stefan, 10-02-2026, drew red lines
   where he expected the sections to part. */
.term-actions > .term-card:first-child,
.tab-pane > .term-card:first-child { border-top:0; padding-top:0; margin-top:0; }
/* A section heading has to outrank the field labels under it, and those are
   uppercase too -- so the heading takes the full text colour while CUSTOM,
   LAST DAY and the rest stay muted. */
.term-card h4 { margin:0 0 4px; font-size:11px; font-weight:750;
  letter-spacing:.06em; text-transform:uppercase; color:var(--text); }
.term-card .helper { margin:0 0 10px; }
.term-chips { display:flex; gap:6px; margin-bottom:9px; flex-wrap:wrap; }
.term-chip { padding:6px 13px; border-radius:9px; font-size:12px; font-weight:650;
  cursor:pointer; color:var(--muted); background:var(--panel);
  border:1px solid var(--line);
  transition:border-color .14s ease, color .14s ease, background .14s ease; }
.term-chip:hover { border-color:var(--blue); color:var(--blue);
  background:color-mix(in srgb, var(--blue) 10%, transparent); }
.term-row { display:flex; align-items:flex-end; gap:8px; flex-wrap:wrap; }
.term-left.is-hot { border-color:var(--red); box-shadow:0 0 0 3px color-mix(in srgb, var(--red) 18%, transparent);
  transition:border-color .2s ease, box-shadow .2s ease; }
.term-left .term-row > input#leftNote { flex:1 1 260px; }
.term-row label { display:block; font-size:9.5px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); }
.term-row input, .term-row select { display:block; margin-top:4px;
  padding:8px 10px; border-radius:9px; background:var(--panel);
  border:1px solid var(--line); color:var(--text); font-size:12.5px; }
.term-row > input { flex:1; min-width:120px; }
/* A date in a term row gets the width it needs rather than whatever a flex
   item is left with -- these are the boxes in Stefan's screenshot with
   MM-DD-YYYY running into the calendar button. The visible control is
   .dp-text (datepick.js swaps every date input for its own), so that is what
   has to be sized; the native rule above only covers the moment before the
   swap. */
.term-row .dp-text { width:138px; }
.term-row > label { min-width:0; flex:0 1 auto; }
/* The custom-days box sits in its own label now, so it needs the width the
   bare input used to get from being a direct child. */
.term-row > label.term-lbl { flex:0 0 auto; }
.term-row > label.term-lbl input { width:110px; }
.term-row input:focus, .term-row select:focus { outline:none;
  border-color:var(--blue); box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }

.term-history { margin-top:22px; padding-top:20px; border-top:1px solid var(--line-strong); }

/* ---- Contract & dates: one action at a time (csm.html, 10-02-2026) --------
   A row of actions in the order they are used; only the one picked opens,
   as a single bordered form under the row (the one box on the pane, because
   it is the one thing being done). Presets are a segmented choice that shows
   what is picked, and every form ends with the line saying what it will do
   and a button that says it again. */
/* Each action is its own outlined button, in the payment bar's gradient --
   Stefan, 10-02-2026: "outline these and maybe add a little bit of that
   gradient color ... so whoever's using it understands that these are
   separate subsections and buttons", and "the same gradient color you did
   for the payment progress bar". They were plain words in one grey tray
   and read as a single strip of text. The border is a second background
   clipped to the border box, which is how a gradient becomes an outline.
   The one that is open fills with a light wash of the same gradient. Left
   the program stays red: it is the one that ends a client. */
.term-acts { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
.term-act { font:inherit; font-size:12.5px; font-weight:650; height:34px; padding:0 15px;
  border-radius:9px; border:1.5px solid transparent; color:var(--text); cursor:pointer;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(90deg, var(--blue), var(--violet)) border-box;
  box-shadow:0 1px 2px rgba(0,0,0,.06);
  transition:background .14s ease, color .14s ease, box-shadow .14s ease, transform .12s ease; }
.term-act:hover { background:
    linear-gradient(90deg, color-mix(in srgb, var(--blue) 7%, var(--panel)),
                    color-mix(in srgb, var(--violet) 7%, var(--panel))) padding-box,
    linear-gradient(90deg, var(--blue), var(--violet)) border-box; }
.term-act:active { transform:scale(.98); }
.term-act[aria-expanded="true"] { color:var(--blue-on-tint, var(--blue));
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--blue) 16%, var(--panel)),
                    color-mix(in srgb, var(--violet) 16%, var(--panel))) padding-box,
    linear-gradient(90deg, var(--blue), var(--violet)) border-box;
  box-shadow:0 2px 8px color-mix(in srgb, var(--violet) 18%, transparent); }
.term-act.is-danger { color:var(--ink-bad);
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(90deg, color-mix(in srgb, var(--ink-bad) 70%, transparent),
                    color-mix(in srgb, var(--ink-bad) 70%, transparent)) border-box; }
.term-act.is-danger:hover, .term-act.is-danger[aria-expanded="true"] {
  background:
    linear-gradient(color-mix(in srgb, var(--ink-bad) 10%, var(--panel)),
                    color-mix(in srgb, var(--ink-bad) 10%, var(--panel))) padding-box,
    linear-gradient(90deg, var(--ink-bad), var(--ink-bad)) border-box; }
.term-act:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }

.term-form { margin-top:10px; padding:14px 16px 16px; border:1px solid var(--line); border-radius:12px;
  background:var(--panel); display:grid; gap:12px; }
.term-form[hidden] { display:none; }
.term-form-h { display:grid; gap:2px; }
.term-form-h h4 { margin:0; font-size:13.5px; font-weight:750; color:var(--text); }
.term-form-h .helper { margin:0; }
.term-more-next { padding-top:12px; border-top:1px solid var(--line); }
.term-sub { margin:2px 0 -4px; font-size:9.5px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }
.term-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:10px; }
.term-grid label { display:grid; gap:4px; min-width:0; font-size:9.5px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); }
.term-grid label .helper { text-transform:none; letter-spacing:0; font-weight:400; }
.term-grid input, .term-grid select, .term-field input, .term-field select { width:100%; min-width:0;
  padding:8px 10px; border-radius:9px; background:var(--panel); border:1px solid var(--line);
  color:var(--text); font-size:12.5px; font-weight:400; letter-spacing:0; text-transform:none; }
.term-grid input:focus, .term-grid select:focus, .term-field input:focus, .term-field select:focus {
  outline:none; border-color:var(--blue); box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.term-grid .dp, .term-grid .dp-text { width:100%; }
.term-field { display:grid; grid-template-columns:74px minmax(0, 1fr); align-items:center; gap:10px; }
.term-k { font-size:9.5px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); }
.term-field-v { display:flex; align-items:center; gap:10px; flex-wrap:wrap; min-width:0; }
.seg, .term-seg { display:inline-flex; flex-wrap:wrap; border:1px solid var(--line-strong); border-radius:9px; overflow:hidden; }
.seg button, .term-seg button { font:inherit; font-size:12px; font-weight:650; height:32px; padding:0 13px; border:0;
  border-left:1px solid var(--line); background:var(--panel); color:var(--muted); cursor:pointer; }
.seg button:first-child, .term-seg button:first-child { border-left:0; }
.seg button:hover, .term-seg button:hover { color:var(--text); }
.seg button[aria-pressed="true"], .term-seg button[aria-pressed="true"] { background:color-mix(in srgb, var(--blue) 13%, var(--panel)); color:var(--blue); }
.seg button:focus-visible, .term-seg button:focus-visible { outline:2px solid var(--blue); outline-offset:-2px; }
.term-custom { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--muted); }
.term-custom[hidden] { display:none; }
.term-custom input { width:76px; padding:7px 9px; border-radius:9px; border:1px solid var(--line);
  background:var(--panel); color:var(--text); font-size:12.5px; }
/* Custom holds a count AND a day, so the row is wider than the old three
   digits: the date box has to fit MM-DD-YYYY with its calendar button.
   It wraps rather than overflowing -- on a phone the card is 349px wide and
   the three pieces side by side put the calendar button off the screen. */
.term-custom-date { gap:9px; flex-wrap:wrap; max-width:100%; }
.term-custom-date .dp { max-width:100%; }
.term-custom-date .dp-text { width:152px; max-width:100%; }
.term-custom-k { font-size:12px; color:var(--muted); white-space:nowrap; }
.term-effect { margin:0; padding:9px 11px; border-radius:9px; font-size:12.5px; font-variant-numeric:tabular-nums;
  background:color-mix(in srgb, var(--blue) 8%, transparent); color:var(--text); }
.term-effect:empty { display:none; }
/* The same line, carrying a refusal instead of a result -- a date before the
   end date, or one more than a year out. */
.term-effect.is-bad { background:var(--tint-red); color:var(--ink-bad); }
.term-effect s { color:var(--muted); }
.term-foot { display:flex; justify-content:flex-end; gap:8px; flex-wrap:wrap; }
.button.is-danger { background:var(--red); }
.button.is-danger:hover { background:color-mix(in srgb, var(--red) 86%, white);
  box-shadow:0 3px 12px color-mix(in srgb, var(--red) 40%, transparent); }
@media (max-width:520px) {
  .term-field { grid-template-columns:minmax(0, 1fr); gap:4px; }
}

/* The agreement: one line each, the same shape whatever system it came from. */
.term-docs .docs-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:4px; }
.term-docs .docs-head h4 { margin:0; }
.term-doc { display:flex; align-items:center; gap:10px; padding:9px 0; border-top:1px solid var(--line); }
.term-doc:first-child { border-top:0; }
.term-doc > svg { color:var(--blue); flex:none; }
.term-doc-t { flex:1; min-width:0; display:grid; gap:1px; }
.term-doc-t b { font-size:12.5px; font-weight:650; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.term-doc-t span { font-size:11px; color:var(--muted); }
.term-doc-a { display:flex; align-items:center; gap:6px; }
/* Calls booked: the count per page sits above the list, behind a rule. */
.calls-sum { margin:4px 0 10px; padding-bottom:4px; border-bottom:1px solid var(--line-strong); }
.calls-sum .term-doc-t span b { color:var(--text); }
/* Whether the client is under a live agreement. One line, the colour saying
   which way it went, and the cure beside it rather than on a tab of its own. */
.doc-cover { display:flex; align-items:center; gap:10px; padding:9px 11px; margin-bottom:10px;
  border-radius:8px; border:1px solid var(--line); background:var(--lift-1,rgba(255,255,255,.03)); }
.doc-cover.gap { border-color:color-mix(in srgb,var(--ink-warn) 45%,var(--line));
  background:color-mix(in srgb,var(--ink-warn) 9%,transparent); }
.doc-cover-t { flex:1; min-width:0; display:grid; gap:1px; }
.doc-cover-t b { font-size:12.5px; font-weight:650; }
.doc-cover.gap .doc-cover-t b { color:var(--ink-warn); }
.doc-cover-t span { font-size:11px; color:var(--muted); }
.term-upload { margin-top:10px; display:inline-flex; align-items:center; gap:5px; }
#contractDrop { margin-top:8px; }

/* The change log, folded to its latest entry. */
.term-history-wrap { margin-top:22px; padding-top:16px; border-top:1px solid var(--line-strong); }
.term-history-wrap > summary { cursor:pointer; list-style:none; font-size:11px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--text); }
.term-history-wrap > summary::-webkit-details-marker { display:none; }
.term-history-wrap > summary::before { content:"▸ "; color:var(--subtle); }
.term-history-wrap[open] > summary::before { content:"▾ "; }
.term-history-wrap > summary .helper { text-transform:none; letter-spacing:0; font-weight:500; }
.term-history-wrap .term-history { margin-top:10px; padding-top:0; border-top:0; }
.term-history h4 { font-size:11px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); margin:0 0 8px; }
.term-log { display:flex; align-items:flex-start; gap:12px; padding:9px 0;
  border-top:1px solid var(--line); font-size:11.5px; color:var(--muted); }
.term-log-kind { flex:0 0 74px; font-weight:750; font-size:10px;
  letter-spacing:.04em; text-transform:uppercase; padding:2px 0; }
.term-log-kind.resign { color:var(--ink-good); }
.term-log-kind.freeze { color:var(--ink-info); }
/* Goodwill, not a pause and not a fix: its own colour, since a correction
   already owns the amber. */
.term-log-kind.extension { color:var(--ink-violet); }
.term-log-kind.program { color:var(--ink-info); }
.term-log-kind.correction { color:var(--ink-warn); }
.term-log-kind.status { color:var(--muted); }
.term-log-kind.unfreeze { color:var(--ink-info); }
/* A month-to-month roll is routine bookkeeping, not a decision somebody made
   — it should read quietly next to a freeze or a re-sign. */
.term-log-kind.rolled { color:var(--muted); }
.term-log-what { flex:1; line-height:1.5; overflow-wrap:anywhere; }
.term-log-who { flex:0 0 auto; text-align:right; font-size:10.5px;
  color:var(--muted); white-space:nowrap; }
.term-log-who .helper { font-size:10px; }

/* The queued term, on the roster row. Small, but it is the whole point: the
   end date on screen is no longer the last word. */
/* THE blank space. This is one line on ONE row out of seventy -- "early
   re-sign · → 09-28-2027 from 09-28-2026" -- and with the table's global
   `td { white-space: nowrap }` it could not break, so the End column was
   sized to it and every column after it was shoved 400px to the right.
   It wraps inside a date's width now; the row it is on grows a line or two
   and the other sixty-nine get the space back. */
.next-term { font-size:10px; color:var(--blue); font-weight:650;
  margin-top:2px; white-space:normal; max-width:124px; line-height:1.35; }
/* The new end date stays on one line -- it is the fact on this row. The
   words under it may wrap to two; they are only there to say from when. */
.next-term b { display:block; font-weight:inherit; white-space:nowrap; }
.next-term span { display:block; }
.next-term span { color:var(--subtle); font-weight:500; }

/* The queued term on a roster row. Green when it is a re-sign: it is the one
   piece of good news in a column of churn signals and should not be read as
   another warning. */
.next-term.good { color:var(--ink-good); }
.next-term.good span { color:rgba(138,240,189,.6); }

/* Sortable columns. The glyph used to appear only on hover, so a wide table
   was not covered in arrows -- and Stefan, who the table is for, concluded
   that only the column already sorted could be sorted, because nothing told
   him otherwise. Faint at rest now: quiet enough not to be noise, present
   enough to be found. */
th.sortable { cursor:pointer; user-select:none; position:relative;
  transition:color .14s ease; }
th.sortable:hover { color:var(--text); }
th.sortable::after { content:"↕"; margin-left:5px; opacity:.32; font-size:9px;
  transition:opacity .14s ease; }
th.sortable:hover::after { opacity:.7; }
th.sortable.sorted { color:var(--blue); }
th.sortable.sorted::after { opacity:1; content:"↑"; }
th.sortable.sorted[data-dir="down"]::after { content:"↓"; }

/* The client's status, as a control rather than a label. Reads as text until
   you go near it, because it sits in a line of plain metadata and should not
   shout over the client's name. */
.status-pick { display:inline-flex; align-items:center; gap:5px; padding:2px 8px;
  margin:-2px 0; border-radius:7px; cursor:pointer; font:inherit;
  font-weight:700; color:var(--muted); background:transparent;
  border:1px solid transparent;
  transition:background .14s ease, border-color .14s ease, color .14s ease; }
.status-pick:hover { background:var(--panel); border-color:var(--line);
  color:var(--text); }
.status-pick svg { opacity:.5; }
.status-pick:hover svg { opacity:1; }
/* A coach reads the status; only the VA and admins move it (10-20-2026). */
.status-pick.is-label, .status-pick.is-label:hover { cursor:default;
  background:transparent; border-color:transparent; color:var(--muted); }

/* Renewal runway ------------------------------------------------------------
   Ninety days is "put it on the list", sixty is "book the call", thirty is
   "nobody has spoken to them and it is happening". One colour flattened all
   three into the same shrug, so each stage gets its own temperature. */
.badge.renew-ahead { border-color:rgba(234,203,94,.32); color:var(--ink-warn);
  background:rgba(234,203,94,.10); }
.badge.renew-soon { border-color:rgba(245,174,78,.42); color:var(--ink-warn);
  background:rgba(245,174,78,.16); }
.badge.renew-urgent { border-color:rgba(251,100,114,.5); color:var(--ink-bad);
  background:rgba(251,100,114,.18); font-weight:750; }

/* Photo permission, beside the name. Three states and never two: "not asked"
   has to stay visibly different from "no", because silence is not a refusal
   and it is certainly not consent. */
.photo-chip { display:inline-flex; align-items:center; gap:4px; margin-left:7px;
  padding:1px 7px; border-radius:20px; font-size:9.5px; font-weight:700;
  letter-spacing:-.005em; white-space:nowrap; vertical-align:middle;
  border:1px solid transparent; }
.photo-chip.ok { color:var(--ink-good); background:rgba(53,201,143,.12);
  border-color:rgba(53,201,143,.3); }
.photo-chip.no { color:var(--ink-bad); background:rgba(251,100,114,.14);
  border-color:rgba(251,100,114,.36); }
.photo-chip.unknown { color:var(--subtle); border-color:var(--line); }
.photo-chip svg { opacity:.85; }
/* The one chip beside a name that does something: a button's edge, a
   pointer and a hover, so it is never mistaken for a label. */
.photo-chip.chip-btn { cursor:pointer; font-family:inherit; padding:2px 8px;
  box-shadow:0 1px 1px rgba(0,0,0,.06); }
.photo-chip.chip-btn.unknown { color:var(--blue); border-color:color-mix(in srgb, var(--blue) 45%, transparent);
  background:color-mix(in srgb, var(--blue) 8%, var(--panel)); }
.photo-chip.chip-btn:hover { filter:brightness(.97); border-color:currentColor; }
.photo-chip.chip-btn:focus-visible { outline:2px solid var(--blue); outline-offset:1px; }
.photo-chip.chip-btn svg { opacity:1; }

/* Onboarding, beside the name. Complete is deliberately the quieter of the
   two: after the backfill it is true of the whole roster, and a loud chip on
   every row is wallpaper that buries the one row still owing a step. */
.onboard-chip { display:inline-flex; align-items:center; gap:4px; margin-left:7px;
  padding:1px 7px; border-radius:999px; font-size:9.5px; font-weight:700;
  letter-spacing:.01em; vertical-align:middle; border:1px solid transparent;
  white-space:nowrap; }
.onboard-chip.ok { color:var(--ink-good); background:rgba(53,201,143,.10);
  border-color:rgba(53,201,143,.26); }
.onboard-chip.open { color:var(--ink-warn); background:rgba(245,174,78,.16);
  border-color:rgba(245,174,78,.4); font-size:10px; padding:2px 8px; }
.onboard-chip svg { opacity:.9; }
.onboard-chip.open svg { display:none; }

/* VA onboarding: the five steps first, one line each. Stefan, 10-02-2026,
   approving the pane review. A done step is a green tick and muted words,
   with who ticked it and when on the right -- not a strike-through, which
   made the finished ones the hardest to read. The number is the order to
   do them in, which is most of what an SOP is. */
.onb-lead { margin:0 0 6px; font-size:12px; color:var(--muted); }
.onb-lead b { color:var(--text); font-weight:700; margin-right:4px; }
.onb-lead[hidden] { display:none !important; }
.steps { list-style:none; margin:0; padding:0; border-top:1px solid var(--line); }
.step-item { border-bottom:1px solid var(--line); }
.step { display:flex; align-items:center; gap:10px; width:100%; min-height:42px;
  padding:8px 6px; text-align:left; cursor:pointer; background:none; border:0;
  border-radius:8px; color:var(--text); font:inherit;
  transition:background .14s ease; }
.step:hover { background:var(--lift-1, rgba(0,0,0,.03)); }
.step:focus-visible { outline:2px solid var(--blue); outline-offset:-2px; }
.step-tick { display:grid; place-items:center; width:22px; height:22px;
  flex:0 0 auto; border-radius:50%; font-size:10.5px; font-weight:800;
  color:var(--subtle); border:1px solid var(--line-strong);
  transition:background .14s ease, color .14s ease, border-color .14s ease; }
.step.done .step-tick { background:rgba(53,201,143,.16); color:var(--ink-good);
  border-color:rgba(53,201,143,.42); }
.step-label { flex:0 1 auto; min-width:0; font-size:12.5px; font-weight:650;
  letter-spacing:-.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.step.done .step-label { color:var(--muted); font-weight:600; }
.step-meta { flex:1 1 0; min-width:0; margin-left:auto; text-align:right;
  font-size:11px; color:var(--muted); white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; }
.step-meta.warn { color:var(--ink-warn); }
/* The address, inside its step, lined up under the step's words. */
.step-item .addr-row { padding:0 6px 12px 38px; }
.addr-row textarea { min-height:54px; }

/* The facts under the steps (photo permission) and under the profile (the
   Client Onboarding Form). A label column and a line each, rather than more
   boxed sections. */
.onb-facts { margin-top:16px; display:grid; gap:12px; }
.onb-fact { display:grid; grid-template-columns:64px minmax(0,1fr); gap:10px; align-items:start; }
.onb-fact-k { padding-top:9px; font-size:10.5px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); }
.onb-fact-v .helper { margin:6px 0 0; font-size:11px; }
/* Under the profile the label is three words, too long for the column: it
   sits on top of its line instead. */
.profile-form { margin:0 0 16px; padding-bottom:14px; border-bottom:1px solid var(--line); }
.profile-form .onb-fact { grid-template-columns:minmax(0,1fr); gap:6px; }
.profile-form .onb-fact-k { padding-top:0; }
.onb-form { display:flex; align-items:center; gap:9px; min-height:32px; }
.onb-form .form-state-mark { margin-top:0; }
.onb-form.good .form-state-mark { color:var(--ink-good); background:color-mix(in srgb, var(--ink-good) 18%, transparent); }
.onb-form.bad .form-state-mark { color:var(--ink-bad); background:color-mix(in srgb, var(--ink-bad) 18%, transparent); }
.onb-form.quiet .form-state-mark { color:var(--muted); background:var(--lift-2, rgba(0,0,0,.06)); }
.onb-form-text { flex:1 1 auto; min-width:0; font-size:12px; color:var(--muted); line-height:1.4; }
.onb-form-text b { font-weight:700; color:var(--text); }
.onb-form.good .onb-form-text b { color:var(--ink-good); }
.onb-form.bad .onb-form-text b { color:var(--ink-bad); }
.onb-form-acts { display:flex; gap:6px; flex:0 0 auto; }
/* Photo permission as one choice: the picked answer carries its meaning --
   green may post, red may not, plain for nobody has asked. */
/* The onboarding answer. "Book it on save" is the one that does something
   rather than records something, so it is the one that looks like a button
   you are about to press. */
.onb-seg { margin-top:6px; }
.onb-seg button[data-onb="booked"][aria-pressed="true"] { color:var(--ink-good);
  background:color-mix(in srgb, var(--green) 14%, transparent); }
.onb-seg button[data-onb="owed"][aria-pressed="true"] { color:var(--ink-warn);
  background:color-mix(in srgb, var(--amber) 16%, transparent); }
.onb-seg button[data-onb="now"][aria-pressed="true"] { color:var(--blue);
  background:color-mix(in srgb, var(--blue) 14%, transparent); }
.photo-seg button[data-photo="yes"][aria-pressed="true"] { color:var(--ink-good);
  background:color-mix(in srgb, var(--ink-good) 14%, var(--panel)); }
.photo-seg button[data-photo="no"][aria-pressed="true"] { color:var(--ink-bad);
  background:color-mix(in srgb, var(--ink-bad) 14%, var(--panel)); }
.photo-seg button[data-photo=""][aria-pressed="true"] { color:var(--text);
  background:var(--lift-2, rgba(0,0,0,.06)); }
@media (max-width:640px) {
  .step-meta { display:none; }
  .photo-seg { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); width:100%; }
  .photo-seg button { height:auto; min-height:36px; padding:6px 8px; line-height:1.25; }
  .onb-fact { grid-template-columns:minmax(0,1fr); gap:4px; }
  .onb-fact-k { padding-top:0; }
  .onb-form { flex-wrap:wrap; }
  .step-item .addr-row { padding-left:6px; }
}

.tab-count { margin-left:5px; padding:1px 6px; border-radius:9px; font-size:9.5px;
  font-weight:800; background:rgba(245,174,78,.2); color:var(--ink-warn); }
.tab-count[hidden] { display:none !important; }
/* Finished reads as an answer, not an absence. The badge used to disappear at
   zero-left, which made "all five done" look exactly like "no checklist here". */
.tab-count.ok { display:inline-grid; place-items:center; padding:2px 6px;
  background:rgba(53,201,143,.2); color:var(--ink-good); }
/* Missing is a fact, not a degree of concern, so it is a red cross rather
   than the amber a warning badge would use. */
.tab-count.missing { display:inline-grid; place-items:center; padding:2px 6px;
  background:rgba(232,86,86,.18); color:var(--ink-bad); }

/* Said out loud on the Onboarding tab, and it takes the place of the "tick them
   as they are done" instruction rather than sitting above it -- once they are
   all ticked the instruction is noise. */
.onboard-complete { display:flex; align-items:center; gap:10px;
  margin:0 0 10px; padding:10px 12px; border-radius:11px;
  background:rgba(53,201,143,.10); border:1px solid rgba(53,201,143,.34); }
.onboard-complete[hidden] { display:none !important; }
.onboard-complete-tick { display:grid; place-items:center; width:22px; height:22px;
  flex:0 0 auto; border-radius:50%; color:var(--ink-good);
  background:rgba(53,201,143,.18); border:1px solid rgba(53,201,143,.42); }
.onboard-complete-body { display:flex; flex-direction:column; gap:1px; min-width:0; }
.onboard-complete-body strong { font-size:12.5px; font-weight:700;
  letter-spacing:-.01em; color:var(--ink-good); }
/* --subtle is the app's faintest ink and it does not survive the green tint
   this line sits on: 3.0:1 in light, 2.8:1 in dark, against 4.5:1 for text
   this size. --muted clears it in both. */
.onboard-complete-body span { font-size:11px; color:var(--muted); }

/* The client's onboarding form on the Onboarding tab: the same box as
   "Onboarding complete", in three tones. Tints are the ink at low alpha so
   both themes keep the same relationship between the box and its words. */
.form-state { display:flex; align-items:flex-start; gap:10px; padding:10px 12px; border-radius:11px; border:1px solid transparent; }
.form-state.good { background:color-mix(in srgb, var(--ink-good) 10%, transparent); border-color:color-mix(in srgb, var(--ink-good) 34%, transparent); }
.form-state.bad { background:color-mix(in srgb, var(--ink-bad) 10%, transparent); border-color:color-mix(in srgb, var(--ink-bad) 34%, transparent); }
.form-state.quiet { background:var(--lift-1, rgba(255,255,255,.03)); border-color:var(--line); }
.form-state-mark { display:grid; place-items:center; width:22px; height:22px; flex:0 0 auto; border-radius:50%; font-size:12px; font-weight:800; margin-top:1px; }
.form-state.good .form-state-mark { color:var(--ink-good); background:color-mix(in srgb, var(--ink-good) 18%, transparent); }
.form-state.bad .form-state-mark { color:var(--ink-bad); background:color-mix(in srgb, var(--ink-bad) 18%, transparent); }
.form-state.quiet .form-state-mark { color:var(--muted); background:var(--lift-2, rgba(255,255,255,.06)); }
.form-state-body { display:flex; flex-direction:column; gap:2px; min-width:0; }
.form-state-body strong { font-size:12.5px; font-weight:700; letter-spacing:-.01em; }
.form-state.good .form-state-body strong { color:var(--ink-good); }
.form-state.bad .form-state-body strong { color:var(--ink-bad); }
.form-state-body span { font-size:11px; color:var(--muted); }
.form-answers { margin-top:8px; display:flex; flex-direction:column; gap:8px; }
/* Question, then its answer in a box under it. Grey label over plain text read
   as one block of words (10-15-2026), so the question is bold ink and the
   answer sits in its own tinted box with an accent edge. */
.form-screen + .form-screen { padding-top:8px; border-top:1px solid var(--line); }
.form-screen-title { font-size:11px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; color:var(--blue); margin-bottom:4px; }
.form-screen-title .helper { text-transform:none; letter-spacing:0; font-weight:500; color:var(--subtle); }
.form-item + .form-item { margin-top:6px; }
.form-item-label { font-size:12.5px; font-weight:650; line-height:1.3; color:var(--text); }
.form-item-value { margin-top:2px; padding:3px 9px; border-left:3px solid var(--blue-line);
  border-radius:4px 8px 8px 4px; background:var(--lift-2); font-size:13px; line-height:1.35; white-space:pre-wrap; }
.form-item-value.ink-warn { border-left-color:var(--ink-warn); background:var(--tint-amber); }

/* An empty status group still shows its heading, so the set of buckets is
   always visible. Dimmed, because there is nothing to do in it. */
.group-head.empty { opacity:.5; }
.group-empty { margin-left:8px; font-size:10.5px; color:var(--subtle);
  font-style:italic; }

/* Tile panel -----------------------------------------------------------------
   A number on a card is a question -- "who are the eight?" -- and it used to
   be a dead end. Clicking one opens the list beside the page rather than
   navigating away, so you can look at them, act on one, and still be where you
   started. Same instinct as the thread panel. */
.metric-open { display:block; width:100%; text-align:left; cursor:pointer;
  font:inherit; position:relative;
  transition:border-color .16s ease, transform .12s ease; }
.metric-open:hover { border-color:var(--line-strong); transform:translateY(-1px); }
.metric-open:active { transform:translateY(0); }
/* A quiet arrow rather than a button: the whole card is the target. */
.metric-open::after { content:"›"; position:absolute; top:12px; right:14px;
  font-size:15px; line-height:1; color:var(--subtle); opacity:0;
  transition:opacity .16s ease, transform .16s ease; }
.metric-open:hover::after { opacity:.8; transform:translateX(2px); }
.metric-open:focus-visible { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 16%, transparent); }

.tile-panel { position:fixed; top:0; right:0; bottom:0; width:min(420px, 92vw);
  z-index:55; display:flex; flex-direction:column; background:var(--panel);
  border-left:1px solid var(--line);
  box-shadow:var(--shadow-lg);
  animation:tileIn .2s cubic-bezier(.2,.8,.3,1); }
.tile-panel[hidden] { display:none !important; }
@keyframes tileIn { from { transform:translateX(24px); opacity:0; }
                    to { transform:none; opacity:1; } }
/* The page slides over rather than being covered, so the roster underneath
   stays readable and keeps its place. */
body.tile-open .app-shell { padding-right:min(420px, 92vw);
  transition:padding-right .2s cubic-bezier(.2,.8,.3,1); }
@media (max-width:900px){ body.tile-open .app-shell { padding-right:0; } }

.tile-panel-head { display:flex; align-items:flex-start; justify-content:space-between;
  gap:10px; padding:16px 16px 13px; border-bottom:1px solid var(--line);
  background:var(--panel-2); }
.tile-panel-head strong { display:block; font-size:15px; letter-spacing:-.015em; }
.tile-panel-head .helper { display:block; margin-top:3px; font-size:11px;
  line-height:1.45; }
.tile-panel-tools { display:flex; align-items:center; gap:7px; flex-wrap:wrap;
  padding:11px 16px; border-bottom:1px solid var(--line); }
.tile-panel-tools input, .tile-panel-tools select { flex:1; min-width:110px;
  padding:7px 10px; border-radius:9px; font-size:12px; background:var(--panel-2);
  border:1px solid var(--line); color:var(--text); }
.tile-panel-tools input:focus, .tile-panel-tools select:focus { outline:none;
  border-color:var(--blue); box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.tile-sort { display:flex; gap:2px; padding:2px; border-radius:9px;
  background:var(--panel-2); border:1px solid var(--line); flex:0 0 auto; }
.tile-sort button { padding:5px 10px; border:0; border-radius:7px;
  background:transparent; color:var(--subtle); font-size:11px; font-weight:650;
  cursor:pointer; transition:background .14s ease, color .14s ease; }
.tile-sort button:hover { color:var(--text); }
.tile-sort button.on { background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--blue); }

.tile-panel-body { flex:1; overflow-y:auto; scrollbar-width:thin; }
.tile-row { display:block; width:100%; text-align:left; padding:12px 16px;
  border:0; border-bottom:1px solid var(--line); background:transparent;
  cursor:pointer; transition:background .13s ease; }
.tile-row:hover { background:var(--lift-2); }
.tile-row-top { display:flex; align-items:center; justify-content:space-between;
  gap:10px; }
.tile-row-name { font-size:13px; font-weight:650; letter-spacing:-.01em; }
.tile-row-sub { display:block; margin-top:3px; font-size:11px;
  color:var(--subtle); }
.tile-row .photo-chip { margin:6px 0 0; }

/* Clients the live roster does not list, surfaced by name search. A finished
   client who has just re-signed is exactly who you go looking for, and until
   now they could not be found at all. Marked clearly so nobody mistakes one
   for an active client. */
.off-roster { margin:10px 0 4px; border:1px solid rgba(245,174,78,.3);
  border-radius:12px; overflow:hidden; background:rgba(245,174,78,.06); }
.off-roster[hidden] { display:none !important; }
.off-roster-head { padding:8px 14px; font-size:10px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-warn);
  border-bottom:1px solid rgba(245,174,78,.2); }
.off-roster-row { display:flex; align-items:center; justify-content:space-between;
  gap:12px; width:100%; padding:10px 14px; border:0; background:transparent;
  cursor:pointer; text-align:left; transition:background .13s ease; }
.off-roster-row:hover { background:rgba(245,174,78,.1); }
.off-roster-row strong { font-size:13px; }
.off-roster-row .helper { display:block; font-size:11px; margin-top:2px; }
.off-roster-open { flex:0 0 auto; font-size:11px; font-weight:700; color:var(--ink-warn); }

/* P&L -----------------------------------------------------------------------
   A month whose costs were never entered is marked, not hidden. The gross
   profit on those months is higher than the truth, and a runway model built on
   them would say the business is in the best shape it has ever been in. */
.pnl-row { cursor:pointer; transition:background .13s ease; }
.pnl-row:hover { background:var(--lift-2); }
.pnl-row.partial td { opacity:.72; }
.pnl-row.partial td:first-child b::after { content:"*"; color:var(--ink-warn);
  margin-left:3px; }
.pnl-gap { font-size:10.5px; font-weight:650; color:var(--ink-warn);
  background:rgba(245,174,78,.13); border:1px solid rgba(245,174,78,.28);
  border-radius:20px; padding:1px 8px; white-space:nowrap; }
.pnl-detail td { background:var(--panel-2); padding:12px 14px; }
.pnl-chip { display:inline-flex; gap:6px; margin:0 6px 6px 0; padding:3px 10px;
  border-radius:20px; font-size:11.5px; color:var(--muted);
  background:var(--panel); border:1px solid var(--line); }
.pnl-chip b { color:var(--text); font-variant-numeric:tabular-nums; }
.pnl-total td { border-top:1px solid var(--line-strong); }
td.num, th.num { text-align:right; font-variant-numeric:tabular-nums; }
td.num.neg { color:var(--ink-bad); }
.term-hint { margin:9px 0 0; font-size:11.5px; }

/* Payment standing. Deliberately amount-free so it can be shown to coaches:
   knowing a client's card is failing is what lets them flag it, and it is a
   different thing from knowing what the contract is worth. */
.pay-chip { display:inline-flex; align-items:center; gap:4px; margin-left:7px;
  padding:1px 7px; border-radius:20px; font-size:9.5px; font-weight:750;
  letter-spacing:-.005em; white-space:nowrap; vertical-align:middle;
  border:1px solid transparent; }
.pay-chip.bad { color:var(--ink-bad); background:var(--tint-red);
  border-color:var(--ink-bad); }
/* Quieter than past due, but still legible -- which it was not. It used a pale
   green at 72% alpha over nothing, chosen against a dark panel; on a white page
   that is a rumour of a chip. It carries its own tint and a real border now,
   and both come from the theme so light gets the dark green and dark gets the
   bright one. */
.pay-chip.ok { color:var(--ink-good); background:var(--tint-green);
  border-color:color-mix(in srgb, var(--ink-good) 45%, transparent); }
.pay-chip.unknown { color:var(--subtle); border-color:var(--line); }
.past-pane[hidden] { display:none !important; }
.wb-state { padding:5px 9px; border-radius:8px; font-size:11.5px;
  background:var(--panel-2); border:1px solid var(--line); color:var(--text); }
.wb-state:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.wb-who { font-size:9.5px; color:var(--subtle); margin-top:3px; }

/* The MM-DD-YYYY echo under a native date input. The browser renders those in
   the operating system's locale and gives no way to override it, so the value
   is repeated in our format rather than left ambiguous. */
.date-echo { display:inline-block; margin-left:8px; font-size:10.5px;
  color:var(--subtle); font-variant-numeric:tabular-nums; }
.date-echo:empty { display:none; }

/* Payroll ------------------------------------------------------------------- */
.pr-field { display:flex; align-items:center; gap:8px; font-size:11px;
  font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--subtle); }
.pr-field select { padding:8px 10px; border-radius:9px; background:var(--panel-2);
  border:1px solid var(--line); color:var(--text); font-size:13px;
  letter-spacing:normal; text-transform:none; font-weight:600; }
.pr-check { display:flex; align-items:center; gap:7px; font-size:12px;
  color:var(--muted); }
.pr-period { margin:6px 0 16px; font-size:13px; color:var(--muted); }
.pr-period b { color:var(--text); }

.pr-coach { padding:0; margin-bottom:10px; overflow:hidden; }
.pr-coach-head { display:flex; align-items:center; gap:14px; width:100%;
  padding:14px 18px; border:0; background:transparent; cursor:pointer;
  text-align:left; transition:background .14s ease; }
.pr-coach-head:hover { background:var(--lift-2); }
.pr-coach-name { font-size:14px; font-weight:700; letter-spacing:-.01em;
  flex:0 0 auto; }
.pr-coach-split { flex:1; }
.pr-coach-total { font-size:17px; font-weight:800; font-variant-numeric:tabular-nums; }
.pr-detail { border-top:1px solid var(--line); padding:4px 8px 8px; }

/* Filtering nineteen clients down to the one being queried. Sits inside the
   coach's card so it is obvious which list it is narrowing. */
.pr-filters { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:10px 6px 8px; }
.pr-filters .pr-search { flex:0 1 220px; min-width:150px; }
.pr-filters select { flex:0 1 170px; min-width:130px; }
.pr-filters input, .pr-filters select { font-size:12px; padding:6px 9px;
  min-height:0; height:auto; }
.pr-count { margin-left:auto; }
/* Inside a coach's card, and inside each paid run in the history: the
   paycheck in its two parts, client pay and commission, each with its own
   subtotal. The commission part is always drawn -- an empty one says so. */
.pr-part { padding:6px 0 4px; }
.pr-part + .pr-part { border-top:1px solid var(--line); margin-top:6px; }
.pr-part-head, .pr-hist-block-head { display:flex; justify-content:space-between; align-items:baseline;
  padding:8px 6px 4px; font-size:12px; font-weight:700; letter-spacing:.02em;
  text-transform:uppercase; color:var(--muted); }
.pr-part-head span:last-child, .pr-hist-block-head span:last-child {
  font-variant-numeric:tabular-nums; text-transform:none; letter-spacing:0; color:var(--text); }
.pr-hist-block { padding:0 10px 6px; }
.pr-hist-empty { margin:0 6px 8px; font-size:12px; }
.pr-hist-coach-head .helper { flex:1; margin:0 12px; }
/* A commission that has already gone out on a paid run: kept for the record,
   read-only, shown on request. */
.pr-paid-row td { color:var(--muted); }
/* The tracker's editable cells: a coach name needs room to read, an amount
   does not. */
#prPending select.pr-mini { min-width:130px; }
#prPending input[data-pamount] { width:76px; }
/* A commission somebody has decided not to pay. Struck rather than deleted --
   it can be put back, and "where did that go" is a worse question than a line
   with a line through it. */
.pr-dropped td { opacity:.5; }
.pr-dropped td:first-child b { text-decoration:line-through; }
.pr-mini { font-size:11px; padding:5px 11px; min-height:0; height:auto;
  width:100%; max-width:170px; }
.pr-pend-actions { display:flex; gap:6px; align-items:center;
  white-space:nowrap; }

/* A coach whose total was agreed rather than worked out. Flagged on the card,
   because a number somebody typed should never look like a number the software
   derived. */
.pr-agreed { border-color:rgba(53,201,143,.35); }
.pr-coach-total .pr-tag { margin-left:8px; vertical-align:middle; }
.pr-coach-note { padding:0 16px 12px; font-size:12px; color:var(--muted);
  border-left:2px solid rgba(53,201,143,.5); margin:0 16px 12px; }
.pr-adjust { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap;
  padding:12px 6px 6px; margin-top:6px; border-top:1px solid var(--line); }
.pr-adjust label { display:flex; flex-direction:column; gap:4px; font-size:11px;
  color:var(--subtle); }
.pr-adjust .pr-adjust-note { flex:1; min-width:220px; }
.pr-adjust .pr-adjust-note input { max-width:none; }
.pr-adjust .helper { flex-basis:100%; }

/* Rates, folded away and read-only until asked for twice. */
.pr-disclose { display:flex; align-items:flex-start; gap:10px; width:100%;
  text-align:left; padding:12px 14px; border:1px solid var(--line);
  border-radius:var(--radius); background:var(--panel); color:inherit;
  cursor:pointer; transition:background .15s ease, border-color .15s ease; }
.pr-disclose:hover { background:var(--lift-2); }
.pr-disclose .pr-chev { flex:0 0 auto; margin-top:3px; color:var(--subtle);
  transition:transform .16s ease; }
.pr-disclose[aria-expanded="true"] .pr-chev { transform:rotate(90deg); }
.pr-disclose-title { display:block; font-size:15px; font-weight:700;
  letter-spacing:-.01em; }
.pr-disclose-sub { display:block; font-size:11.5px; color:var(--subtle);
  margin-top:2px; }
.pr-lock { display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  margin:10px 0; padding:9px 13px; border-radius:11px;
  border:1px solid var(--line); background:var(--panel-2); }
.pr-lock.on { border-color:rgba(245,174,78,.45);
  background:rgba(245,174,78,.09); }
.pr-lock .helper { flex:1; }
.pr-rate-cols { align-items:start; }
.pr-rate-cols input:disabled, .pr-rate-cols button:disabled { opacity:.5;
  cursor:default; }
.pr-comm-row td { background:rgba(53,201,143,.06); }
.pr-tag { display:inline-block; padding:1px 7px; border-radius:20px; font-size:9.5px;
  font-weight:700; color:var(--subtle); border:1px solid var(--line); }
.pr-tag.warn { color:var(--ink-warn); border-color:rgba(245,174,78,.4); }
.pr-tag.good { color:var(--ink-good); border-color:rgba(53,201,143,.35); }
.pr-rate, .pr-comm { width:90px; padding:5px 8px; border-radius:7px; text-align:right;
  background:var(--panel-2); border:1px solid var(--line); color:var(--text);
  font-variant-numeric:tabular-nums; }

/* The date field ------------------------------------------------------------
   Ours rather than the browser's, because the browser's renders in the
   operating system's locale and cannot be told otherwise -- which is how the
   same contract read 08-01 in Arizona and 01-08 in Manila. */
.dp { display:inline-flex; align-items:center; position:relative; max-width:100%;
  width:max-content; justify-self:start; vertical-align:middle; }
/* A page that styles every span under its labels (the client record's
   `.field > span { display:block }`) must not turn this wrapper into a block:
   the calendar button then dropped onto its own line and, with its negative
   margin, hung outside the dialog's edge -- Stefan, 09-21-2026: "the date
   picker ... is being cut off". The wrapper spans exactly what its input
   spans, and the button is pinned inside the input's right edge whatever
   width the page gives it. */
label > span.dp { display:flex; width:max-content; margin:0; }
.field > span.dp { width:100%; }
/* "MM-DD-YYYY" has to fit next to the calendar button, and it did not.
   Stefan, 09-26-2026: "I see a common reoccurring theme with the date box
   throughout the app ... it ends up bleeding outside a lot of the boxes. We
   either need to resize the text or expand the boxes."

   It was 132px wide with 34px reserved for the button and 10px of padding --
   86px of room for ten characters at 12.5px, which just fits. The trouble is
   `max-width:100%`: in any cell narrower than 132px the box shrank and the
   text went under the button. So: a size down, a narrower button to reserve
   less, a wider box, and a floor under it, so ten digits fit at every width
   this appears at. */
.dp-text { padding:8px 30px 8px 9px; border-radius:9px; background:var(--panel);
  border:1px solid var(--line); color:var(--text); font-size:12px;
  font-variant-numeric:tabular-nums; width:138px; max-width:100%; min-width:112px; }
.dp-text:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
/* Something unparseable stays on screen and is marked, rather than being
   silently discarded or silently guessed at. */
.dp-text.bad { border-color:var(--ink-bad); box-shadow:0 0 0 3px rgba(226,86,75,.16); }
.dp-btn { display:grid; place-items:center; width:25px; height:25px; margin:0;
  position:absolute; right:2px; top:50%; transform:translateY(-50%);
  border:0; background:transparent; color:var(--subtle); cursor:pointer;
  border-radius:7px; transition:color .14s ease; }
.dp-btn:hover { color:var(--blue); }

.dp-cal { position:fixed; z-index:95; width:252px; padding:10px;
  border-radius:14px; background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--shadow-lg);
  animation:menuIn .12s cubic-bezier(.2,.8,.3,1); }
.dp-head { display:flex; align-items:center; justify-content:space-between;
  margin-bottom:8px; }
.dp-title { font-size:12.5px; font-weight:700; letter-spacing:-.01em; }
.dp-nav { width:26px; height:26px; border:0; border-radius:7px; cursor:pointer;
  background:transparent; color:var(--muted); font-size:16px; line-height:1;
  transition:background .13s ease, color .13s ease; }
.dp-nav:hover { background:var(--panel-2); color:var(--text); }
.dp-dow, .dp-grid { display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
.dp-dow span { text-align:center; font-size:9.5px; font-weight:800;
  letter-spacing:.06em; color:var(--subtle); padding:4px 0; }
.dp-day { height:30px; border:0; border-radius:8px; background:transparent;
  color:var(--muted); font-size:12px; cursor:pointer;
  font-variant-numeric:tabular-nums;
  transition:background .12s ease, color .12s ease; }
.dp-day:hover { background:var(--lift-3); color:var(--text); }
.dp-day.today { color:var(--blue); font-weight:750; }
.dp-day.on { background:var(--blue-solid); color:#fff; font-weight:750; }
.dp-pad { height:30px; }
.dp-foot { display:flex; gap:6px; margin-top:8px; padding-top:8px;
  border-top:1px solid var(--line); }
.dp-quick { flex:1; padding:6px; border-radius:8px; font-size:11px;
  font-weight:650; cursor:pointer; background:var(--panel-2);
  border:1px solid var(--line); color:var(--muted);
  transition:color .13s ease, border-color .13s ease; }
.dp-quick:hover { color:var(--text); border-color:var(--subtle); }

/* Payroll: the run bar, exclusions and history */
.pr-runbar { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap;
  margin:0 0 16px; padding:12px 14px; border:1px solid var(--line);
  border-radius:12px; background:var(--panel-2); }
.pr-notewrap { flex:1; min-width:240px; display:flex; flex-direction:column;
  gap:5px; font-size:10px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }
.pr-notewrap input { padding:8px 11px; border-radius:9px; background:var(--panel);
  border:1px solid var(--line); color:var(--text); font-size:12.5px;
  letter-spacing:normal; text-transform:none; font-weight:400; }
.pr-notewrap input:focus { outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 14%, transparent); }
.pr-status { margin-left:auto; font-size:11.5px; }
.pr-paid { color:var(--ink-good); font-weight:700; }
/* A removed line stays on the page struck through rather than vanishing: a
   client who is simply gone from a payslip is what starts an argument. */
.pr-struck { text-decoration:line-through; opacity:.5; }
/* A row's own action, drawn as a button: filled, with the small ghost
   button's border and shadow, so it never reads as one of the .pr-tag pills
   beside it. Stefan, 10-19-2026, of pay days and put back: "These almost just
   look like tags." Red on hover, because most of them take something away;
   .is-go is one that pays or restores, in the accent, and stays it. */
.pr-x { display:inline-flex; align-items:center; padding:3px 9px; border-radius:7px;
  font-size:11px; font-weight:700; line-height:1.3; white-space:nowrap; cursor:pointer;
  background:var(--panel); color:var(--text); border:1px solid var(--line-strong);
  box-shadow:var(--shadow-sm);
  transition:color .13s ease, border-color .13s ease, background .13s ease, box-shadow .13s ease; }
.pr-x:hover { color:var(--ink-bad); border-color:rgba(226,86,75,.55); background:var(--tint-red);
  box-shadow:var(--shadow); }
.pr-x:active { transform:scale(.97); }
.pr-x.is-go { color:var(--ink-info); background:color-mix(in srgb, var(--blue) 9%, var(--panel));
  border-color:color-mix(in srgb, var(--blue) 45%, var(--line-strong)); }
.pr-x.is-go:hover { color:var(--ink-info); background:color-mix(in srgb, var(--blue) 16%, var(--panel));
  border-color:var(--blue); }

.pr-hist { padding:0; margin-bottom:10px; overflow:hidden; }
.pr-hist-head { display:flex; align-items:flex-start; justify-content:space-between;
  gap:14px; padding:14px 18px; border-bottom:1px solid var(--line);
  background:var(--panel-2); }
.pr-hist-head .helper { display:block; margin-top:2px; }
.pr-hist-note { margin-top:6px; font-size:12px; color:var(--muted);
  padding:6px 10px; border-radius:8px; background:color-mix(in srgb, var(--blue) 10%, transparent); }
.pr-hist-coach { border-bottom:1px solid var(--line); }
.pr-hist-coach:last-child { border-bottom:0; }
.pr-hist-coach-head { display:flex; justify-content:space-between;
  padding:10px 18px 4px; font-size:12.5px; }
.pr-hist-coach-head span { font-variant-numeric:tabular-nums; }
.pr-decide { display:flex; gap:6px; white-space:nowrap; }

/* Documents ----------------------------------------------------------------
   A document is a thing you will probably end up handing to someone, so it is
   laid out like a sheet of paper rather than like a record in a table. */
.doc-row, .doc-line { display:flex; align-items:center; gap:12px; padding:11px 12px;
  border-bottom:1px solid var(--line); text-decoration:none; color:inherit;
  border-radius:10px; transition:background .15s ease, transform .12s ease; }
.doc-row:hover, .doc-line:hover { background:var(--lift-2); }
.doc-row:active { transform:scale(.995); }
.doc-icon { width:28px; height:28px; flex:0 0 28px; border-radius:8px;
  display:grid; place-items:center; font-size:13px; color:var(--blue);
  background:color-mix(in srgb, var(--blue) 14%, transparent); }
.doc-icon.file { color:var(--ink-violet); background:rgba(139,92,246,.16); }
.doc-row-main, .doc-line-main { flex:1; min-width:0; display:flex;
  flex-direction:column; gap:2px; text-decoration:none; color:inherit; }
.doc-row-title, .doc-line-title { font-size:13px; font-weight:600;
  letter-spacing:-.01em; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.doc-row-sub { font-size:11.5px; color:var(--subtle); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.doc-row-who { flex:0 0 auto; }
.doc-row-meta { flex:0 0 auto; font-size:11px; color:var(--muted);
  text-align:right; }
.doc-line { border-radius:9px; }

/* The sheet */
.doc-sheet { max-width:820px; margin:0 auto; background:var(--panel);
  border:1px solid var(--line); border-radius:var(--radius); padding:30px 34px; }
.doc-head { display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap;
  padding-bottom:16px; margin-bottom:22px; border-bottom:1px solid var(--line); }
.doc-head-main { flex:1; min-width:220px; }
.doc-head h1 { font-size:23px; letter-spacing:-.02em; }
.doc-meta { font-size:11.5px; color:var(--subtle); margin-top:5px; }
.doc-meta a { color:var(--blue); text-decoration:none; }
.doc-unfiled { color:var(--ink-warn); }
.doc-body { font-size:14px; line-height:1.65; }
.doc-body h4 { font-size:14.5px; font-weight:700; margin:20px 0 8px; }
.doc-body h4:first-child { margin-top:0; }
.doc-body p { margin:0 0 11px; }
.doc-body ul, .doc-body ol { margin:0 0 12px; padding-left:22px; }
.doc-body li { margin-bottom:5px; }
.doc-body code { font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:12.5px; padding:1px 5px; border-radius:5px;
  background:var(--lift-3); }
.doc-file { text-align:center; padding:34px 20px; border:1.5px dashed var(--line);
  border-radius:12px; display:flex; flex-direction:column; gap:14px;
  align-items:center; }
.doc-editor textarea { width:100%; font-family:ui-monospace, SFMono-Regular,
  Menlo, monospace; font-size:12.5px; line-height:1.6; }

/* Printing is the PDF. Everything that is not the document itself goes. */
@media print {
  body { background:#fff !important; color:#111 !important; }
  .sidebar, .topbar, .crumb, .crumbs, .doc-tools, .toast, .brain-overlay, .bside,
  #sdOverlay { display:none !important; }
  .app-shell { display:block !important; }
  main { padding:0 !important; margin:0 !important; background:#fff !important; }
  .doc-sheet { max-width:none; margin:0; padding:0; border:0; background:#fff !important; }
  .doc-head { border-bottom:1px solid #ddd; }
  .doc-head h1, .doc-body, .doc-body h4 { color:#111 !important; }
  .doc-meta, .doc-meta a { color:#555 !important; }
  .doc-body code { background:#f1f1f1 !important; color:#111 !important; }
  a { text-decoration:none; }
}

/* Saving something into a client's file */
.sd-panel { max-width:560px; }
.sd-body { padding:18px; }
.sd-label { display:block; font-size:10.5px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); margin:14px 0 5px; }
.sd-label:first-child { margin-top:0; }
.sd-label .helper { text-transform:none; letter-spacing:0; font-weight:500; }
.sd-body input { width:100%; }
.sd-pick { position:relative; }
.sd-results { position:absolute; left:0; right:0; top:calc(100% + 4px); z-index:40;
  max-height:236px; overflow-y:auto; padding:5px; border-radius:11px;
  background:var(--panel-2); border:1px solid var(--line);
  box-shadow:var(--shadow-lg); }
.sd-row { width:100%; display:flex; flex-direction:column; gap:1px; padding:7px 9px;
  border:0; background:transparent; color:inherit; cursor:pointer;
  border-radius:8px; text-align:left; }
.sd-row:hover { background:var(--lift-3); }
.sd-row-name { font-size:12.5px; font-weight:600; }
.sd-chosen { display:flex; align-items:center; gap:9px; padding:8px 10px;
  border-radius:10px; font-size:13px; background:color-mix(in srgb, var(--blue) 16%, transparent);
  border:1px solid color-mix(in srgb, var(--blue) 34%, transparent); }
.sd-chosen span { flex:1; }
.sd-preview { max-height:210px; overflow-y:auto; padding:12px 14px;
  border:1px solid var(--line); border-radius:11px; background:var(--panel-2);
  font-size:12.5px; line-height:1.55; color:var(--muted); }
.sd-preview h4 { font-size:12.5px; font-weight:700; margin:10px 0 5px;
  color:var(--text); }
.sd-preview h4:first-child { margin-top:0; }
.sd-preview p { margin:0 0 7px; }
.sd-preview ul, .sd-preview ol { margin:0 0 7px; padding-left:18px; }

/* What you can do with an answer once it exists */
/* Always visible, just quiet. Hiding these until hover meant nobody knew an
   answer could be copied rather than filed. */
.ai-actions { display:flex; gap:6px; margin-top:9px; opacity:.72;
  transition:opacity .15s ease; }
.ai-turn:hover .ai-actions, .ai-actions:focus-within { opacity:1; }
.ai-act { display:inline-flex; align-items:center; gap:5px; padding:4px 9px;
  font-size:11px; color:var(--muted); background:transparent; cursor:pointer;
  border:1px solid var(--line); border-radius:8px;
  transition:color .14s ease, border-color .14s ease, background .14s ease; }
.ai-act:hover { color:var(--text); border-color:color-mix(in srgb, var(--blue) 50%, transparent);
  background:color-mix(in srgb, var(--blue) 10%, transparent); }


/* Dropping a file onto a client. The target is the whole panel rather than a
   button, because dragging a contract out of Finder and letting go is the
   entire interaction -- there is nothing to click. */
.filedrop { display:flex; flex-direction:column; align-items:center; gap:6px;
  padding:22px 18px; text-align:center; color:var(--muted);
  border:1.5px dashed var(--line); border-radius:12px;
  transition:border-color .15s ease, background .15s ease, color .15s ease; }
.filedrop svg { color:var(--subtle); transition:color .15s ease; }
.filedrop p { font-size:12.5px; margin:0; }
.filedrop:hover { border-color:color-mix(in srgb, var(--blue) 45%, transparent); }
.filedrop.over { border-color:var(--blue); background:color-mix(in srgb, var(--blue) 10%, transparent);
  color:var(--text); }
.filedrop.over svg { color:var(--blue); }

/* Contract check: one card per person, because each is a small decision. */
.cc-card { border-left:3px solid var(--metric-color, var(--line));
  margin-bottom:10px; }
.cc-head { display:flex; align-items:flex-start; justify-content:space-between;
  gap:12px; }
.cc-who { display:flex; flex-direction:column; gap:2px; min-width:0; }
.cc-name { font-size:14.5px; font-weight:700; letter-spacing:-.01em;
  color:var(--text); text-decoration:none; }
.cc-name:hover { color:var(--blue); }
.cc-why { font-size:12.5px; color:var(--muted); margin-top:9px; }
.cc-known { font-size:12px; color:var(--muted); margin-top:6px;
  display:flex; gap:8px; align-items:baseline; }
.cc-docs { margin-top:10px; border-top:1px solid var(--line); padding-top:8px; }
.cc-doc { display:flex; justify-content:space-between; gap:12px; padding:4px 0;
  font-size:12px; flex-wrap:wrap; }
.cc-actions { margin-top:12px; }

/* Owner pay: typed per month, because it is a decision rather than a figure
   anything else in the system could know. */
.pnl-own { display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  margin-top:12px; padding-top:11px; border-top:1px solid var(--line); }
.pnl-own label { font-size:10.5px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); }
.pnl-own input { width:130px; font-size:12.5px; padding:6px 9px; min-height:0;
  height:auto; font-variant-numeric:tabular-nums; }
.pnl-own .helper { flex:1 1 15rem; }

/* The expense grid, laid out like the tracker: months down, categories across.
   Every figure is an input because this is the screen a bank statement gets
   categorised on, and a dialog per cell would make that unbearable. */
.pnl-grid { font-size:12px; }
.pnl-grid th { white-space:nowrap; font-size:9.5px; vertical-align:bottom; }
.pnl-grid th:first-child, .pnl-grid td:first-child { position:sticky; left:0;
  background:var(--panel); z-index:2; }
.pnl-grid tfoot td { border-top:1px solid var(--line-strong); }
.pnl-cell { width:88px; text-align:right; font-size:12px; padding:4px 6px;
  min-height:0; height:auto; border-color:transparent; background:transparent;
  font-variant-numeric:tabular-nums;
  transition:border-color .12s ease, background .12s ease; }
.pnl-cell:hover { border-color:var(--line); background:var(--lift-2); }
.pnl-cell:focus { border-color:var(--blue); background:var(--panel-2);
  outline:none; }
.pnl-cell.saving { opacity:.5; }
.pnl-cell::placeholder { color:var(--subtle); }

/* The categoriser's memory, tucked behind a disclosure: needed occasionally,
   in the way the rest of the time. */
.bank-memory { margin-top:18px; border-top:1px solid var(--line); padding-top:12px; }
.bank-memory summary { cursor:pointer; font-family:inherit; font-size:12.5px;
  font-weight:600; color:var(--muted); list-style:none; display:flex;
  align-items:center; gap:7px; }
.bank-memory summary::-webkit-details-marker { display:none; }
.bank-memory summary::before { content:"▸"; color:var(--subtle);
  transition:transform .15s ease; display:inline-block; }
.bank-memory[open] summary::before { transform:rotate(90deg); }
.bank-memory summary:hover { color:var(--text); }
.bank-memory summary span { font-weight:400; color:var(--subtle); }

/* The P&L, restructured: the year, the months, the categories -- and the
   machinery of importing statements folded out of the way beneath them. */
.pnl-head { display:flex; align-items:flex-end; justify-content:space-between;
  gap:16px; flex-wrap:wrap; margin-bottom:18px; }
.pnl-year-title { font-size:19px; letter-spacing:-.02em; }
.pnl-head select { width:130px; }
/* Close the month (Statements): a row per month, its accounts as a quiet
   line, the lines that want a look answerable in place. */
.month-close .mc-row { padding:12px 0; border-top:1px solid var(--line); }
.month-close .mc-row:first-child { border-top:0; padding-top:4px; }
.month-close .mc-top { display:flex; align-items:center; gap:10px; flex-wrap:wrap; font-size:14px; }
.month-close .mc-top .button { margin-left:auto; }
.month-close .mc-state { font-size:11px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  padding:3px 8px; border-radius:999px; border:1px solid var(--line); color:var(--muted); }
.month-close .is-closed .mc-state { color:var(--ink-good, var(--green)); border-color:color-mix(in srgb, var(--ink-good, var(--green)) 40%, var(--line)); }
.month-close .is-ready .mc-state { color:var(--ink-info, var(--blue)); border-color:color-mix(in srgb, var(--ink-info, var(--blue)) 40%, var(--line)); }
.month-close .is-look .mc-state { color:var(--ink-warn, #b7791f); border-color:color-mix(in srgb, var(--ink-warn, #b7791f) 45%, var(--line)); }
.month-close .mc-meta { font-size:12.5px; color:var(--muted); margin-top:4px; line-height:1.5; }
.month-close .mc-meta b { color:var(--text); font-weight:600; }
.month-close .mc-money.warn { color:var(--ink-warn, #b7791f); font-weight:600; }
.month-close .mc-accts { display:flex; flex-wrap:wrap; gap:4px 14px; margin-top:4px; font-size:11.5px; color:var(--muted); }
.month-close .mc-accts b { color:var(--text); font-weight:600; }
.month-close .mc-accts i { font-style:normal; color:var(--ink-info, var(--blue)); }
.month-close .mc-look { margin-top:8px; border:1px solid var(--line); border-radius:10px; }
.month-close .mc-line { display:grid; grid-template-columns:auto minmax(0,1fr) auto auto auto; gap:6px 10px;
  align-items:center; padding:7px 10px; border-top:1px solid var(--line); font-size:12.5px; }
.month-close .mc-line:first-child { border-top:0; }
.month-close .mc-when { color:var(--muted); white-space:nowrap; }
.month-close .mc-what { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.month-close .mc-amt { font-variant-numeric:tabular-nums; white-space:nowrap; }
.month-close .mc-line select { max-width:180px; }
@media (max-width:760px) {
  .month-close .mc-line { grid-template-columns:auto minmax(0,1fr) auto; }
  .month-close .mc-line select, .month-close .mc-line .ask-yes { grid-column:span 1; }
  .month-close .mc-top .button { margin-left:0; }
}
/* Money in, checked (Statements): a sentence, the deposits nothing explains
   as rows with their two decisions, then a month strip. */
.money-in .mi-sum { margin:10px 0 8px; font-size:13.5px; }
.money-in .mi-ok { color:var(--ink-good, var(--green)); }
.money-in .mi-warn { color:var(--ink-warn, #b7791f); font-weight:600; }
.money-in .mi-list { border:1px solid var(--line); border-radius:10px; margin-bottom:12px; }
.money-in .mi-row { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:6px 14px; align-items:center;
  padding:10px 12px; border-top:1px solid var(--line); }
.money-in .mi-list .mi-row:first-child, .money-in .mi-decided .mi-row:first-of-type { border-top:0; }
.money-in .mi-row.small { padding:6px 10px; font-size:12.5px; }
.money-in .mi-what { font-size:13px; line-height:1.45; min-width:0; }
.money-in .mi-hint, .money-in .mi-desc { margin-top:2px; overflow-wrap:anywhere; }
.money-in .mi-acts { display:flex; gap:10px; align-items:center; justify-self:end; }
.money-in .mi-form { grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:8px 14px; align-items:flex-end;
  padding-top:6px; font-size:12.5px; }
.money-in .mi-form label { display:flex; flex-direction:column; gap:3px; font-weight:600; }
.money-in .mi-say { grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.money-in .mi-say[hidden] { display:none; }
.money-in .mi-say input { flex:1 1 220px; min-width:0; }
.money-in .mi-form input[type=number] { width:120px; }
.money-in .mi-form .mi-note { flex:1 1 100%; margin:0; font-weight:400; }
.money-in .mi-pick { position:relative; display:block; }
.money-in .mi-pick input { width:220px; }
.money-in .mi-sugg { position:absolute; z-index:5; left:0; right:0; top:100%; margin-top:2px; background:var(--panel);
  border:1px solid var(--line); border-radius:8px; box-shadow:var(--shadow); display:flex; flex-direction:column;
  max-height:220px; overflow:auto; }
.money-in .mi-sugg[hidden] { display:none; }
.money-in .mi-sugg button { text-align:left; padding:6px 10px; border:0; background:none; font:inherit;
  font-weight:500; cursor:pointer; color:var(--text); }
.money-in .mi-sugg button:hover { background:var(--hover, rgba(127,127,127,.08)); }
.money-in .mi-sugg > .helper { padding:6px 10px; }
.money-in .mi-months { margin-top:4px; }
.money-in .mi-decided { margin-top:10px; font-size:13px; }
.money-in .mi-decided summary { cursor:pointer; font-weight:600; padding:4px 0; }
@media (max-width:700px) {
  .money-in .mi-row { grid-template-columns:1fr; }
  .money-in .mi-acts { justify-self:start; }
}
.fin-machinery { margin-top:26px; border:1px solid var(--line);
  border-radius:var(--radius); background:var(--panel); }
.fin-machinery summary { cursor:pointer; padding:14px 18px; font-size:13.5px;
  font-weight:600; list-style:none; display:flex; align-items:center; gap:8px; }
.fin-machinery summary::-webkit-details-marker { display:none; }
.fin-machinery summary::before { content:"▸"; color:var(--subtle);
  transition:transform .15s ease; display:inline-block; }
.fin-machinery[open] summary::before { transform:rotate(90deg); }
.fin-machinery summary span { font-weight:400; color:var(--subtle);
  font-size:12px; }
.fin-machinery-body { padding:0 18px 18px; }

/* ===========================================================================
   Raised surfaces and semantic colour.
   Stefan's note: the hub reads flat and grey, and the parts he likes -- the AI
   answer bubbles, the conversation cards -- are the ones that sit up off the
   page. So: a real shadow, a lighter ground than the page behind it, and a
   lift on hover for anything you can click. Money is coloured by what it MEANS
   -- green good, red bad, amber unsure -- and never for decoration, so colour
   still carries information when it appears.
   =========================================================================== */
.raised {
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow:var(--shadow);
}
.raised-hover { transition: transform .16s ease, box-shadow .16s ease,
                            border-color .16s ease; }
.raised-hover:hover { transform: translateY(-2px);
  box-shadow:var(--shadow);
  border-color: var(--line-strong); }
@media (prefers-reduced-motion: reduce) {
  .raised-hover:hover { transform: none; }
}

/* Money, coloured by meaning */
.money-good { color: var(--green); }
.money-bad  { color: var(--red); }
.money-warn { color: var(--amber); }
.money-flat { color: var(--muted); }

/* The P&L's headline figures, as raised objects rather than boxes on a grid */
#pnlStats { display:grid; grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  gap:10px; margin-bottom:18px; }
#pnlStats .stat { border-radius:var(--radius); padding:14px 16px; min-width:0;
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  border:1px solid var(--line);
  box-shadow:var(--shadow);
  border-top:2px solid var(--tone, transparent);
  display:flex; flex-direction:column; gap:5px; }
#pnlStats .stat span { font-size:10px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted); }
#pnlStats .stat b { font-size:21px; font-weight:800; letter-spacing:-.03em;
  font-variant-numeric:tabular-nums; color:var(--tone, var(--text)); }
#pnlStats .stat.is-good { --tone: var(--green); }
#pnlStats .stat.is-bad  { --tone: var(--red); }
#pnlStats .stat.is-warn { --tone: var(--amber); }
#pnlStats .stat.is-plain { --tone: transparent; }
#pnlStats .stat.is-plain b { color: var(--text); }

/* Tables lifted off the page the same way */
/* A table is an object too, not a grid ruled onto the page. */
.table-wrap { border-radius:var(--radius); overflow-x:auto;
  background: linear-gradient(160deg, var(--panel-2), var(--panel));
  border:1px solid var(--line);
  box-shadow:var(--shadow); }
.table-wrap > table { margin:0; }

/* Per-client profitability. Stefan, 10-03-2026: "it's poorly laid out. Way too
   much space. I shouldn't have to scroll left to right."

   Eight columns of short values in an auto-layout table inside a full-width
   card: the browser hands the slack to whichever column asks loudest, so the
   name and the coach took half the page and the five figures a reader actually
   compares sat off the right-hand edge. Fixed layout and declared widths put
   the money columns beside each other, where comparing them is the point, and
   the whole table inside the card with nothing to scroll. */
#econTable { table-layout:fixed; min-width:0; }
#econTable th, #econTable td { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#econTable th:nth-child(1), #econTable td:nth-child(1) { width:24%; white-space:normal; }
#econTable th:nth-child(2), #econTable td:nth-child(2) { width:13%; }
#econTable th:nth-child(3), #econTable td:nth-child(3),
#econTable th:nth-child(4), #econTable td:nth-child(4),
#econTable th:nth-child(5), #econTable td:nth-child(5) { width:11%; }
#econTable th:nth-child(6), #econTable td:nth-child(6),
#econTable th:nth-child(7), #econTable td:nth-child(7) { width:12%; }
#econTable th:nth-child(8), #econTable td:nth-child(8) { width:106px; }
#econTable td:nth-child(1) .pill { margin-left:4px; }
#econTable tfoot td { white-space:normal; }
/* At laptop width the two "to date" totals are the ones a glance can spare;
   what is earned, what the coach costs and the margin between them stay. */
@media (max-width:1200px) {
  #econTable th:nth-child(6), #econTable td:nth-child(6),
  #econTable th:nth-child(7), #econTable td:nth-child(7) { display:none; }
  #econTable th:nth-child(1), #econTable td:nth-child(1) { width:34%; }
  #econTable th:nth-child(2), #econTable td:nth-child(2) { width:18%; }
}
.table-wrap th { background:var(--lift-1); }
.card .table-wrap { box-shadow:none; border-color:var(--line);
  background:transparent; }
.pnl-row { cursor:pointer; transition:background .13s ease; }
.pnl-row:hover td { background:var(--lift-2); }
.pnl-row.partial td:first-child { box-shadow:inset 3px 0 0 var(--amber); }
tr.pnl-total td { background:var(--lift-2); border-top:1px solid var(--line-strong); }
.section-head h2 { letter-spacing:-.02em; }
.section-head { padding-bottom:2px; }

.pnl-chip { display:inline-block; margin:3px 6px 3px 0; padding:4px 10px;
  border-radius:20px; font-size:11px; background:var(--panel);
  border:1px solid var(--line); }

/* The categoriser asking for help. Sits with the headline figures, because a
   question buried in the statement it came from is a question nobody answers. */
.ask-card { padding:16px 18px; margin-bottom:18px;
  border-left:3px solid var(--amber); display:flex; flex-direction:column; gap:12px; }
.ask-head b { font-size:13.5px; }
.ask-head .helper { display:block; margin-top:3px; }
.ask-list { display:flex; flex-direction:column; gap:2px; }
/* One question. The description used to be a single truncated line, which is
   exactly wrong here -- a Chase ACH descriptor carries the only identifying
   detail at its END, so an ellipsis hides the part that answers the question.
   The tidy name is the heading, the raw text sits under it in full, and both
   wrap. */
.ask-row { display:flex; align-items:flex-start; gap:14px; padding:11px 0;
  border-top:1px solid var(--line); flex-wrap:wrap; }
.ask-row:first-child { border-top:0; }
.ask-text { flex:1 1 20rem; min-width:0; }
.ask-what { font-size:13px; font-weight:600; line-height:1.4; word-break:break-word; }
.ask-raw { margin:3px 0 0; font-size:10.5px; line-height:1.5; color:var(--subtle);
  font-family:ui-monospace, monospace; word-break:break-all; }
.ask-meta { font-size:11px; color:var(--muted); margin-top:5px; line-height:1.5; }
.ask-row select { flex:0 0 auto; max-width:210px; margin-top:2px; }
.ask-row .ask-yes { margin-top:2px; }

/* ---------- check-ins ----------
   The panel reuses the conversation drawer's shape deliberately: reading a
   client's check-in and reading their thread are the same motion, and a second
   drawer that behaved differently would be a second thing to learn. */
.ci-panel { position: fixed; top: 0; right: 0; width: min(460px, 100%); height: 100vh;
  z-index: 30; display: flex; flex-direction: column;
  background: linear-gradient(160deg, var(--panel), var(--panel-2));
  border-left: 1px solid var(--line); box-shadow:var(--shadow-lg); }
/* .convo-panel caps its body at 360px because inline threads sit inside a
   card. This one is a full-height drawer, so the cap has to go or a nine
   question check-in is cut off at question four. */
.ci-panel .msg-panel-body { max-height: none; flex: 1 1 auto; min-height: 0;
  display: block; background: transparent; }
.ci-panel .msg-box { flex: none; border-top: 1px solid var(--line); }
/* The run table. A row is a thing you open, so it has to look like one --
   the whole row lifts on hover, not just the link in it. */
/* The outbox, one block per coach. Sending is done by a coach with their own
   clients open, so the queue is grouped the way the work is done. */
.ci-outg { margin-bottom:18px; }
.ci-outg-head { display:flex; align-items:center; justify-content:space-between;
  gap:12px; margin-bottom:8px; flex-wrap:wrap; }
.ci-outg-head h3 { margin:0; font-size:13px; font-weight:800; }
.ci-outg-head h3 .helper { font-weight:600; margin-left:6px; }
/* A flag a person raised, not one the system worked out. Deliberately quieter
   than badge-warn: it is a note to somebody, not an alarm. */
.badge-note { background:var(--tint-violet, rgba(139,92,246,.16)); color:var(--ink-violet); }

/* The @ picker. Sits above the composer because the composer is usually at the
   bottom of a panel, where a list hanging below would fall off the screen. */
/* Month to month: a standing arrangement, not an alarm. Quieter than the
   overdue chip on purpose -- it is information, and it sits next to one that
   genuinely is a warning. */
.chip-monthly { display:inline-block; margin-left:6px; font-size:9.5px;
  font-weight:800; letter-spacing:.05em; text-transform:uppercase;
  padding:2px 7px; border-radius:20px; vertical-align:middle;
  background:var(--tint-violet); color:var(--ink-violet); }

/* A name in a note is a person, so it looks like one you can click. */
.mention { color:var(--blue); font-weight:600; cursor:default;
  border-radius:4px; padding:0 2px; }
.mention:hover { background:var(--tint-blue); }
.msg-turn.mine .msg-bubble .mention { color:#fff; text-decoration:underline;
  text-decoration-color:rgba(255,255,255,.5); text-underline-offset:2px; }
.msg-turn.mine .msg-bubble .mention:hover { background:rgba(255,255,255,.18); }
.mention-mirror { z-index:-1; }
.inbox-back { display:inline-block; margin-bottom:12px; font-size:12.5px;
  font-weight:600; color:var(--blue); text-decoration:none; }
.inbox-back:hover { text-decoration:underline; }

.mention-card { position:absolute; z-index:90; width:250px; background:var(--panel);
  border:1px solid var(--line); border-radius:12px; padding:13px 15px;
  box-shadow:var(--shadow-lg); }
.mention-card .mc-top { display:flex; align-items:center; gap:9px; margin-bottom:9px; }
.mention-card .mc-av { width:28px; height:28px; border-radius:50%; flex:0 0 auto;
  background:var(--blue-solid); color:#fff; font-weight:700; font-size:13px;
  display:flex; align-items:center; justify-content:center; }
.mention-card .mc-name { font-size:14px; font-weight:700; }
.mention-card .mc-row { font-size:12px; color:var(--muted); margin-bottom:3px; }
.mention-card .mc-acts { display:flex; gap:7px; margin-top:11px; }
.mention-card .mc-btn { flex:1; text-align:center; font-size:12px; font-weight:600;
  padding:6px 8px; border-radius:8px; border:1px solid var(--line);
  color:var(--text); text-decoration:none; }
.mention-card .mc-btn:hover { background:var(--lift-2); border-color:var(--blue); }
.mention-card button.mc-btn { font-family:inherit; cursor:pointer;
  background:var(--panel); color:var(--text); }
.mention-card .mc-send { margin-top:10px; }
.mention-card .mc-send textarea { width:100%; resize:vertical; font:inherit;
  font-size:12.5px; color:var(--text); background:var(--field);
  border:1px solid var(--line); border-radius:8px; padding:7px 9px; }
.mention-card .mc-send textarea:focus { border-color:var(--blue); outline:none; }
.mention-card .mc-send-foot { display:flex; align-items:center;
  justify-content:space-between; gap:8px; margin-top:7px; }
.mention-card .mc-said { font-size:11px; color:var(--muted); }
/* button.mc-go, not .mc-go: the rule above is (0,2,1) because of the element
   selector, so a plain two-class rule loses to it and the Send button drew
   white text on a white box — readable only on hover, where the extra
   pseudo-class finally won. Matched specificity, and later in the file. */
.mention-card button.mc-go { flex:0 0 auto; background:var(--blue-solid); color:#fff;
  border-color:var(--blue-solid); }
.mention-card button.mc-go:hover { filter:brightness(1.08);
  background:var(--blue-solid); color:#fff; }
.mention-card button.mc-go:disabled { opacity:.6; cursor:default; }

.mention-pop.floating { position:fixed; z-index:400; }
.mention-pop { position:absolute; z-index:80; background:var(--panel);
  border:1px solid var(--line); border-radius:10px; padding:4px;
  box-shadow:var(--shadow-lg); max-height:232px; overflow-y:auto; }
/* A row is a face and two lines, the way ClickUp and Monday draw a picker --
   Stefan, 09-23-2026. The circle is their picture, or their initials on the
   colour their name always makes. */
.mention-pop button { display:flex; flex-direction:row; align-items:center;
  gap:9px; width:100%; text-align:left; background:none; border:0; cursor:pointer;
  padding:6px 10px; border-radius:7px; color:var(--text); font:inherit; }
.mention-pop .mn-lines { display:flex; flex-direction:column; align-items:flex-start;
  gap:1px; min-width:0; flex:1 1 auto; }
.mention-pop .mn-lines > span { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mn-face { position:relative; width:28px; height:28px; flex:0 0 auto; border-radius:50%;
  display:grid; place-items:center; overflow:hidden; color:#fff; font-size:10.5px;
  font-weight:800; letter-spacing:.02em; }
.mn-face img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  border-radius:50%; }
.mn-face.brain { background:var(--tint-blue, rgba(67,135,245,.14)); color:var(--blue); }
.mn-face.brain svg { width:17px; height:17px; }
.mention-pop button:hover, .mention-pop button.on { background:var(--lift-2); }
.mention-pop button.on { outline:1px solid var(--blue); outline-offset:-1px; }
.mention-pop .mn-head { font-size:9.5px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); padding:7px 10px 3px; }
.mention-pop .mn-name { font-size:13px; font-weight:600; }
.mention-pop .mn-mail { font-size:11px; color:var(--muted); }

.ci-tier { display:inline-block; font-size:10px; font-weight:800; letter-spacing:.05em;
  text-transform:uppercase; padding:2px 8px; border-radius:20px;
  background:var(--tint-blue); color:var(--ink-info); }
.ci-drift { color:var(--ink-warn); font-weight:700; }
.ci-filters { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.ci-filters input[type="search"] { width:min(260px, 46vw); }
.ci-filters select { width:auto; min-width:130px; }
.ci-row { cursor:pointer; }
.ci-row td { vertical-align:top; }
.ci-row .helper { font-size:11px; line-height:1.4; margin-top:2px; }
.ci-status { display:inline-block; font-size:10px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; color:var(--st); }
.ci-late { color:var(--ink-bad); font-weight:700; }
/* The link that actually went back to the client, on the row. Green because
   it is the only thing on this page that proves the loop closed -- a check-in
   can read "Sent back" with nothing attached, and that is worth being able to
   tell apart at a glance. */
/* Marks a test record that has been sorted above the real work, so a row at
   the top of a worklist does not read as the most urgent thing on it. */
.ci-staff { display:inline-block; margin-left:7px; padding:1px 6px; border-radius:4px;
  background:var(--lift-3); color:var(--muted); font-size:9.5px; font-weight:800;
  letter-spacing:.07em; text-transform:uppercase; vertical-align:1px; }

/* A second button beside the first in the Next step column. */
.ci-and { margin-left:6px; }
.ci-sent { display:inline-block; font-size:11px; font-weight:700;
  color:var(--ink-good); text-decoration:none; }
.ci-sent:hover { text-decoration:underline; }

.ci-answer { padding: 14px 0; border-bottom: 1px solid var(--line); }
.ci-answer:last-child { border-bottom: 0; }
.ci-q { color: var(--muted); font-size: 12px; letter-spacing: .04em;
  text-transform: uppercase; margin-bottom: 5px; }
.ci-a { line-height: 1.6; white-space: pre-wrap; }
.ci-reply { background: rgba(53,201,143,.07); border-radius: 10px; padding: 12px 13px;
  border: 1px solid rgba(53,201,143,.22); margin-top: 12px; }
.ci-link { display: block; margin-top: 8px; padding: 10px 12px; border-radius: 9px;
  background: var(--field); border: 1px solid var(--line); color: var(--muted);
  font-size: 12px; word-break: break-all; }

/* The question rows in the form builder. Arrows rather than drag: reordering
   nine questions on a phone with drag-and-drop is a fight, and the arrows are
   also the only version a keyboard can use. */
.ci-qrow { display: flex; align-items: center; gap: 10px; padding: 9px 0;
  border-bottom: 1px solid var(--line); }
.ci-qrow:last-child { border-bottom: 0; }
.ci-qmove { display: flex; flex-direction: column; gap: 2px; }
.ci-qlabel { flex: 1; min-width: 140px; }
.ci-arrow { width: 26px; height: 20px; border-radius: 6px; border: 1px solid var(--line);
  background: transparent; color: var(--muted); font-size: 11px; line-height: 1;
  display: grid; place-items: center; transition: color .12s ease, background .12s ease; }
.ci-arrow:hover:not([disabled]) { color: var(--text); background: var(--lift-2); }
.ci-arrow[disabled] { opacity: .3; cursor: default; }

/* On the client record: the check-in history, densest form that still reads. */
.ci-hist { display: flex; flex-direction: column; gap: 8px; }
.ci-item { border: 1px solid var(--line); border-radius: 11px; overflow: hidden;
  background: var(--lift-1); }
.ci-head { display: flex; align-items: center; gap: 10px; padding: 11px 13px;
  cursor: pointer; transition: background .12s ease; }
.ci-head:hover { background: var(--lift-2); }
.ci-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.ci-when { font-weight: 600; }
.ci-tag { margin-left: auto; font-size: 11.5px; color: var(--muted); }
.ci-body { padding: 4px 13px 14px; border-top: 1px solid var(--line); }
@media (max-width: 640px) {
  .ci-qrow { flex-wrap: wrap; }
  .ci-panel { width: 100%; }
}

/* The outbox message column. Wraps rather than truncating: the whole point is
   that somebody reads what is about to go to a client before it goes. */
/* pre-wrap, not normal: these messages have real line breaks in them and
   the person pasting one into Trainerize needs to see where they fall. */
.ci-msg { max-width: 520px; line-height: 1.5; color: var(--muted);
  font-size: 12.5px; white-space: pre-wrap; }
.ci-vh { margin: 0 0 10px; font-size: 12px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--subtle); }
.ci-checks { display: flex; flex-direction: column; gap: 8px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--lift-1); }
.ci-checks .pr-check { font-size: 12.5px; }

/* ---------- the switch ----------

   ONE RULE DECIDES WHETHER SOMETHING IS A SWITCH: a switch says the change
   has ALREADY happened. Stefan, 10-16-2026, on the Client Hub filters:
   "can we make this a cleaner toggle switch instead of just a checkbox ...
   I notice in the system there's a lot more of these checkboxes."

   So it is used for a view filter that redraws as you flip it, and for a
   setting that saves itself on change. It is NOT used for a box inside a
   form with a Save button (flipping it would promise something that has not
   happened yet), for a checklist somebody ticks off, or for picking several
   things out of a list -- those stay checkboxes, which is what they are.

   It is a REAL <input type="checkbox" role="switch">, not a div: it keeps
   the keyboard, the label click target, the form value and the screen
   reader's "on/off", and only the paint is ours. */
.switch { display:inline-flex; align-items:center; gap:9px; font-size:12.5px;
  color:var(--muted); cursor:pointer; user-select:none; }
.switch > input[type="checkbox"] {
  appearance:none; -webkit-appearance:none; margin:0; flex:none;
  width:34px; height:20px; border-radius:999px; position:relative;
  background:var(--panel-2); border:1px solid var(--line-strong);
  transition:background .16s ease, border-color .16s ease; cursor:pointer; }
.switch > input[type="checkbox"]::after {
  content:""; position:absolute; top:50%; left:2px; transform:translateY(-50%);
  width:14px; height:14px; border-radius:50%; background:var(--muted);
  transition:transform .16s ease, background .16s ease; }
.switch > input[type="checkbox"]:checked { background:var(--blue); border-color:var(--blue); }
.switch > input[type="checkbox"]:checked::after { transform:translateY(-50%) translateX(14px); background:#fff; }
.switch > input[type="checkbox"]:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
.switch > input[type="checkbox"]:disabled { opacity:.5; cursor:not-allowed; }
.switch:has(> input:disabled) { cursor:not-allowed; opacity:.7; }
.switch > input[type="checkbox"]:checked ~ * { color:var(--text); }
/* LIVE OR NOT LIVE, which is a different question from on or off.

   Stefan, 10-16-2026: "Can you make the live green pulse? It shouldn't look
   like the save. It should be something showing that it's live and active."
   And: "if it's off, it should be red."

   So this one is a status light, not a control that happens to be switched:
   green while it is running, red while it is not. The accent blue is what
   Save is, and a live automation wearing it read as a button rather than a
   state. Filters and preferences keep the blue -- "only the open ones" is a
   choice you made, not something that is live.

   --sw-* are the swatch colours, for filled shapes rather than text, which
   is exactly what a track is. */
/* Not a flat block of colour. Stefan, 10-16-2026: "I don't mind it being
   kind of like a gradient green. It shouldn't be just this solid ugly green
   color." Same shape as --btn-grad: 135deg, the swatch as the floor, running
   to a partner hue -- green towards teal, red towards rose. Mixed FROM the
   swatch with color-mix rather than written out, so each theme's own green
   and red carry their own gradient with nothing to keep in step by hand.
   Note the house rule this bends: semantic colours are otherwise flat (see
   --btn-grad). A status light is the exception he asked for. */
.switch.live > input[type="checkbox"] {
  background:linear-gradient(135deg,
    color-mix(in srgb, var(--sw-red) 70%, #ef5d8a), var(--sw-red));
  border-color:transparent; }
.switch.live > input[type="checkbox"]::after { background:#fff;
  box-shadow:0 1px 2px rgba(0,0,0,.22); }
.switch.live > input[type="checkbox"]:checked {
  background:linear-gradient(135deg,
    color-mix(in srgb, var(--sw-green) 66%, #1fd1b0), var(--sw-green));
  border-color:transparent; }

/* The breath says RUNNING, so it is only on the things that actually run.
   A content shelf being visible is a standing fact, and sixteen of them
   pulsing down a list is a disco, not a signal. */
.switch.live.beat > input[type="checkbox"]:checked { animation:sw-live 2.4s ease-in-out infinite; }
@keyframes sw-live {
  0%, 100% { box-shadow:0 0 0 0 transparent; }
  50%      { box-shadow:0 0 0 3px color-mix(in srgb, var(--sw-green) 38%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .switch.live.beat > input[type="checkbox"]:checked { animation:none;
    box-shadow:0 0 0 2px color-mix(in srgb, var(--sw-green) 30%, transparent); }
}

/* Smaller, for a dense list where a switch sits on every row. */
.switch.tiny { gap:6px; font-size:11px; }
.switch.tiny > input[type="checkbox"] { width:28px; height:16px; }
.switch.tiny > input[type="checkbox"]::after { width:11px; height:11px; }
.switch.tiny > input[type="checkbox"]:checked::after { transform:translateY(-50%) translateX(12px); }

/* A switch whose label runs to a second line sits at the TOP of it, not
   floating against the middle of a paragraph. */
.switch.top { align-items:flex-start; }
.switch.top > input[type="checkbox"] { margin-top:1px; }
.switch .helper { display:block; margin-top:2px; }

/* The flip is information, not decoration: without motion it still has to
   read as moved, so the knob jumps rather than slides. */
@media (prefers-reduced-motion: reduce) {
  .switch > input[type="checkbox"],
  .switch > input[type="checkbox"]::after { transition:none; }
}

/* ---------- theme switch and drawn icons ---------- */

/* Three states, one button: light, dark, follow the system. Only the glyph for
   the current state is shown, so the button says what you are on rather than
   what you would get. */
.theme-toggle .th-sun, .theme-toggle .th-moon, .theme-toggle .th-auto { display: none; }
:root .theme-toggle .th-moon { display: block; }
:root[data-theme="light"] .theme-toggle .th-moon { display: none; }
:root[data-theme="light"] .theme-toggle .th-sun { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .th-moon { display: none; }
  :root:not([data-theme="dark"]) .theme-toggle .th-sun { display: block; }
}

/* Appearance: light or dark, and the accent -- the ClickUp Themes card,
   opened from the sun/moon in the top bar or from the account menu. */
.appear-pop { position:fixed; z-index:120; width:340px; padding:14px; border-radius:14px;
  background:var(--panel); border:1px solid var(--line); box-shadow:var(--shadow-lg);
  animation:menuIn .12s cubic-bezier(.2,.8,.3,1); }
.appear-pop h4 { margin:0 0 10px; font-size:13px; letter-spacing:-.01em; }
.appear-pop h5 { margin:12px 0 8px; font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--subtle); }
.appear-modes { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.appear-mode { border:2px solid var(--line); border-radius:12px; padding:8px; background:transparent; cursor:pointer;
  display:flex; flex-direction:column; gap:7px; color:var(--text); font:inherit; font-size:12px; font-weight:650; text-align:left; }
.appear-mode:hover { border-color:var(--line-strong); }
.appear-mode.on { border-color:var(--blue); box-shadow:0 0 0 3px var(--tint-blue); }
/* A tiny page: rail, bar, two lines. Drawn in the mode's own colours so the
   tile shows what you would get. */
.appear-thumb { height:52px; border-radius:8px; border:1px solid rgba(0,0,0,.12); position:relative; overflow:hidden; }
.appear-thumb::before { content:""; position:absolute; left:0; top:0; bottom:0; width:22%; }
.appear-thumb i { position:absolute; left:30%; height:5px; border-radius:3px; }
.appear-thumb i:nth-child(1) { top:12px; width:50%; }
.appear-thumb i:nth-child(2) { top:24px; width:34%; }
.appear-thumb i:nth-child(3) { top:36px; width:42%; }
.appear-thumb.light { background:#f7f6f3; } .appear-thumb.light::before { background:#e9e7e2; } .appear-thumb.light i { background:#cfcdc7; }
.appear-thumb.dark { background:#131820; border-color:rgba(255,255,255,.1); } .appear-thumb.dark::before { background:#1a212b; } .appear-thumb.dark i { background:#39424f; }
.appear-thumb i.acc { background:var(--blue-solid) !important; width:16px !important; left:auto; right:10px; top:12px; height:8px; }
.appear-colors { display:grid; grid-template-columns:repeat(3,1fr); gap:6px; }
.appear-color { display:flex; align-items:center; gap:8px; padding:7px 9px; border:1.5px solid var(--line); border-radius:9px;
  background:transparent; color:var(--text); font:inherit; font-size:12px; font-weight:600; cursor:pointer; text-align:left; }
.appear-color:hover { border-color:var(--line-strong); background:var(--lift-1); }
.appear-color.on { border-color:var(--blue); background:var(--tint-blue); }
.appear-color b { width:14px; height:14px; border-radius:4px; flex:0 0 auto; background:var(--sw); box-shadow:inset 0 0 0 1px rgba(0,0,0,.08); }
.appear-color.on b::after { content:"✓"; display:block; color:#fff; font-size:10px; line-height:14px; text-align:center; font-weight:800; }
.appear-pop .helper { margin:10px 0 0; font-size:11px; }
@media (max-width:700px) { .appear-pop { width:min(340px, calc(100vw - 16px)); } }

/* Line icons in the rail. Sized in ems so they track the label, and coloured by
   inheritance so hover and active states need no icon-specific rules. */
.nav-glyph { width: 17px; height: 17px; display: block; color: inherit; }
.nav-icon-btn { display: grid; place-items: center; }
.nav-icon-btn.custom { font-size: 15px; line-height: 1; }

/* Light mode is a different material, not an inversion, so a few things that
   read well on black need easing back on paper. */
:root[data-theme="light"] .brand-mark,
:root:not([data-theme="dark"]) .brand-mark {
  --mark-drop: 0 1px 2px color-mix(in srgb, var(--blue-solid) 28%, transparent),
               0 6px 12px -4px color-mix(in srgb, var(--blue-solid) 42%, transparent);
}
:root[data-theme="light"] .nav-item.active,
:root:not([data-theme="dark"]) .nav-item.active {
  color: var(--text); background: var(--lift-3);
  box-shadow: inset 2px 0 0 var(--blue);
}
:root[data-theme="light"] .sidebar,
:root:not([data-theme="dark"]) .sidebar { background: var(--sidebar); }

/* ---------- calendar ---------- */
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px; margin-top: 14px; }
.cal-day { border: 1px solid var(--line); border-radius: 11px; overflow: hidden;
  background: var(--lift-1); min-height: 150px; display: flex; flex-direction: column; }
.cal-day.is-today { border-color: var(--blue); background: var(--tint-blue); }
.cal-dayhead { display: flex; align-items: baseline; gap: 6px; padding: 9px 11px;
  border-bottom: 1px solid var(--line); }
.cal-dow { color: var(--subtle); font-size: 10px; font-weight: 800;
  letter-spacing: .09em; text-transform: uppercase; }
.cal-num { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
.cal-events { padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.cal-event { border-radius: 8px; padding: 7px 8px; background: var(--panel-2);
  border: 1px solid var(--line); display: flex; flex-direction: column; gap: 2px; }
.cal-event.is-off { opacity: .72; }
.cal-event.is-off .cal-who, .cal-event.is-off .cal-time { text-decoration: line-through; }
/* What happened, in colour: closed is green, a no close is red, a cancelled
   or no-show is crossed out, and only a call still ahead stays blue. */
.cal-event.is-won { border-color: color-mix(in srgb, var(--green, #35c98f) 55%, var(--line));
  background: var(--tint-green); box-shadow: inset 3px 0 0 var(--green, #35c98f); }
.cal-event.is-won .cal-time { color: var(--ink-good); }
.cal-event.is-lost { border-color: color-mix(in srgb, var(--red) 55%, var(--line));
  background: var(--tint-red); box-shadow: inset 3px 0 0 var(--red); }
.cal-event.is-lost .cal-time { color: var(--red); }
.cal-event.is-off .cal-time { color: var(--subtle); }
.cal-time { color: var(--blue); font-size: 11px; font-weight: 700;
  font-variant-numeric: tabular-nums; }
.cal-who { font-size: 12.5px; line-height: 1.35; }
.cal-closer { color: var(--subtle); font-size: 10.5px; }
.cal-empty { color: var(--subtle); text-align: center; margin: 10px 0; }
/* Your own Google events, around the bookings: a thin line each, in the
   calendar's own colour, quieter than a call so the calls still read first. */
.cal-gev { display: grid; grid-template-columns: 8px minmax(0,1fr); grid-template-rows: auto auto; column-gap: 6px; row-gap: 1px;
  align-items: center; padding: 4px 7px; border-radius: 7px; border: 1px dashed var(--line); background: transparent;
  color: var(--muted); font-size: 11.5px; line-height: 1.3; text-decoration: none; }
.cal-gev:hover { background: var(--lift-2); color: var(--text); border-style: solid; }
.cal-gev.is-past { opacity: .6; }
.cal-gdot { width: 8px; height: 8px; border-radius: 50%; grid-row: 1 / span 2; }
.cal-gwho { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-weight: 550; }
.cal-gtime { font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--subtle); white-space: nowrap; }
.cal-gmeet { font-size: 8px; color: var(--blue); vertical-align: middle; }
.cal-gnote { color: var(--muted); }
/* The Account page's list of calendars. */
.gcal-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.gcal-cal { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.gcal-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
@media (max-width: 900px) {
  .cal-grid { grid-template-columns: 1fr; }
  .cal-day { min-height: 0; }
}

/* ---------- sub-navigation ----------
   Sits under the top bar, above the page. Quiet by design: the rail is the
   navigation, this only says which view of one job you are looking at. */
/* Drawn as a segmented control rather than a hairline of small text: the
   first version was so quiet that Payroll, one of its two tabs, went
   unfound for weeks. The current segment is filled the way the page tabs
   below it are, so the two rows read as the same kind of thing. */
.subnav { display: inline-flex; gap: 3px; margin: 14px 0 -4px; flex-wrap: wrap;
  padding: 4px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--lift-1); }
.subnav-tab { position: relative; padding: 7px 14px 7px 20px; color: var(--muted); font-size: 13px; font-weight: 600;
  border-radius: 9px; text-decoration: none;
  transition: color .14s ease, background .14s ease; white-space: nowrap; }
.subnav-tab:hover { color: var(--text); background: var(--lift-2); }
.subnav-tab.on { color: #fff; background: var(--blue-solid); }
.subnav-tab.on:hover { color: #fff; background: var(--blue-solid); }
/* Rearranging the strip. The tab being carried fades; the rest of the strip
   says it is in the middle of something so a half-finished drag never looks
   like the page has simply gone odd. */
/* The grip: invisible until the tab is hovered, exactly like the rail's, and
   sitting in padding that is always there so showing it moves nothing. */
.tab-grip { position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; fill: currentColor; color: var(--subtle);
  opacity: 0; cursor: grab; transition: opacity .12s ease; }
/* Hovering ANYWHERE on the strip shows every grip, not just the one under
   the pointer. Stefan asked for the ability to reorder these tabs -- it was
   already built and already saving; what was missing is that nothing said
   so. One tab quietly growing a handle reads as decoration; the whole row
   growing handles together reads as "these move". */
.subnav:hover .tab-grip, .subnav:focus-within .tab-grip { opacity: .4; }
.subnav-tab:hover .tab-grip, .subnav-tab:focus-visible .tab-grip { opacity: .7; }
.tab-grip:hover { opacity: 1; color: var(--blue); }
.tab-grip:active { cursor: grabbing; }
.subnav-tab.on .tab-grip { color: #fff; }
.subnav.tab-rearranging .tab-grip { opacity: .55; }
.subnav-tab.tab-dragging { opacity: .45; }
.subnav.tab-rearranging .subnav-tab { transition: transform .12s ease; }
.subnav.tab-rearranging { cursor: grabbing; }
@media (max-width: 700px) {
  .subnav { display: flex; overflow-x: auto; flex-wrap: nowrap; }
}

/* Setup instructions inside a settings card. Numbered because they are a real
   sequence -- step four fails if step two was skipped. */
.setup-steps { margin: 12px 0 0; padding-left: 20px; color: var(--muted);
  font-size: 12.5px; line-height: 1.85; }
.setup-steps b { color: var(--text); font-weight: 600; }
.setup-uri { display: block; margin-top: 5px; padding: 7px 9px; border-radius: 7px;
  background: var(--field); border: 1px solid var(--line); color: var(--text);
  font-size: 11.5px; word-break: break-all; user-select: all; }
.button.is-off { opacity: .5; pointer-events: none; }

/* A button that reads as a link — used where the sentence is the instruction
   and a boxed button would interrupt it. */
.linky { background: none; border: 0; padding: 0; color: var(--blue);
  font: inherit; cursor: pointer; text-decoration: underline;
  text-underline-offset: 2px; }
.linky:hover { color: var(--text); }
.filedrop.over { border-color: var(--blue); background: var(--tint-blue); }
.filedrop.over svg { color: var(--blue); }

/* The statement grid. Chase descriptions are long enough to squeeze the
   category dropdown to three letters, which is exactly what happened -- the
   column that has to be usable was the one giving up its width. */
#bankLines th:nth-child(2), #bankLines td:nth-child(2) {
  max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#bankLines th:nth-child(4), #bankLines td:nth-child(4) { min-width: 200px; }
#bankLines td:nth-child(4) .pr-mini { max-width: none; }
#bankLines th:nth-child(5), #bankLines td:nth-child(5) { min-width: 200px; }
.bank-desc { cursor: help; }

/* "Looks right" — accepting the categoriser's guess. Quiet until you hover,
   because agreeing is the common case and a row of loud buttons reads as a
   list of problems. */
.ask-yes { padding: 5px 11px; border-radius: 8px; font-size: 12px; font-weight: 600;
  border: 1px solid var(--line); background: transparent; color: var(--muted);
  white-space: nowrap; transition: background .14s ease, color .14s ease,
  border-color .14s ease; }
.ask-yes:hover:not([disabled]) { background: var(--tint-green); color: var(--green);
  border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.ask-yes[disabled] { opacity: .4; cursor: default; }
.ask-foot { margin: 10px 2px 0; }

/* The dates behind a grouped question. Folded away because the answer usually
   does not need them -- but Stefan searches his bank by date, so when he does
   need them he needs every one, not a range, and the raw description too. */
.ask-dates { grid-column: 1 / -1; margin-top: 2px; }
.ask-dates summary { font-size: 11.5px; color: var(--subtle); cursor: pointer;
  list-style: none; display: inline-flex; align-items: center; gap: 4px; }
.ask-dates summary::-webkit-details-marker { display: none; }
.ask-dates summary::before { content: "▸"; font-size: 9px; transition: transform .15s ease; }
.ask-dates[open] summary::before { transform: rotate(90deg); }
.ask-dates summary:hover { color: var(--text); }
.ask-when { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 8px 0 0;
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ask-when b { color: var(--text); font-weight: 600; }

.mr-when { display: flex; align-items: center; gap: 7px; color: var(--muted);
  font-size: 12px; }
.recur { border: 1px solid var(--line); border-radius: 11px; padding: 12px 14px;
  background: var(--lift-1); }
.recur summary { cursor: pointer; font-size: 13px; }
.recur summary .helper { margin-left: 6px; }
.recur-body { margin-top: 4px; }

/* Files waiting to be uploaded. Deliberately looks like a list of things you
   still have to do something to, not like a list of things that happened. */
#bankQueue { margin: 12px 0; padding: 12px 13px; border-radius: 12px;
  border: 1px dashed var(--line-strong); background: var(--lift-1); }
.queue-head { display:flex; align-items:baseline; gap:10px; margin-bottom:9px;
  font-size:11.5px; font-weight:700; color:var(--text); }
.queue-head .linky { margin-left:auto; font-weight:600; }
.queue-row { display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:9px 10px; border-radius:10px; background:var(--panel);
  border:1px solid var(--line); }
.queue-row + .queue-row { margin-top:7px; }
.queue-row.done { opacity:.55; }
.queue-file { display:flex; flex-direction:column; gap:1px; min-width:160px;
  max-width:230px; }
.queue-file strong { font-size:12px; font-weight:650; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.queue-row input, .queue-row select { height:32px; font-size:12px; }
.queue-row .q-drop { margin-left:auto; }

/* The MRR tables fold. Native <details>, so it works with no JS and keeps
   keyboard and find-in-page behaviour; only the remembered state is scripted.
   The default triangle is replaced because it sits on the text baseline of an
   <h2> and looks like a bullet rather than a control. */
.mr-fold > summary { cursor: pointer; list-style: none; display: flex;
  align-items: baseline; gap: 9px; }
.mr-fold > summary::-webkit-details-marker { display: none; }
.mr-fold > summary::before { content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 5px solid currentColor; border-top: 4px solid transparent;
  border-bottom: 4px solid transparent; color: var(--subtle);
  transition: transform .16s ease; transform-origin: 25% 50%; }
.mr-fold[open] > summary::before { transform: rotate(90deg); }
.mr-fold > summary:hover::before { color: var(--text); }
.mr-fold > summary h2 { margin: 0; }
/* The figures ride to the right of the heading, so a shut fold reads as a row
   of numbers rather than a closed door. Same shape as the stat cards above. */
.mr-fold > summary .fold-kpis { display: flex; flex-wrap: wrap; gap: 4px 16px;
  margin-left: auto; font-size: 11px; color: var(--subtle); }
.mr-fold > summary .fold-kpi b { color: var(--text); font-weight: 700;
  font-variant-numeric: tabular-nums; margin-right: 3px; }
.mr-fold > summary:focus-visible { outline: 2px solid var(--blue);
  outline-offset: 3px; border-radius: 6px; }
/* A shut fold is just its heading, so the card should not keep the padding it
   needs when open. */
.mr-fold:not([open]) { padding-top: 12px; padding-bottom: 12px; }
.pill.ok { background: var(--tint-green); color: var(--green); }

/* ---------- tasks ----------
   One column per list, side by side and scrolling sideways, because that is the
   shape Stefan already reads every day in Google Tasks. Circle ticks, a star on
   the right, and an overdue date that earns colour by being late. */
.tk-board { display: flex; gap: 14px; overflow-x: auto; padding: 4px 2px 20px;
  align-items: flex-start; margin-top: 20px; scrollbar-width: thin; }
.tkc { flex: 0 0 296px; border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel); padding: 14px 12px 10px;
  box-shadow: var(--shadow-sm); }
.tkc-head { display: flex; align-items: center; gap: 6px; padding: 0 4px 4px; }
.tkc-head h2 { margin: 0; font-size: 14.5px; letter-spacing: -.01em; flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tkc-menu { border: 0; background: none; color: var(--subtle); font-size: 11px;
  padding: 3px 6px; border-radius: 6px; cursor: pointer; opacity: 0;
  transition: opacity .14s ease, color .14s ease, background .14s ease; }
.tkc:hover .tkc-menu { opacity: 1; }
.tkc-menu:hover { background: var(--lift-2); color: var(--text); }
.tkc-menu.danger:hover { color: var(--red); background: var(--tint-red); }

.tkc-add { display: flex; align-items: center; gap: 8px; width: 100%;
  border: 0; background: none; color: var(--blue); font: inherit; font-size: 12.5px;
  font-weight: 600; padding: 7px 6px; border-radius: 8px; cursor: pointer;
  transition: background .14s ease; }
.tkc-add:hover { background: var(--tint-blue); }
.tkc-plus { font-size: 15px; line-height: 1; }
.tkc-input { padding: 4px 2px 6px; }
/* The box you type a new task into wraps and grows with it, the way Google
   Tasks does -- never a single line that scrolls the start of the sentence
   off to the left. Stefan, 09-19-2026. */
.tkc-input textarea { font-size: 13px; min-height: 36px; padding: 8px 9px;
  line-height: 1.42; resize: none; overflow: hidden; }
.tkc-body { display: flex; flex-direction: column; gap: 2px; min-height: 30px;
  border-radius: 9px; transition: background .14s ease; }
.tkc-body.over { background: var(--tint-blue); }
.tkc-empty { color: var(--subtle); font-size: 12px; padding: 8px 6px; margin: 0; }
.tkc-done { margin-top: 6px; border-top: 1px solid var(--line); padding-top: 6px; }
.tkc-done summary { color: var(--muted); font-size: 12px; cursor: pointer;
  padding: 4px 6px; border-radius: 7px; }
.tkc-done summary:hover { background: var(--lift-2); }
.tkc-new { flex: 0 0 220px; display: flex; gap: 6px; align-items: center;
  background: transparent; border-style: dashed; }
/* The two fixed columns. Assigned is the one that matters -- it is what the
   red count on the sidebar is about -- so it carries the house colour; Given
   is a receipt and stays quiet. Neither takes a drop: they are views. */
.tkc-assigned { --card-color: var(--blue); border-color: color-mix(in srgb, var(--blue) 45%, var(--line)); }
.tkc-assigned .tkc-head h2 { color: var(--blue); }
.tkc-given { background: transparent; border-style: dashed; box-shadow: none; }
.tkc-given .tkc-head h2 { color: var(--muted); }
.tkc-count { flex: 0 0 auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
  background: var(--blue-solid); color: #fff; font-size: 11px; font-weight: 800; line-height: 20px;
  text-align: center; font-variant-numeric: tabular-nums; }
.tkc-given .tkc-count { background: var(--lift-3); color: var(--muted); }
.tkc.is-fixed .tkt { cursor: default; }
.tkc.is-fixed .tkt-grip { visibility: hidden; }
/* Handed to you and not yet looked at: a dot where the grip would be. */
.tkt.is-new .tkt-grip { visibility: visible; opacity: 1; }
.tkt.is-new .tkt-grip::before { content: ""; display: block; width: 7px; height: 7px; margin: 3px 0 0 1px;
  border-radius: 50%; background: var(--red); }
.tkt-client { font-size: 10.5px; padding: 2px 7px; border-radius: 20px; background: var(--tint-green, rgba(53,201,143,.12)); color: var(--ink-good, #35c98f); }
.tkt-list { font-size: 10.5px; padding: 2px 7px; border-radius: 20px; background: var(--lift-3); color: var(--muted); }
/* The Starred view: one column, the star's colour. */
.tkc-starred { --card-color: var(--amber); border-color: color-mix(in srgb, var(--amber) 50%, var(--line)); flex-basis: 360px; }
.tkc-starred .tkc-head h2 { color: var(--amber); }
.tkc-starred .tkc-count { background: var(--amber); color: #1a1408; }

/* One task */
.tkt { display: flex; gap: 7px; align-items: flex-start; padding: 8px 6px 8px 4px;
  border-radius: 9px; cursor: grab; transition: background .14s ease; }
/* The six dots, the same handle as the sidebar's: there on hover, the whole
   card still drags. */
.tkt-grip { flex: 0 0 10px; margin-top: 3px; color: var(--subtle); fill: currentColor; opacity: 0; transition: opacity .12s ease; }
.tkt:hover .tkt-grip, .tkt.is-dragging .tkt-grip { opacity: .7; }
.tkt:active { cursor: grabbing; }
.tk-grow { resize: none; overflow: hidden; line-height: 1.4; min-height: 38px; }
#tkdText { font-size: 14px; font-weight: 650; }
.tkt:hover { background: var(--lift-2); }
.tkt.is-open { background: var(--tint-blue); }
.tkt.is-dragging { opacity: .4; }
.tkt.is-done .tkt-text { text-decoration: line-through; color: var(--muted); }
.tkt.is-done { opacity: .6; }
/* A circle, not a checkbox. Google Tasks uses one and it is the single detail
   that makes a list read as a list rather than as a form. */
.tkt-tick { flex: 0 0 17px; width: 17px; height: 17px; margin-top: 2px;
  border: 1.6px solid var(--line-strong); border-radius: 50%; background: none;
  cursor: pointer; position: relative; transition: border-color .14s ease,
  background .14s ease; }
.tkt-tick:hover { border-color: var(--blue); }
.tkt-tick.on { background: var(--blue-solid); border-color: var(--blue-solid); }
.tkt-tick.on::after { content: ""; position: absolute; left: 4.5px; top: 1.5px;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 1.8px 1.8px 0;
  transform: rotate(42deg); }
.tkt-body { flex: 1; min-width: 0; }
.tkt-text { font-size: 13px; line-height: 1.42; word-break: break-word; }
.tkt-note { color: var(--muted); font-size: 11.5px; line-height: 1.45; margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.tkt-link { display: block; color: var(--blue); font-size: 11px; margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tkt-meta { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.tkt-due, .tkt-subs, .tkt-who { font-size: 10.5px; padding: 2px 7px;
  border-radius: 20px; background: var(--lift-3); color: var(--muted); }
.tkt-due.late { background: var(--tint-red); color: var(--red); font-weight: 600; }
.tkt-subs { background: var(--tint-blue); color: var(--blue); }
.tkt-who { background: var(--tint-violet); color: var(--violet); }
.tkt-star { flex: 0 0 22px; border: 0; background: none; color: var(--line-strong);
  cursor: pointer; padding: 2px; border-radius: 6px; margin-top: 1px;
  transition: color .14s ease; }
.tkt:hover .tkt-star { color: var(--subtle); }
.tkt-star.on, .tkt-star.on:hover { color: var(--amber); }
.tkt-star:hover { color: var(--amber); }

/* The detail, to the right, over the board */
.tk-detail { position: fixed; top: 0; right: 0; width: min(380px, 100%);
  height: 100vh; z-index: 30; display: flex; flex-direction: column;
  background: linear-gradient(160deg, var(--panel), var(--panel-2));
  border-left: 1px solid var(--line); box-shadow: -30px 0 80px var(--sink-1); }
.tk-detail-body { padding: 14px; overflow-y: auto; display: flex;
  flex-direction: column; gap: 6px; }
.tk-detail-body label { display: block; margin-top: 6px; }
.tk-fields { grid-template-columns: 1fr 1fr; }
.tk-detail-foot { padding: 12px 14px; border-top: 1px solid var(--line);
  display: flex; align-items: center; gap: 12px; }
.tk-linky { background: none; border: 0; padding: 0; color: var(--muted);
  font: inherit; font-size: 11.5px; cursor: pointer; }
.tk-linky.danger:hover { color: var(--red); }
.tk-links { display: flex; flex-direction: column; gap: 4px; }
.tk-link { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.tk-link a { color: var(--blue); word-break: break-all; }
.tk-linkadd { display: flex; gap: 5px; margin-top: 4px; }
.tk-subs { display: flex; flex-direction: column; gap: 4px; }
.tk-sub { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px;
  color: var(--muted); }
.tk-sub.is-done span { text-decoration: line-through; opacity: .7; }
.tk-x { border: 0; background: none; color: var(--subtle); font-size: 11px;
  padding: 2px 5px; border-radius: 6px; cursor: pointer; }
.tk-x:hover { color: var(--red); background: var(--tint-red); }
@media (max-width: 640px) { .tkc { flex: 0 0 84vw; } .tk-detail { width: 100%; } }

/* ---------- the house card ----------
   Stefan pointed at the corner glow on a metric tile and asked for it
   everywhere: "easier to click on, have some weight, look like they're
   floating." So the treatment that was on one tile type becomes the treatment
   for anything you can open.

   The glow is a radial wash bled off the bottom-right corner. It is deliberately
   faint -- what makes a card read as liftable is the LIFT on hover, and the
   colour is there so the corner is not dead space. --card-color lets a card say
   something with it; unset, it is the house blue. */
.card, .module-card, .tkc, .metric, .stat, .table-wrap {
  position: relative;
}
.card::after, .module-card::after, .tkc::after {
  content: ""; position: absolute; inset: auto -24px -50px auto;
  width: 118px; height: 118px; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, var(--card-color, var(--tint-blue)),
              transparent 70%);
  opacity: .85; transition: opacity .18s ease, transform .18s ease;
}
.card > *, .module-card > *, .tkc > * { position: relative; z-index: 1; }
.card, .module-card, .tkc { overflow: hidden; }
/* A sheet that is also a card keeps the glow but not the bleed. The bleed
   reaches 24px past the right edge and 50px past the bottom, and inside a
   .modal that made the sheet scrollable sideways: any nudge (a focus, a
   trackpad) slid its content left over the padding, and the sign-in link
   sheet on Client Hub read flush to the edge (10-20-2026). The same circle,
   drawn only where it was ever visible, so nothing overflows -- and .modal's
   own overflow:auto is back, so a tall sheet scrolls instead of clipping. */
.modal.card { overflow: auto; }
.modal.card::after {
  inset: auto 0 0 auto; width: 94px; height: 68px;
  border-radius: 0;
  background: radial-gradient(circle 83px at 59px 59px,
              var(--card-color, var(--tint-blue)), transparent 70%);
}

/* Anything that opens something lifts. Anything that does not, does not --
   a card that rises under the cursor and then cannot be clicked is a lie. */
.module-card, .tkc, .card.is-clickable {
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.module-card:hover, .card.is-clickable:hover {
  transform: translateY(-2px); border-color: var(--line-strong);
  box-shadow: var(--shadow-lg);
}
.module-card:hover::after, .card.is-clickable:hover::after {
  opacity: 1; transform: scale(1.12);
}
.module-card.is-idle:hover { transform: none; box-shadow: var(--shadow); }
@media (prefers-reduced-motion: reduce) {
  .module-card:hover, .card.is-clickable:hover, .tkc:hover { transform: none; }
  .card::after, .module-card::after, .tkc::after { transition: none; }
}

/* A card can colour its own corner by what it is saying. Semantic, not
   decorative: green means the number is fine, red means it is not. */
.card.tone-good, .module-card.tone-good { --card-color: var(--tint-green); }
.card.tone-warn, .module-card.tone-warn { --card-color: var(--tint-amber); }
.card.tone-bad,  .module-card.tone-bad  { --card-color: var(--tint-red); }
.card.tone-quiet, .module-card.tone-quiet { --card-color: var(--lift-2); }

/* ---------- AI chat rail ----------
   A chat needed three things it did not have: when it was, a way to copy it out,
   and a way to rename or delete it without opening it first. */
.ai-proj-row { display: flex; align-items: stretch; border-radius: 9px;
  transition: background .14s ease; }
.ai-proj-row:hover { background: var(--lift-2); }
.ai-proj-row.on { background: var(--tint-blue); }
.ai-proj-row .ai-proj { flex: 1; min-width: 0; background: none; }
.ai-proj-row:hover .ai-proj, .ai-proj-row.on .ai-proj { background: none; }
.ai-proj-more { flex: 0 0 26px; border: 0; background: none; color: var(--subtle);
  font-size: 15px; line-height: 1; border-radius: 7px; cursor: pointer;
  opacity: 0; transition: opacity .14s ease, color .14s ease; }
.ai-proj-row:hover .ai-proj-more, .ai-proj-row.on .ai-proj-more { opacity: 1; }
.ai-proj-more:hover { color: var(--text); background: var(--lift-3); }

.ai-menu { position: fixed; z-index: 60; min-width: 178px; padding: 5px;
  border: 1px solid var(--line); border-radius: 11px; background: var(--panel-2);
  box-shadow: var(--shadow-lg); display: flex; flex-direction: column; gap: 1px; }
.ai-menu button { text-align: left; border: 0; background: none; color: var(--text);
  font: inherit; font-size: 12.5px; padding: 8px 10px; border-radius: 7px;
  cursor: pointer; }
.ai-menu button:hover { background: var(--lift-3); }
.ai-menu button.danger { color: var(--red); }
.ai-menu button.danger:hover { background: var(--tint-red); }


/* A metric that explains itself. Stefan's reason for wanting these: he should
   not have to come back to a chat log to remember how a number was reached, or
   ask anyone. The "?" shows on hover and on keyboard focus, and opens a note
   over the card without navigating away -- the card itself still links through. */
.metric { position: relative; }
.metric-why { position:absolute; top:10px; right:10px; z-index:3;
  width:19px; height:19px; border-radius:50%; padding:0; line-height:17px;
  font-size:11px; font-weight:800; cursor:pointer;
  border:1px solid var(--line-strong); background:var(--lift-1); color:var(--muted);
  opacity:0; transition:opacity .14s, color .14s, border-color .14s; }
.metric:hover .metric-why, .metric-why:focus-visible, .metric-why[aria-expanded="true"] {
  opacity:1; }
.metric-why:hover, .metric-why[aria-expanded="true"] {
  color:var(--blue); border-color:var(--blue); }
/* The explanation as a popover beside the card -- the one explain component:
   HQ, Payroll, Ads and Finance all use it, so the number and what it means
   can be read together. */
.why-pop { position:fixed; z-index:60; width:min(300px, calc(100vw - 24px));
  padding:13px 15px; border-radius:12px; pointer-events:auto;
  background:var(--panel); border:1px solid var(--blue);
  box-shadow:0 14px 38px -12px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.16);
  color:var(--text); font-size:11.5px; line-height:1.55;
  opacity:0; visibility:hidden; transform:translateY(-4px);
  transition:opacity .13s ease, transform .13s ease; }
.why-pop.open { opacity:1; visibility:visible; transform:translateY(0); }
.why-pop h6 { margin:0 0 6px; font-size:9.5px; font-weight:800; letter-spacing:.09em;
  text-transform:uppercase; color:var(--muted); }
.why-pop p { margin:0; white-space:pre-line; }
@media (prefers-reduced-motion:reduce) { .why-pop { transition:none; } }


/* ---------------------------------------------------------------------------
   Hover, everywhere something can be clicked.

   Stefan's reference is ClickUp: point at a client name, an icon, anything
   actionable, and a quiet highlight appears saying "this is the thing you have
   got". It is not decoration -- it is the answer to "will something happen if I
   click here", given before the click rather than after it.

   Subtle on purpose. --lift-2 is a few percent of ink, so it reads as a change
   of state and never as a colour. One rule set rather than forty, so a new page
   inherits it without anyone remembering to.
   --------------------------------------------------------------------------- */

/* Rows you can open. Marked by the cursor the page already sets, so nothing
   has to be relabelled to take part. */
.grid tbody tr:hover td,
tbody tr[data-id]:hover td,
tbody tr[onclick]:hover td,
tr.row-link:hover td {
  background: var(--lift-2);
}
.grid tbody tr { transition: background .13s ease; }
.grid tbody tr:has(a):hover td,
.grid tbody tr[data-id]:hover td { background: var(--lift-2); }

/* Links inside tables and lists: the whole word lifts, not just the underline.
   NOT the ones styled as buttons. This selector is (0,2,2) and `.button:hover`
   is (0,2,0), so it won on specificity and turned white button text blue --
   blue on the blue gradient, which is most of the way to invisible. It has
   been wrong since the buttons became gradients; a flat blue button hid it
   because blue on blue was merely dull rather than unreadable. */
.grid td a:not(.button):hover,
.table-wrap td a:not(.button):hover {
  color: var(--blue);
  text-decoration: none;
}

/* Anything small and clickable — icon buttons, link buttons, chips, pickers.
   They shared a habit of only changing colour, which on a busy row is easy to
   miss; a ground behind them is not. */
.link-btn, .nav-icon-btn, .status-pick, .msg-x, .chip, .pill-btn,
.tb-btn, .theme-toggle, .metric-why, summary {
  border-radius: 7px;
  transition: background .13s ease, color .13s ease, border-color .13s ease;
}
.link-btn:hover, .nav-icon-btn:hover, .msg-x:hover, .chip:hover,
.pill-btn:hover, .tb-btn:hover {
  background: var(--lift-2);
  color: var(--text);
}
.link-btn.danger:hover { background: var(--tint-red); color: var(--red); }

/* A <summary> is a button that does not look like one until you touch it. */
summary { padding-inline: 6px; margin-inline: -6px; }
summary:hover { background: var(--lift-2); }

/* Cards that navigate. The lift is the same everywhere; only the distance
   changes, so a big card moves as little as a small one. */
.metric-link:hover, .module-card[data-href]:hover, .sop:hover, .wl:hover {
  background: var(--lift-1);
}

/* Focus is the same promise for a keyboard. Never remove it, and never let it
   be quieter than hover -- somebody navigating by keyboard has less to go on,
   not more. */
a:focus-visible, button:focus-visible, summary:focus-visible,
[tabindex]:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .grid tbody tr, .link-btn, .nav-icon-btn, .status-pick, .msg-x, .chip,
  .pill-btn, .tb-btn, summary { transition: none; }
}


/* Renewal overdue. Worked out from the end date rather than stored, so it
   reaches In-Person clients too -- their status is a delivery mode, not a
   lifecycle stage, and overwriting it to say "overdue" would lose which of
   Mike's people he actually trains in the gym. Two facts, both kept. */
.chip-overdue {
  display: inline-block; margin-left: 7px; padding: 1.5px 7px;
  border-radius: 20px; font-size: 8.5px; font-weight: 900;
  letter-spacing: .06em; text-transform: uppercase; vertical-align: 1px;
  color: var(--alarm-ink); background: var(--alarm-bg);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--alarm-bg) 22%, transparent);
}
.renew-late {
  color: var(--alarm-ink); background: var(--alarm-bg);
  font-weight: 800; font-size: 10px; padding: 1.5px 6px; border-radius: 5px;
}

/* The count above the roster. Hidden at zero, which is where it should live --
   so when it appears at all, it appears loudly. */
#overdueFilter {
  border: 0; font-weight: 900; letter-spacing: .03em; text-transform: uppercase;
  font-size: 9.5px; padding: 6px 11px; border-radius: 8px;
  color: var(--alarm-ink); background: var(--alarm-bg);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--alarm-bg) 55%, transparent);
  animation: alarmPulse 2.4s ease-out infinite;
}
#overdueFilter:hover { filter: brightness(1.08); }
#overdueFilter.on {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--alarm-bg) 45%, transparent);
  animation: none;
}
@keyframes alarmPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--alarm-bg) 55%, transparent); }
  70%  { box-shadow: 0 0 0 9px color-mix(in srgb, var(--alarm-bg) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--alarm-bg) 0%, transparent); }
}
/* Money past due: the same alarm, one step hotter and in its own colour, so
   "3 payments past due" and "9 renewals overdue" never read as one number. */
#pastDueFilter {
  border: 0; font-weight: 900; letter-spacing: .03em; text-transform: uppercase;
  font-size: 9.5px; padding: 6px 11px; border-radius: 8px;
  color: #fff; background: var(--money-alarm, #c2410c);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--money-alarm, #c2410c) 55%, transparent);
  animation: moneyPulse 2.4s ease-out infinite;
}
#pastDueFilter:hover { filter: brightness(1.08); }
#pastDueFilter.on {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--money-alarm, #c2410c) 45%, transparent);
  animation: none;
}
@keyframes moneyPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--money-alarm, #c2410c) 55%, transparent); }
  70%  { box-shadow: 0 0 0 9px color-mix(in srgb, var(--money-alarm, #c2410c) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--money-alarm, #c2410c) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  #overdueFilter { animation: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--alarm-bg) 35%, transparent); }
  #pastDueFilter { animation: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--money-alarm, #c2410c) 35%, transparent); }
}

/* ---- Lifetime value (Finance) ------------------------------------------
   The distribution is a table with bars in it, not a chart: each band keeps
   its count and share written beside the bar, and the longest band runs the
   full width so the shape reads at a glance. One hue, since every bar is the
   same thing (clients) and only the magnitude differs. */
.ltv-dist { display:grid; gap:7px; padding:4px 0 2px; }
.ltv-band { display:grid; grid-template-columns: 96px 1fr 44px 52px; align-items:center;
  gap:10px; font-size:12px; font-variant-numeric: tabular-nums; }
.ltv-band-label { color:var(--muted); white-space:nowrap; }
.ltv-band-bar { display:block; height:14px; border-radius:4px; background:var(--tint-blue);
  overflow:hidden; }
.ltv-band-bar i { display:block; height:100%; border-radius:4px; background:var(--blue);
  opacity:.82; transition: width .35s ease; }
.ltv-band-n { text-align:right; font-weight:700; }
.ltv-band-pct { text-align:right; color:var(--subtle); }
@media (max-width: 640px) {
  .ltv-band { grid-template-columns: 80px 1fr 40px 46px; gap:6px; font-size:11px; }
}

#ltvAll tr.muted td { color: var(--subtle); }
#ltvAll tr.muted td a { color: var(--muted); }

/* ---- loading -------------------------------------------------------------
   Two signals while a page's data is on its way, both driven by the fetch
   wrapper in common.js. The bar along the top edge says "working" the way
   every browser and app already does; the placeholder shapes below say
   where the numbers and rows are about to be, so the page has its layout
   before it has its content instead of arriving blank and then jumping.
   The shapes exist only while html.oo-loading is on -- an empty container
   after loading is just empty. */
.loadbar { position:fixed; top:0; left:0; right:0; height:2px; z-index:90;
  background:linear-gradient(90deg, transparent 0%, var(--blue) 30%, var(--blue) 70%, transparent 100%);
  background-size:40% 100%; background-repeat:no-repeat;
  animation:loadbar 1.1s ease-in-out infinite; pointer-events:none; }
.loadbar[hidden] { display:none; }
@keyframes loadbar { 0% { background-position:-40% 0; } 100% { background-position:140% 0; } }

@keyframes skel { 0% { background-position:-200% 0; } 100% { background-position:200% 0; } }
.oo-loading .metric-grid:empty::before, .oo-loading .stat-row:empty::before,
.oo-loading .kpi-strip:empty::before, .oo-loading .split-list:empty::before,
.oo-loading .bar-list:empty::before, .oo-loading .chart-box:empty::before,
.oo-loading .msg-log:empty::before, .oo-loading .ibx-list:empty::before,
.oo-loading .tk-board:empty::before, .oo-loading .module-grid:empty::before,
.oo-loading .tool-grid:empty::before, .oo-loading .skel:empty::before {
  content:""; display:block; grid-column:1 / -1; flex:1 1 100%; width:100%;
  border-radius:12px; min-height:72px;
  background:linear-gradient(90deg, var(--lift-1) 0%, var(--lift-3) 45%, var(--lift-1) 90%);
  background-size:200% 100%; animation:skel 1.4s ease-in-out infinite; }
.oo-loading .metric-grid:empty::before { min-height:108px; }
.oo-loading .chart-box:empty::before, .oo-loading .msg-log:empty::before,
.oo-loading .ibx-list:empty::before, .oo-loading .tk-board:empty::before { min-height:220px; }
/* Tables: a stack of row-shaped bands instead of one block, so the shape
   says "list" before the list is there. A table with no rows has no height
   of its own, so it is given one while it waits. */
.oo-loading table.grid:empty, .oo-loading table:empty[id] {
  display:block; min-height:150px; border-radius:10px;
  background:repeating-linear-gradient(180deg, var(--lift-2) 0 22px, transparent 22px 34px);
  -webkit-mask:linear-gradient(90deg, #000 0%, #000 60%, rgba(0,0,0,.35) 100%);
  mask:linear-gradient(90deg, #000 0%, #000 60%, rgba(0,0,0,.35) 100%);
  animation:skel-fade 1.4s ease-in-out infinite; }
/* A header already on the page with an empty body under it: the bands go
   under the header, drawn on the wrapper so the header keeps its layout. */
.oo-loading .table-wrap:has(> table > tbody:empty)::after {
  content:""; display:block; min-height:120px; border-radius:0 0 10px 10px;
  background:repeating-linear-gradient(180deg, var(--lift-2) 0 22px, transparent 22px 34px);
  -webkit-mask:linear-gradient(90deg, #000 0%, #000 60%, rgba(0,0,0,.35) 100%);
  mask:linear-gradient(90deg, #000 0%, #000 60%, rgba(0,0,0,.35) 100%);
  animation:skel-fade 1.4s ease-in-out infinite; }
@keyframes skel-fade { 0%, 100% { opacity:.55; } 50% { opacity:1; } }
@media (prefers-reduced-motion: reduce) {
  .loadbar, .oo-loading *::before, .oo-loading *::after, .oo-loading table { animation:none !important; }
}
/* After a failure, a section that stayed empty says so instead of reading as
   "nothing here". Same containers as the placeholders above. */
.oo-failed:not(.oo-loading) .metric-grid:empty::before, .oo-failed:not(.oo-loading) .stat-row:empty::before,
.oo-failed:not(.oo-loading) .kpi-strip:empty::before, .oo-failed:not(.oo-loading) .split-list:empty::before,
.oo-failed:not(.oo-loading) .bar-list:empty::before, .oo-failed:not(.oo-loading) .chart-box:empty::before,
.oo-failed:not(.oo-loading) .msg-log:empty::before, .oo-failed:not(.oo-loading) .ibx-list:empty::before,
.oo-failed:not(.oo-loading) .tk-board:empty::before, .oo-failed:not(.oo-loading) .module-grid:empty::before,
.oo-failed:not(.oo-loading) table.grid:empty::before, .oo-failed:not(.oo-loading) table:empty[id]::before,
.oo-failed:not(.oo-loading) .table-wrap:has(> table > tbody:empty)::after {
  content:"Couldn't load this — reload the page to try again."; display:block; grid-column:1 / -1;
  padding:14px 16px; border:1px dashed rgba(251,100,114,.45); border-radius:10px;
  color:var(--ink-bad); font-size:12.5px; background:var(--tint-red); }
.oo-failed:not(.oo-loading) table.grid:empty, .oo-failed:not(.oo-loading) table:empty[id] { display:block; }

/* ==========================================================================
   Phones. The shell already folds to a rail; these are the PAGES. One
   block, so the next person can see every phone rule in one read. Rule of
   thumb throughout: a row of controls becomes a scrolling strip, a table
   scrolls inside its own frame, a dialog takes the whole screen, and the
   page itself never scrolls sideways.
   ========================================================================== */
@media (max-width: 680px) {
  /* -- the page never scrolls sideways; anything wide scrolls in its frame.
        `clip`, not `hidden`: hidden makes the body a scroll container that
        never scrolls, and a sticky top bar inside it never sticks. */
  html, body { overflow-x: clip; }
  main { overflow-x: clip; }

  /* -- top bar: eight controls have to fit beside the rail on a 375px screen */
  .topbar { gap: 6px 10px; padding-top: 8px; }
  .crumb { flex-basis: 100%; order: 1; font-size: 12px; }
  .topbar-actions { flex-basis: 100%; order: 2; gap: 3px; justify-content: space-between; }
  .topbar-actions .tb-btn { height: 34px; min-width: 34px; padding: 0 6px; }
  .tb-clocks { margin-right: 0; }
  .tb-clocks.tbc-solo { display: none; }
  .tb-clock { height: 34px; }
  .tbc-toggle { display: none; }
  .tbc-zone { padding: 0 7px; }
  .tbc-zone b { font-size: 12px; }
  .tb-account { margin-left: 0; }
  .tb-account .account-avatar { width: 34px; height: 34px; flex-basis: 34px; }

  /* -- tab rows scroll instead of stacking three deep (Finance had six tabs
        on three lines before a number was visible) */
  .tab-row { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding-bottom: 8px; margin-inline: -4px; padding-inline: 4px; }
  .tab-row::-webkit-scrollbar { display: none; }
  .tab-row .tb-btn { flex: 0 0 auto; white-space: nowrap; }

  /* -- cards, stats, folds */
  .stat-row { gap: 8px; }
  .stat-row .stat { flex: 1 1 calc(50% - 8px); min-width: 0; padding: 10px 12px; }
  .stat .v { font-size: 17px; }
  .metric { min-height: 96px; padding: 12px 14px; }
  .metric-value { font-size: 20px; }
  .mr-fold > summary { flex-wrap: wrap; gap: 4px 12px; }
  .mr-fold > summary h2 { font-size: 15px; }
  .mr-fold > summary .fold-kpis { flex-basis: 100%; gap: 3px 12px; }
  .metric-detail { padding: 12px 12px; }
  .metric-detail .working { max-width: none; }

  /* -- dialogs take the screen: the client profile, book-a-call, save a
        document, the end-of-day report -- every .brain-overlay modal */
  .brain-overlay { padding: 0; align-items: stretch; }
  .brain-overlay .brain-panel { width: 100%; max-width: none; height: 100%;
    max-height: none; border-radius: 0; border: 0; }
  .note-dialog, .note-dialog.wide { width: 100%; }
  .note-dialog .brain-head { flex-wrap: wrap; gap: 6px 10px; padding: 12px 14px; }
  .note-dialog .brain-head strong { max-width: calc(100% - 60px); }
  .note-dialog .brain-body { padding: 12px 14px !important; }
  .money-grid { grid-template-columns: 1fr; }
  .convo-head { flex-wrap: wrap; gap: 8px; }

  /* -- Finance */
  .mr-when { flex-wrap: wrap; }
  .mr-when select { width: auto !important; flex: 1 1 120px; }
  .fin-top { flex-wrap: wrap; align-items: stretch; }
  .fin-top .cash-input, .fin-top .button { flex: 1 1 100%; }
  .fin-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .ledger td.lab { max-width: 60vw; }
  #bankQueue .queue-row { grid-template-columns: 1fr; }
  .ltv-band { grid-template-columns: 76px 1fr 36px 44px; gap: 6px; font-size: 11px; }

  /* -- Ads */
  .filter-bar { gap: 10px; }
  .filter-group { flex: 1 1 100%; }
  .filter-bar select { min-width: 0; width: 100%; }
  .filter-row-inner input[type="date"] { flex: 1 1 120px; min-width: 0; }
  .viz-root .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .chart-box { min-height: 200px; }
  .segmented { flex-wrap: wrap; }

  /* -- Clients: the roster scrolls in its frame; the by-coach table too */
  .roster th, .roster td { padding: 8px 8px; font-size: 12px; white-space: nowrap; }
  .tile-panel { width: 100vw; }

  /* -- Inbox: the actions drop under the message instead of squeezing it
        to one word a line */
  .ibx-card { flex-wrap: wrap; padding: 12px 14px; }
  .ibx-card-body { flex: 1 1 calc(100% - 44px); }
  .ibx-actions { flex: 0 1 auto; margin-left: auto; justify-content: flex-end; padding-top: 6px; }
  .ibx-mark { margin-top: 6px; }
  .ibx-head { flex-wrap: wrap; gap: 8px 12px; padding: 12px 14px; }
  .ibx-rail { padding: 6px; }
  .ibx-rail .ibx-rail-head { border-top: 0; margin: 0; padding: 8px 8px 2px; }
  .msg-panel-body .msg-turn { max-width: 96%; }

  /* -- Tasks: one column at a time, swipe for the next */
  .tk-board { gap: 10px; }
  .tkc { flex: 0 0 88vw; }

  /* -- Check-ins, Payroll, Calls: the toolbars wrap and the panels fill */
  .ci-panel { width: 100%; }
  .ci-outg-head { flex-wrap: wrap; gap: 6px; }
  .entry-row { grid-template-columns: 1fr !important; }
  .entry-row .span-2 { grid-column: auto; }
  .page-heading .action-row { width: 100%; }
  .page-heading .action-row .button { flex: 1 1 auto; justify-content: center; }

  /* -- the Brain and the search pill */
  .bside { width: 100vw; }
  .gsearch { min-width: 0; }
}

@media (max-width: 480px) {
  .metric-grid, .viz-root .metric-grid, .fin-metrics { grid-template-columns: 1fr !important; }
  .stat-row .stat { flex-basis: 100%; }
  .ltv-band { grid-template-columns: 64px 1fr 32px 40px; }
}

/* ==========================================================================
   Utilities. What the three hundred and eighty most repeated inline styles
   turned into, so spacing and widths come from one short list instead of a
   different number typed on every page. Spacing rhythm: 4 / 8 / 12 / 16 --
   the 10s and 14s that had crept in are 12 now. Widths: four sizes. These
   replace inline styles, so they carry the same authority.
   ========================================================================== */
.mt-4 { margin-top: 4px !important; }   .mt-8 { margin-top: 8px !important; }
.mt-12 { margin-top: 12px !important; } .mt-16 { margin-top: 16px !important; }
.mb-4 { margin-bottom: 4px !important; } .mb-8 { margin-bottom: 8px !important; }
.mb-12 { margin-bottom: 12px !important; } .mb-16 { margin-bottom: 16px !important; }
.block { display: block !important; }
.wrap { flex-wrap: wrap !important; }
.nowrap { white-space: nowrap !important; }
.justify-end { justify-content: flex-end !important; }
.grow { flex: 1 1 0% !important; min-width: 0; }
.hstack { display: flex !important; align-items: center; gap: 6px; }
.pointer { cursor: pointer; }
.select-all { user-select: all; }
.num { text-align: right !important; font-variant-numeric: tabular-nums; }
.tnum { font-variant-numeric: tabular-nums; }
.w-130 { width: 130px !important; } .w-160 { width: 160px !important; }
.w-110 { width: 110px !important; } .w-190 { width: 190px !important; } .w-220 { width: 220px !important; }
.w-full { width: 100% !important; } .w-auto { width: auto !important; }
.ink-bad { color: var(--ink-bad) !important; }
.ink-warn { color: var(--ink-warn) !important; }
/* The one that was missing. The client card asked for `ink-good` on a check-in
   that had gone back to the client and got no colour at all, because only the
   other two were ever written. */
.ink-good { color: var(--ink-good) !important; }
.ink-blue { color: var(--blue) !important; }
.fs-11 { font-size: 11px !important; } .fs-12 { font-size: 12px !important; }
@media (max-width: 680px) {
  .w-110, .w-130, .w-160, .w-190, .w-220 { width: auto !important; flex: 1 1 120px; min-width: 0; }
}

/* ---- Contracts: the agreement form, and what you get back ---------------
   Six class names on this page were invented and never given a rule --
   .tabs, .tab-btn, .ct-send, .ct-tpl, .ct-fields, .ct-link -- so the toggle
   was two raw browser buttons inside a styled card and the form was browser
   defaults. Stefan, 10-10-2026: "The Send and Template buttons are not on
   brand and feel kind of low quality." They were not low quality; they were
   not designed at all. */
.ct-send, .ct-tpl { display:grid; gap:14px; }
.ct-send > label, .ct-tpl > label, .ct-fields > label {
  display:grid; gap:6px; min-width:0; align-content:start; }
/* A tick is a row, not a stacked field: its words belong beside the box, not
   under it. The rule above lays every label out as a stacked grid, which
   turned this one into a 686px-wide checkbox with its sentence underneath. */
.ct-send > label.pr-check, .ct-tpl > label.pr-check, .ct-fields > label.pr-check {
  display:flex; align-items:center; gap:7px; }
.ct-tpl > label.pr-check input, .ct-send > label.pr-check input { flex:0 0 auto; width:auto; }
.ct-send > label > span, .ct-tpl > label > span, .ct-fields > label > span {
  font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--subtle); }
.ct-send input, .ct-send select, .ct-tpl input, .ct-tpl textarea, .ct-fields input {
  width:100%; padding:9px 11px; border-radius:9px; border:1px solid var(--line);
  background:var(--panel); color:var(--text); font:inherit; font-size:13px; }
.ct-send input:focus, .ct-send select:focus, .ct-tpl input:focus,
.ct-tpl textarea:focus, .ct-fields input:focus {
  outline:none; border-color:var(--blue); box-shadow:0 0 0 3px var(--tint-blue); }
.ct-tpl textarea { font:12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  resize:vertical; min-height:240px; }
/* The spots a template asks for, ONE PER ROW, the same rhythm as the four
   fields above them. I shipped these two across on 10-10-2026 and Stefan was
   straight back: "Things are not in order. They're not aligned ... it looks
   like it just threw up on the page." He was right, and two things were
   wrong at once.
   
   The heights are not comparable. A plain box, a box with four quick-pick
   chips under it, and a three-row textarea are not three things that belong
   in a row together -- and squeezed into a 190px column the chips wrapped
   onto two lines, which is what made it look thrown.
   
   And a stretched grid cell stretches what is inside it: each field is its
   own little grid, so a short one sitting beside a tall one had its label at
   the top of the cell and its input pushed to the bottom, with a hole in
   between. That is the gap under "Date of the agreement" in his screenshot.
   align-content keeps a field's own rows together whatever happens around
   it, and is kept here as the belt to the single column's braces. */
.ct-fields { display:grid; gap:14px; }
.ct-fields:empty { display:none; }
.ct-fields > label[hidden] { display:none; }

/* THE MOMENT IT WORKS. It was a read-only input with Copy beside it, which
   is the weakest-looking thing on the page attached to the one point where
   the work pays off. It says who is signing what, and offers the two ways to
   get it to them -- emailing it being the one that was impossible until
   OfferOps had a mailer (10-05-2026) and nobody came back to it. */
.ct-done { display:grid; gap:12px; padding:15px; border-radius:12px;
  border:1px solid color-mix(in srgb, var(--green) 38%, var(--line));
  background:color-mix(in srgb, var(--green) 7%, transparent); }
.ct-done[hidden] { display:none; }
.ct-done-head { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
.ct-done-head b { font-size:14px; }
.ct-done-head .muted { font-size:12.5px; }
.ct-done-url { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.ct-done-url input { flex:1 1 220px; min-width:0; padding:8px 10px; border-radius:8px;
  border:1px solid var(--line); background:var(--panel); color:var(--muted);
  font:12px ui-monospace, SFMono-Regular, Menlo, monospace; }
.ct-done .action-row { gap:8px; }
/* Copy the link and Text it to them, once done: green, and it stays that
   way. While Text is asking "Text to <number>?" it is dashed amber. */
.ct-done .button.is-done { color:var(--ink-good); border-color:color-mix(in srgb, var(--ink-good) 45%, var(--line)); }
.ct-done .button.is-done:disabled { opacity:1; }
.ct-done .button.pending { border-style:dashed; border-color:var(--ink-warn); color:var(--ink-warn); }

/* ---- the re-sign list: the roster and the Dashboard --------------------- */
.renew-card { padding:14px 16px; }
.renew-chip { display:inline-flex; align-items:center; gap:5px; padding:4px 10px; border-radius:999px;
  font-size:11.5px; font-weight:600; color:var(--muted); background:var(--lift-2);
  border:1px solid transparent; font-family:inherit; cursor:pointer;
  transition:border-color .14s ease, background .14s ease, transform .12s ease; }
.renew-chip:hover { border-color:var(--line-strong); transform:translateY(-1px); }
.renew-chip.on { border-color:var(--blue); background:var(--tint-blue); color:var(--blue-on-tint); }
.renew-chip.on b { color:var(--blue-on-tint); }
.renew-chip.renew-ended.on { border-color:var(--red); }
.renew-chip.renew-urgent.on { border-color:var(--amber); background:var(--tint-amber); color:var(--ink-warn); }
.renew-chip.renew-urgent.on b { color:var(--ink-warn); }
.renew-chip:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
.renew-chip b { color:var(--text); }
.renew-chip.renew-ended { background:var(--tint-red); color:var(--ink-bad); }
.renew-chip.renew-ended b { color:var(--ink-bad); }
.renew-chip.renew-urgent { background:var(--tint-amber); color:var(--ink-warn); }
.renew-chip.renew-urgent b { color:var(--ink-warn); }
.renew-stage { display:inline-block; margin-left:8px; font-size:10px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase; color:var(--subtle); }
tr.renew-ended .renew-stage { color:var(--ink-bad); }
tr.renew-urgent .renew-stage { color:var(--ink-warn); }
/* The runway pill under "Contract ends". One hue per band, each a tint with
   its ink, so it reads in both themes; ended is the one solid. */
.due { display:inline-block; min-width:64px; text-align:center; padding:3px 9px; border-radius:999px;
  font-size:11.5px; font-weight:700; font-variant-numeric:tabular-nums; margin-right:6px; }
.due-90 { background:var(--tint-green); color:var(--ink-good); }
.due-60 { background:var(--tint-yellow); color:var(--ink-yellow); }
.due-45 { background:var(--tint-amber); color:var(--ink-warn); }
.due-30 { background:var(--tint-red); color:var(--ink-bad); }
.due-ended { background:var(--alarm-bg); color:var(--alarm-ink); }
/* Compact: six rows on screen, the rest scroll inside the card. */
/* Tall enough to be a view of the roster rather than a peephole. It was six
   rows; Stefan asked on 09-18-2026 to see more at a 10,000-foot view, so it is
   about fourteen now and still scrolls when there are more than that. The
   second value keeps it off a short laptop screen, where a table taller than
   the window buries everything under it. */
/* No floor (10-20-2026): a 232px min-height left a filtered list of one
   row sitting in a box of blank space -- Stefan: "we don't need this much
   blank space". The card is as tall as its rows, up to the cap. */
.renew-scroll { max-height:min(544px, calc(100vh - 360px)); overflow-y:auto; }
.renew-scroll.uncapped { max-height:none; }
.renew-table th { position:sticky; top:0; z-index:1; background:var(--panel); }
.renew-table td { padding:6px 10px; font-size:12.5px; }
#renewTable td { vertical-align:middle; }
.renew-note-in { width:200px; max-width:50vw; height:30px; font-size:12px; }
/* An update is a sentence about where it has got to, so its box gets the
   column's width rather than a tick's. */
.renew-note.again { display:flex; flex-direction:column; align-items:stretch; gap:6px; width:100%; max-width:440px; }
.renew-note.again .renew-note-in { width:100%; min-width:0; max-width:none; height:auto; min-height:52px;
  resize:none; line-height:1.45; padding:6px 10px; font:inherit; font-size:12.5px; }
.renew-note.again .renew-note-btns { gap:10px; }
/* A floor as well as a ceiling. The table already scrolls sideways on a
   narrow window, and with only a max-width the auto-layout squeezed this
   column to a ribbon -- the same note that was three lines at full width
   became thirteen. A little more scrolling is the cheaper of the two. */
#renewTable td:nth-child(4) { min-width:240px; }

/* THE RE-SIGN CONVERSATION, on the row -- drawn like the team's notes on the
   client card (.cmt-flat): who and when over a flat yellow note, one box per
   update. 10-20-2026: the first pass put every update in plain text and two
   of them open "looks all smushed together". A box each is what keeps them
   apart.

   The note is a sentence, not a value, so it has to be allowed to break: the
   table's global `td { white-space: nowrap }` once let a note run on under
   the Mark call booked button (10-12-2026). It is held to three lines; a
   note that was cut short opens in place on a click. */
.renew-thread { display:flex; flex-direction:column; gap:10px; width:clamp(250px, 22vw, 440px); padding:2px 0; }
.renew-entry { display:flex; flex-direction:column; gap:4px; min-width:0; }
.renew-head { display:flex; align-items:center; gap:6px; min-width:0; white-space:nowrap;
  font-size:11px; color:var(--muted); }
.renew-head b { font-weight:700; color:var(--muted); letter-spacing:-.01em; }
/* The notes' avatar, without the ring: at 18px a 2px border left "CM" 14px
   to sit in and clipped its first letter. */
.renew-head .avatar.xs { width:20px; height:20px; border:0; font-size:8.5px; letter-spacing:0; }
.renew-when { color:var(--subtle); }
.renew-when::before { content:"·"; margin-right:6px; color:var(--line-strong); }
.renew-said { font-size:12.5px; line-height:1.45; color:var(--text); white-space:normal;
  padding:6px 10px; border-radius:10px; border-top-left-radius:4px;
  background:var(--tint-yellow); border:1px solid color-mix(in srgb, var(--ink-yellow) 38%, transparent);
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; overflow-wrap:anywhere; }
.renew-said.none { color:var(--muted); font-style:italic; }
.renew-said.cut { cursor:pointer; }
.renew-said.cut:hover { border-color:color-mix(in srgb, var(--ink-yellow) 70%, transparent); }
.renew-said.open { -webkit-line-clamp:unset; display:block; }
.renew-said:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
/* Always there, not on hover. A touch screen has no hover, and a control that
   only exists under a mouse is a control an iPad cannot reach -- so it is
   quiet instead of hidden: muted text, no underline, the accent on hover.
   Pushed to the right of the header so the note under it stays clear. */
.renew-acts { margin-left:auto; display:inline-flex; gap:10px; padding-left:12px; }
.renew-act { background:none; border:0; padding:1px 4px; margin:0 -4px; font:inherit; font-weight:600;
  color:var(--muted); cursor:pointer; border-radius:5px; }
.renew-act:hover { color:var(--blue); background:var(--tint-blue); }
.renew-act:focus-visible { outline:2px solid var(--blue); outline-offset:1px; }
/* The ones behind it are the same picture, a step quieter, on a rule. */
.renew-earlier { display:flex; flex-direction:column; gap:10px; padding-left:12px;
  border-left:2px solid var(--line); }
.renew-earlier[hidden] { display:none; }
.renew-entry.older .renew-said { font-size:12px; }
/* Said quietly, beside the time, like an edited note on the client card. */
.renew-edited { color:var(--subtle); font-style:italic; }
.renew-edited::before { content:"·"; margin-right:6px; color:var(--line-strong); font-style:normal; }
/* Editing an update: the box takes the note's place, full width of it. */
.renew-entry .renew-note { width:100%; max-width:none; }
/* Arrived from a tag: the row, and the update in it, lit for a moment. */
#renewTable tr.renew-landed > td { background:var(--tint-blue); transition:background .6s ease; }
#renewTable tr > td { transition:background .6s ease; }
.renew-entry.renew-landed .renew-said { box-shadow:0 0 0 2px var(--blue); }
.renew-booked { display:inline-block; font-size:12px; color:var(--ink-good); font-weight:650; }
#renewTable td .link-btn { font-size:11.5px; margin-left:6px; }

/* The re-sign brief, in a panel out to the right -- the same shape as the
   client card that opens from the same table, because Mike is reading the row
   he came from while he writes it.

   It fits on one screen. Stefan, 09-26-2026: "I don't think we need to be able
   to scroll up and down when we open up the resign call card ... ideally not
   scrolling down at all." Seven answers of a line or two each do not need a
   column of full-width boxes, so they sit two across in short ones, and the
   panel is sized to its content rather than to the window. */
.resign-panel { display:flex; flex-direction:column; }
.resign-panel .resign-body { padding:12px 16px 14px; overflow:auto; flex:1 1 auto; }
.resign-intro { margin:0 0 10px; font-size:11.5px; line-height:1.4; }
.resign-grid { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:8px 14px; }
.resign-q { display:block; min-width:0; }
.resign-q.wide { grid-column:1 / -1; }
.resign-q > b { display:block; font-size:12px; font-weight:700; line-height:1.3; }
.resign-q > small { display:block; color:var(--subtle); font-size:10.5px; line-height:1.3;
  margin:1px 0 3px; }
.resign-q textarea { width:100%; font:inherit; font-size:12px; line-height:1.4; resize:vertical;
  min-height:38px; padding:5px 7px; }
.resign-q input[type="datetime-local"] { width:100%; font:inherit; font-size:12px; height:32px; }
.resign-foot { margin-top:12px; align-items:center; gap:10px; flex-wrap:wrap; }
.resign-foot .helper { margin:0; }
.resign-read { margin-bottom:12px; }
.resign-read > b { display:block; font-size:12px; font-weight:700; }
.resign-read > p { margin:2px 0 0; font-size:12.5px; line-height:1.5; white-space:pre-wrap; }

/* ---- a page rendered for a panel on another page ----------------------
   body.embed: no shell, and on the roster the list is hidden and the client
   card fills the frame instead of floating over a backdrop. */
body.embed .sidebar, body.embed .topbar, body.embed .app-footer, body.embed .subnav { display:none !important; }
body.embed .app-shell { display:block; }
body.embed main { padding:0; }
body.embed .page { padding-top:0; max-width:none; }
/* :not(.cd) -- this rule exists so that an EMBEDDED Clients page shows only
   its note overlay and not the dashboard behind it. The client card uses
   `.viz-root cd` as its own wrapper, so the rule was blanking the whole card
   when the Call Pipeline started showing it in a frame (09-24-2026). */
body.embed .viz-root, body.embed #tilePanel { display:none !important; }
body.embed #noteOverlay { position:static; padding:0; background:none; backdrop-filter:none;
  display:block; min-height:100vh; }
body.embed #noteOverlay[hidden] { display:none; }
body.embed #noteDialog, body.embed #noteDialog.wide { width:100%; max-width:none; max-height:none;
  min-height:100vh; border:0; border-radius:0; box-shadow:none; }
body.embed .bside { display:none !important; }
/* ...and with it the ? that opens one. The guide sits BESIDE the page; a
   panel has nothing beside it, so the button would be a control that does
   nothing. The page itself is one click away at the top of the panel. */
body.embed .ws-help-btn { display:none !important; }
/* Inside the side panel the chrome carries the name and the close, so the
   card must not carry them too: the name read twice one line apart, and the
   card's own "esc" shut the card INSIDE the frame, leaving the panel open on
   an empty roster. The panel's x is the way out. 10-01-2026. */
body.embed #noteTitle .nd-name,
body.embed #noteClose { display:none !important; }

/* The panel on the Dashboard that holds the client card. */
.client-panel-backdrop { position:fixed; inset:0; z-index:64; background:rgba(4,7,12,.45); }
/* A lead card opened from a form card goes over it (.formview is 70). */
.client-panel-backdrop.over-form { z-index:75; }
.client-panel.over-form { z-index:76; }
.client-panel-backdrop[hidden] { display:none; }
.client-panel { position:fixed; top:0; right:0; bottom:0; width:min(780px, 96vw); z-index:65;
  display:flex; flex-direction:column; background:var(--panel); border-left:1px solid var(--line);
  box-shadow:var(--shadow-lg); animation:tileIn .2s cubic-bezier(.2,.8,.3,1); }
.client-panel[hidden] { display:none; }
.client-panel-head { display:flex; align-items:center; gap:10px; padding:10px 14px;
  border-bottom:1px solid var(--line); flex:0 0 auto; }
.client-panel-head b { flex:1 1 auto; font-size:14px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.client-panel iframe { flex:1 1 auto; width:100%; border:0; background:var(--panel); }

/* The team inbox in that same panel, from the header's Messages icon. Wider
   than a client card because it is three things side by side -- the rail, the
   conversation, and a thread when one is open. At 780px the inbox would fall
   to its phone layout and stack the rail above the conversation, which is the
   one arrangement a panel cannot afford. */
.msgs-panel { width:min(1120px, 96vw); }

/* A person opened FROM the Messages panel: the same card, standing in the
   middle of the window above that panel instead of replacing it, so closing
   it -- the x, Escape, or a click beside it -- leaves the inbox where it was.
   Margins rather than a transform to centre it: tileIn animates transform,
   and a transformed box slices the tooltips inside it. */
.client-panel-backdrop.centred { z-index:66; }
.client-panel.centred { z-index:67; top:28px; bottom:28px; left:0; right:0; margin:0 auto;
  width:min(780px, calc(100vw - 48px)); border:1px solid var(--line); border-radius:14px;
  overflow:hidden; animation:cardRise .18s cubic-bezier(.2,.8,.3,1); }
/* 1120, the panel's own width: any narrower and the Prospects Inbox puts the
   person's details UNDER the conversation instead of beside it. */
.client-panel.centred.msgs-panel { width:min(1120px, calc(100vw - 48px)); }
@keyframes cardRise { from { transform:translateY(12px); opacity:0; } to { transform:none; opacity:1; } }

/* The "get a buzz for new messages" line, above the queue it is about. */
/* A run marked paid, being edited on purpose. */
.pr-lockline { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:8px 12px; margin:0 0 10px; border-radius:10px;
  background:var(--lift-1); border:1px solid var(--line); font-size:12px; }
/* Commission somebody is NOT being paid, kept in view rather than removed. */
.pr-dropped-lines { margin-top:12px; padding-top:10px; border-top:1px dashed var(--line); }
.pr-dropped-lines .grid td { opacity:.75; }

/* "This was scored before the call was logged." Amber rather than red: the
   review is not wrong, it was answering a slightly different question. */
.cr-blind { display:flex; align-items:center; justify-content:space-between; gap:16px;
  flex-wrap:wrap; margin:0 0 14px; padding:12px 16px; border-radius:12px;
  border:1px solid var(--ink-warn); background:var(--tint-amber); }
.cr-blind strong { display:block; font-size:13px; color:var(--ink-warn); }
.cr-blind p { margin:3px 0 0; font-size:12.5px; color:var(--muted); line-height:1.55; }
.cr-blind .button { flex:0 0 auto; }

/* A booking row folds: the week is there when it is asked for and out of the
   way when it is not. Ten pages each showing a whole week is a page of
   scrolling to find one address. */
.bkp-row.is-open { border-color:var(--line-strong); }
.bkp-open { display:flex; align-items:center; gap:7px; min-width:0; font:inherit;
  border:0; background:transparent; padding:0; cursor:pointer; color:inherit; text-align:left; }
.bkp-open h3 { margin:0; font-size:12.5px; font-weight:700; letter-spacing:-.01em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.bkp-chev { flex:0 0 auto; color:var(--subtle); transition:transform .15s ease; }
.bkp-row.is-open .bkp-chev { transform:rotate(90deg); }
.bkp-open:hover h3 { color:var(--blue); }
/* The details line carries the badges, so it has to be able to hold one
   without growing the row. */
.oo-row-meta .badge { vertical-align:baseline; margin-right:2px; }
/* Live or Paused, and the switch for it -- one control, because a state you
   can read but not change means going somewhere else to change it. */
.bkp-state { font:inherit; font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px;
  border:1px solid transparent; cursor:pointer; margin-right:2px; }
.bkp-state.on { border-color:rgba(53,201,143,.35); color:var(--ink-good); background:rgba(53,201,143,.10); }
.bkp-state.off { border-color:rgba(251,100,114,.4); color:var(--ink-bad); background:rgba(251,100,114,.10); }
.bkp-state.on:hover { background:rgba(53,201,143,.2); }
.bkp-state.off:hover { background:rgba(251,100,114,.2); }
.bkp-state[disabled] { opacity:.5; cursor:default; }

.bkp-more { grid-column:1 / -1; }
.bkp-more[hidden] { display:none; }
.bkp-more-in { display:flex; flex-wrap:wrap; gap:18px; justify-content:space-between;
  margin-top:10px; padding-top:12px; border-top:1px solid var(--line); }
.bkp-more-h { margin:0 0 6px; font-size:10px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--subtle); }
/* How many have booked the page, on its Bookings button. */
.bkp-n { margin-left:4px; padding:0 6px; border-radius:999px; font-size:10.5px; font-weight:700;
  background:var(--lift-2); color:var(--muted); font-variant-numeric:tabular-nums; }

/* THE BOOKING LOG (booking_log.js): who booked what, in a panel over the
   page. The filters sit above the scrolling list so a search is never
   scrolled away from the rows it is narrowing. */
.bkl-head { min-width:0; }
.bkl-head h3 { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bkl-head .helper { margin:3px 0 0; }
.bkl-filters { display:flex; flex-direction:column; gap:8px; padding:12px 18px;
  border-bottom:1px solid var(--line); }
.bkl-selects { display:flex; flex-wrap:wrap; gap:8px; }
.bkl-selects select { flex:1 1 130px; width:auto; min-width:0; }
.bkl-show { align-self:flex-start; }
.bkl-show button { height:28px; padding:0 11px; }
.bkl-count { margin:0 0 10px; font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
.bkl-count b { color:var(--text); }
.bkl-rows { display:flex; flex-direction:column; gap:6px; }
.bkl-row { padding:10px 12px; border:1px solid var(--line); border-radius:10px;
  background:linear-gradient(160deg, var(--panel-2), var(--panel)); }
.bkl-row.is-off .bkl-name { color:var(--muted); }
.bkl-top { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.bkl-name { font-size:13px; font-weight:650; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.bkl-st { flex:0 0 auto; font-size:10.5px; padding:2px 8px; }
.bkl-line { margin:3px 0 0; font-size:12px; color:var(--muted); line-height:1.45;
  font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.bkl-who { color:var(--subtle); }
.bkl-k { display:inline-block; min-width:52px; margin-right:6px; font-size:10px; font-weight:800;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); }
.bkl-quiet { color:var(--subtle); }
.bkl-empty { margin:4px 0 8px; font-size:12.5px; color:var(--muted); line-height:1.55; }
.bkl-more { margin-top:12px; }
.bkl-foot { margin:12px 0 0; }

/* A heading over each kind of thing: Applications, Forms, Links. The rows
   under them are all the same shape, so the heading is what separates them. */
.ap-group { display:flex; align-items:flex-start; justify-content:space-between;
  gap:12px; flex-wrap:wrap; margin:0 0 10px; }
.ap-group h2 { margin:0; font-size:12px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--muted); }
.ap-group p { margin:3px 0 0; font-size:12px; color:var(--subtle); line-height:1.5; }
.ap-none { margin:0; font-size:12.5px; color:var(--subtle); line-height:1.6;
  padding:14px 16px; border:1px dashed var(--line); border-radius:12px; }

.ibx-push { display:flex; align-items:center; gap:10px; padding:10px 18px;
  border-bottom:1px solid var(--line); background:var(--tint-blue);
  font-size:12.5px; color:var(--text); }
.ibx-push > span:first-child { flex:1 1 auto; min-width:0; }
.ibx-push-acts { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
body.embed.ibx-page main { padding:0; }
body.embed.ibx-page .ibx-shell { height:100vh; height:100dvh; }
.tab-row .tb-tab-link { margin-left:auto; color:var(--blue); text-decoration:none; }

/* ---- a payment typed in on the Payroll page ------------------------------ */
.byhand { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.byhand input, .byhand select { width: auto; }
.byhand input[type="date"] { width: 150px; }
.byhand select { width: 110px; }
.byhand-x { font-size: 15px; line-height: 1; padding: 2px 6px; }
@media (max-width: 680px) {
  .byhand input, .byhand select, .byhand input[type="date"] { flex: 1 1 140px; width: auto; }
}

/* ---- Setter Log: today's card and the two daily reports ------------------ */
.today-card { padding:18px 20px; }
.today-row { display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:20px; align-items:start; }
.today-actions { display:flex; gap:10px; flex-wrap:wrap; align-self:center; }
.today-btn { min-height:44px; padding:0 20px; font-size:14px; font-weight:700; }
.today-status { min-width:0; }
.today-status .card-sub { margin:0; font-size:13px; }
.today-date { margin-left:auto; text-align:right; font-size:15px; font-weight:800;
  letter-spacing:-.01em; font-variant-numeric:tabular-nums; white-space:nowrap; }
.today-date-label { display:block; font-size:10px; font-weight:800; letter-spacing:.13em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:2px; }
@media (max-width:760px) {
  .today-row { grid-template-columns:1fr; }
  .today-date { margin-left:0; text-align:left; order:-1; }
}
.today-pri { margin:6px 0 0; padding-left:18px; font-size:12.5px; }
.today-pri li { margin:2px 0; }
.sod-priorities { display:grid; gap:8px; margin-top:6px; }
.eod-h { font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  color:var(--muted); margin:16px 0 8px; }
.eod-check { display:flex; align-items:center; gap:8px; font-size:12.5px; margin:6px 0; }
.eod-check input { width:auto; }
.day-card { margin-bottom:10px; padding:14px; }
.day-head { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
.day-head strong { font-size:14px; }
.day-date { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
.day-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:10px; }
@media (max-width:680px) { .day-grid { grid-template-columns:1fr; } }
/* A report inside the day: a bordered panel that reads as a thing you open.
   The whole panel is the click; "Edit" in the corner says what the click does. */
.day-report { border:1px solid var(--line); border-radius:12px; padding:10px 12px; cursor:pointer;
  background:var(--lift-1); transition:border-color .14s ease, background .14s ease; }
.day-report:hover, .day-report:focus-visible { border-color:var(--blue); background:var(--tint-blue); outline:none; }
.day-report-head { display:flex; justify-content:space-between; align-items:baseline; margin-bottom:4px;
  font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.day-report-edit { font-size:11px; font-weight:700; letter-spacing:0; text-transform:none; color:var(--blue); }
.day-line { font-size:12.5px; margin:2px 0 0; }
.day-part-h { font-size:10.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  color:var(--muted); margin-bottom:4px; }
.day-text { font-size:12px; white-space:pre-wrap; margin:4px 0 0; }

/* The Day Log (VAs): the setter's day panels, carrying a list instead of
   numbers. One panel a day; the log is a bulleted list, in the day's own
   words. Stefan, 09-19-2026. */
.dl-day { margin-top:8px; }
.dl-day:first-child { margin-top:0; }
.dl-day.dl-theirs { cursor:default; }
.dl-day.dl-theirs:hover { border-color:var(--line); background:var(--lift-1); }
.dl-day .day-report-head { text-transform:none; letter-spacing:0; font-size:12.5px; font-weight:600;
  color:var(--text); }
.dl-day .day-report-head > span:first-child b { font-weight:750; }
.dl-times { font-size:11.5px; font-weight:500; color:var(--muted); white-space:nowrap; }
.dl-times b { color:var(--text); }
.dl-list { margin:4px 0 0; padding-left:18px; font-size:12.5px; line-height:1.5; }
.dl-list li { margin:1px 0; }
.dl-note { color:var(--muted); font-style:italic; }
.dl-empty { color:var(--subtle); }
.dl-block { color:var(--amber); margin-top:4px; }
.dl-block b { color:inherit; }
.dl-opt { font-weight:500; color:var(--subtle); margin-left:4px; }
.dl-grid-2 { grid-template-columns:repeat(2, minmax(0,1fr)); }
@media (max-width:680px) { .dl-times { white-space:normal; } }


/* ---- viewing as somebody else ------------------------------------------- */
.asbar { position: sticky; top: 0; z-index: 60; display: flex; align-items: center; gap: 10px;
  padding: 9px 18px; font-size: 13px; line-height: 1.4; color: #fff;
  background: var(--violet, #7c3aed); box-shadow: 0 1px 0 rgba(0,0,0,.14); }
.asbar b { font-weight: 700; }
.asbar-ico { width: 16px; height: 16px; flex: 0 0 16px; opacity: .9; fill: none; stroke: currentColor; }
.asbar-gap { flex: 1; }
.asbar-back { flex: 0 0 auto; white-space: nowrap; color: #fff; font-weight: 650; padding: 4px 12px;
  border-radius: 999px; background: rgba(255,255,255,.17); border: 1px solid rgba(255,255,255,.3); }
.asbar-back:hover { background: rgba(255,255,255,.3); color: #fff; }
/* The sidebar is sticky at the top of the window; with a bar above it, it has
   to start below the bar or its bottom runs off the screen. */
body[data-asbar] .sidebar { top: var(--asbar, 40px); height: calc(100vh - var(--asbar, 40px)); }
@media (max-width: 680px) { .asbar { padding: 8px 12px; font-size: 12px; } .asbar-more { display: none; } }

/* ---- Conversations: one prospect, whole ---------------------------------
   A name search, then their timeline on every channel with the pre-call
   brief above it. The brief also opens in a side panel from the Call Log
   and the Calendar (brief.js), using the Brain's frame. */
.who-row { display:flex; justify-content:space-between; gap:12px; width:100%; text-align:left;
  font:inherit; padding:9px 12px; border:1px solid var(--line); border-radius:10px;
  background:var(--lift-1); color:var(--text); cursor:pointer; margin-top:6px;
  transition:border-color .14s ease, background .14s ease; }
.who-row:hover, .who-row:focus-visible { border-color:var(--blue-line); background:var(--lift-2); outline:none; }
.who-name { font-weight:650; font-size:13px; }
.who-meta { color:var(--subtle); font-size:11.5px; white-space:nowrap; }
.prospect-head { display:flex; justify-content:space-between; gap:14px; align-items:flex-start; flex-wrap:wrap; }
.prospect-head h3 { margin:0; }
.brief-card { border:1px solid var(--line); border-radius:14px; padding:14px 16px; background:var(--panel-2); }
.brief-card-head { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:6px; }
.brief-card h4, .brief-empty h4 { margin:0; font-size:13px; }
.brief-empty { display:flex; justify-content:space-between; align-items:center; gap:16px; flex-wrap:wrap; }
.brief-empty p { margin:4px 0 0; max-width:60ch; }
.brief-headline { font-size:14.5px; font-weight:650; line-height:1.45; margin:4px 0 8px; letter-spacing:-.01em; }
.brief-booking { color:var(--muted); font-size:12.5px; margin:0 0 10px; }
.brief-angle { border-left:3px solid var(--blue); padding:6px 12px; margin:0 0 12px;
  background:var(--lift-1); border-radius:0 10px 10px 0; }
.brief-angle p { margin:0; font-size:13px; line-height:1.55; }
.brief-sec { margin-top:10px; }
.brief-sec h5, .brief-angle h5 { margin:0 0 4px; font-size:10.5px; font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; color:var(--subtle); }
.brief-list { margin:0; padding-left:18px; font-size:13px; line-height:1.55; }
.brief-list li { margin:2px 0; }
.brief-quote { margin:6px 0; padding:8px 12px; border-radius:10px; background:var(--lift-2);
  font-size:12.5px; line-height:1.5; font-style:italic; }
.brief-quote span { display:block; font-style:normal; color:var(--subtle); font-size:11px; margin-top:3px; }
.brief-stamp { color:var(--subtle); font-size:11px; margin:12px 0 0; }
.brief-stale { color:var(--ink-warn, #c98a2b); }
.brief-link { font:inherit; border:0; background:none; padding:0; color:var(--blue); cursor:pointer; text-decoration:underline; }
.brief-side .brief-body { display:block; }
.brief-side .brief-empty { flex-direction:column; align-items:flex-start; }
.timeline { max-height:min(560px, 62vh); overflow-y:auto; padding:12px 14px; border:1px solid var(--line);
  border-radius:14px; background:var(--lift-1); display:flex; flex-direction:column; gap:8px; scrollbar-width:thin; }
.tl-day { align-self:center; font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  color:var(--subtle); margin:10px 0 2px; }
.tl-event { align-self:center; max-width:90%; text-align:center; font-size:11.5px; color:var(--subtle);
  padding:4px 10px; border:1px dashed var(--line); border-radius:999px; }
.tl-event span { font-weight:700; margin-right:6px; }
.tl-event em { font-style:normal; margin-left:8px; opacity:.8; }
.tl-turn { display:flex; flex-direction:column; max-width:78%; }
.tl-turn.us { align-self:flex-end; align-items:flex-end; }
.tl-turn.them { align-self:flex-start; align-items:flex-start; }
.tl-bubble { padding:9px 13px; border-radius:16px; font-size:13px; line-height:1.5; white-space:pre-wrap; overflow-wrap:anywhere; }
.tl-turn.us .tl-bubble { background:var(--blue-solid); color:var(--on-accent); border-bottom-right-radius:5px; }
.tl-turn.them .tl-bubble { background:var(--panel); border:1px solid var(--line); border-bottom-left-radius:5px; }
.tl-meta { font-size:10.5px; color:var(--subtle); margin-top:3px; padding:0 4px; }
/* Where a row is a person: the two ways into their conversation. */
.convo-links { display:inline-flex; gap:5px; flex-wrap:wrap; }
.cal-event.has-contact { cursor:pointer; }
.cal-event.has-contact:hover { border-color:var(--blue-line); }
.cal-brief { font-size:10px; color:var(--blue); font-weight:600; }
/* The pick list under a text field (GHL contact on the pitch form). */
.pick { position:relative; }
.pick-list { position:absolute; left:0; right:0; top:calc(100% + 4px); z-index:20; max-height:260px; overflow-y:auto;
  background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow-lg); padding:4px; }
.pick-list[hidden] { display:none; }
.pick-row { display:flex; justify-content:space-between; gap:12px; width:100%; text-align:left; font:inherit;
  font-size:12.5px; padding:8px 10px; border:0; border-radius:8px; background:transparent; color:var(--text); cursor:pointer; }
.pick-row:hover, .pick-row:focus-visible { background:var(--lift-2); outline:none; }
.pick-row .muted { font-size:11px; white-space:nowrap; }
.pitch-hint { margin-top:4px; font-size:11.5px; }
.pitch-hint:not(.muted) { color:var(--ink-warn, #c98a2b); }
/* The chip that should nag: Needs outcome, with its count. */
/* Confirmed: the same green the call turns on Google Calendar, so the two
   screens agree at a glance. Stefan, 09-25-2026 -- "MJ will click if the
   call is confirmed on the upcoming scheduled calls." Off it is an ordinary
   chip asking a question; on it is a statement, filled, and still a button
   because the commonest correction is a tick on the wrong row. */
.chips .chip.confirm-btn.on, .chip.confirm-btn.on {
  background: var(--tint-green); border-color: color-mix(in srgb, var(--ink-good) 45%, transparent);
  color: var(--ink-good); }
.chips .chip.confirm-btn.on:hover, .chip.confirm-btn.on:hover {
  background: color-mix(in srgb, var(--ink-good) 22%, transparent); color: var(--ink-good); }
.chip.confirm-btn[disabled] { opacity: .55; cursor: progress; }

/* "Free the time": the hour given back while the call stays on. AMBER, not
   green and not red -- it is neither a confirmation nor a cancellation, and
   the one thing a glance down the list has to say is "that hour is spoken for
   by nobody now". Stefan, 10-12-2026. */
.chips .chip.free-btn.on, .chip.free-btn.on {
  background: var(--tint-amber, color-mix(in srgb, var(--amber) 14%, transparent));
  border-color: color-mix(in srgb, var(--amber) 45%, transparent);
  color: var(--amber); }
.chips .chip.free-btn.on:hover, .chip.free-btn.on:hover {
  background: color-mix(in srgb, var(--amber) 22%, transparent); color: var(--amber); }
.chip.free-btn[disabled] { opacity: .55; cursor: progress; }

/* The onboarding tick, under the coach select it belongs to. The field
   grid's `input { width:100% }` would otherwise stretch the box across the
   column and crush its label -- the same bug the client card's own stylesheet
   already carries a note about. */
.cp-oncall { display:flex; align-items:flex-start; gap:8px; margin-top:7px;
  cursor:pointer; font-size:12.5px; line-height:1.4; color:var(--text); }
.cp-oncall input[type="checkbox"] { width:auto; min-width:0; flex:0 0 auto; margin:1px 0 0; }
.cp-oncall span { flex:1 1 auto; min-width:0; }

/* Message client: which pipe, and who is on the other end of it. */
.reach-way { display:inline-flex; align-items:center; gap:6px; }
.reach-way svg { flex:0 0 auto; }
.reach-way.active svg { color:#fff; }
.reach-to { margin:8px 0 0; font-size:12.5px; line-height:1.5; color:var(--text); }
.reach-to b { font-weight:650; color:var(--muted); margin-right:4px; }
.reach-to .helper { margin-left:8px; }

/* THE CLIENT CARD: A NOTE IS NOT A TEXT.

   Stefan, 10-17-2026, of Internal notes and Message client: "they look
   pretty similar, and I would hate to say something about a client but then
   accidentally be texting them." Both drew your own words as the blue bubble
   on the right, over the same white box.

   Now the notes are the Prospects Inbox's notes -- flat, every one under its
   author's name, on a sticky-note ground (.cmt-flat, with Convo's flat
   option) -- written in a box of the same colour, under the "Team only" bar.
   Yellow for the team's, the coaching side's green for the coaches'. And
   Message client is the one box on the card framed in the accent, with who
   it goes to written inside it. Blue on the right means it reached them. */
.cc-internal { --note-ground:var(--tint-yellow);
  --note-line:color-mix(in srgb, var(--ink-yellow) 38%, transparent);
  --note-ink:var(--ink-yellow); }
.cc-coaching { --note-ground:var(--tint-green);
  --note-line:color-mix(in srgb, var(--ink-good) 34%, transparent);
  --note-ink:var(--ink-good); }
.cc-internal .msg-box textarea { background:var(--note-ground); border-color:var(--note-line); }
/* The browser's grey placeholder sank to 2.0:1 on the dark yellow. */
.cc-internal .msg-box textarea::placeholder { color:color-mix(in srgb, var(--muted) 65%, var(--text)); }
.cc-internal .msg-box textarea:focus { border-color:var(--note-ink);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--note-ink) 16%, transparent); }
/* The note whose thread is open beside it. Flat notes drop every shadow, and
   with them the ring that said which one you are replying under. */
.cc-internal.cmt-flat .msg-turn.threaded .msg-bubble {
  box-shadow:0 0 0 2px color-mix(in srgb, var(--note-ink) 60%, transparent); }
.cc-team-only { align-items:center; margin:0 0 10px; }
.cc-outbound #reachBox { margin-top:10px; border:1.5px solid color-mix(in srgb, var(--blue) 55%, transparent);
  border-radius:14px; background:color-mix(in srgb, var(--blue) 5%, var(--panel)); }
#reachBox .reach-to { margin:0; padding:0 2px 8px;
  border-bottom:1px solid color-mix(in srgb, var(--blue) 22%, transparent); }
#reachBox .reach-to b { color:color-mix(in srgb, var(--blue) 70%, var(--text)); font-size:10.5px; font-weight:800;
  letter-spacing:.07em; text-transform:uppercase; margin-right:6px; }
#reachBox .reach-to.ink-bad { color:var(--ink-bad); }
#reachBox .reach-dest { font-weight:650; }

.chip.attn { border-color:rgba(245,174,78,.55); color:var(--ink-warn, #b7791f); }
/* Overdue is red, not amber.

   These calls happened and nobody wrote down what came of them -- Stefan,
   09-27-2026: "since they're overdue, can you put the number in a little red
   circle around it? That way, it's more of a bold notification." A tinted
   amber pill reads as a tally; the same red circle the unread counts wear
   reads as a thing to go and fix. Solid, so it carries at a glance across the
   page rather than only when you are looking straight at it. */
.chip.attn b { display:inline-grid; place-items:center; min-width:19px; height:19px;
  padding:0 6px; margin-left:5px; border-radius:999px;
  background:#e5484d; color:#fff; font-size:10.5px; font-weight:800;
  font-variant-numeric:tabular-nums; line-height:1; }
.chip.attn.active b { box-shadow:0 0 0 2px var(--panel); }
/* A count that is not a warning still deserves a shape. Stefan, 09-27-2026:
   "it doesn't need to be a red bubble ... but can you put some type of
   bubble, whether it's yellow or gray, on the upcoming? That way, it doesn't
   look like 'upcoming 2'." Grey: the same circle, saying how many rather than
   how urgent, so the red one keeps meaning something. */
#stateChips .chip b { display:inline-grid; place-items:center; min-width:19px; height:19px;
  padding:0 6px; margin-left:5px; border-radius:999px;
  background:var(--lift-2); color:var(--muted); font-size:10.5px; font-weight:750;
  font-variant-numeric:tabular-nums; line-height:1; }
#stateChips .chip.active b { background:var(--lift-3, rgba(255,255,255,.18)); color:inherit; }
/* The red one wins wherever both could apply. */
#stateChips .chip.attn b, .chip.attn b { background:#e5484d; color:#fff; font-weight:800; }
.chip.attn.active { background:rgba(245,174,78,.18); border-color:rgba(245,174,78,.8); color:var(--ink-warn, #b7791f); }
/* What happened last time: a callout under the headline, not a section among eight. */
.brief-lastcall { border:1px solid rgba(245,174,78,.55); background:rgba(245,174,78,.10); border-radius:12px;
  padding:10px 14px; margin:0 0 12px; }
.brief-lastcall h5 { margin:0 0 6px; font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
  color:var(--ink-warn, #b7791f); }
.brief-lastcall .brief-list { font-size:13px; }
.brief-lastcall .brief-list li { margin:3px 0; }
/* The calls themselves, with the notetaker's notes behind a toggle. */
.brief-calls { margin-top:16px; padding-top:12px; border-top:1px solid var(--line); }
/* Where the brief starts, so the orange box above it is unmistakably the
   call and everything under this is what we knew before it. */
/* A payment problem somebody already answered with a real payment. */
tr.is-settled td { opacity:.62; }
tr.is-settled td:last-child { opacity:1; }

/* On a call row: the tags say what the call is, the buttons do something.
   A hairline between them, so the two are never read as one strip. */
.row-actions { margin-top:8px; padding-top:8px; border-top:1px solid var(--line-strong); }

/* Recordings waiting for a name, on Call Reviews. */
.card-attention { border-color:rgba(245,174,78,.55); background:rgba(245,174,78,.06); }
.tofile { display:flex; flex-wrap:wrap; gap:10px 16px; justify-content:space-between;
          align-items:flex-start; padding:11px 0; border-top:1px solid var(--line); }
.tofile:first-child { border-top:0; }
.tofile-what { min-width:220px; display:flex; flex-direction:column; gap:2px; }
.tofile-do { display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.tofile-hits { display:flex; flex-wrap:wrap; gap:6px; flex-basis:100%; }

.brief-label { margin:16px 0 8px; font-size:11px; font-weight:800; letter-spacing:.08em;
               text-transform:uppercase; color:var(--accent, #3d7bfd); display:flex;
               align-items:center; gap:8px; }
.brief-label::after { content:""; flex:1; height:1px; background:var(--line); }
.brief-calls h5 { margin:0 0 6px; font-size:10.5px; font-weight:700; letter-spacing:.05em;
                  text-transform:uppercase; color:var(--subtle); }
.brief-nocall { margin:0 0 12px; padding:9px 12px; border-radius:10px; font-size:12.5px;
                border:1px solid var(--line); background:var(--lift-1, rgba(0,0,0,.03)); }
.brief-call { border:1px solid var(--line); border-radius:10px; padding:8px 12px; margin-top:6px; background:var(--lift-1); }
.brief-call summary { cursor:pointer; font-size:12.5px; list-style:none; display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
.brief-call summary::-webkit-details-marker { display:none; }
.brief-call summary::before { content:"▸"; color:var(--subtle); font-size:11px; }
.brief-call[open] summary::before { content:"▾"; }
.brief-call-notes { margin-top:8px; font-size:12.5px; line-height:1.55; }
.brief-call-notes h1, .brief-call-notes h2, .brief-call-notes h3 { font-size:12px; margin:10px 0 4px; }
.brief-call-notes ul { padding-left:18px; margin:4px 0; }
.brief-flash { animation:briefFlash 1.6s ease-out; }
@keyframes briefFlash { 0% { box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 55%, transparent); } 100% { box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 0%, transparent); } }
.cal-again { font-size:10px; color:var(--ink-warn, #b7791f); font-weight:600; }
/* Logging an outcome: pick (dashed), then the green tick writes it. */
.chip.pending { border-style:dashed; border-color:var(--green); color:var(--green); }
.confirm-row { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:11px; padding:9px 12px;
  border:1px dashed rgba(53,201,143,.55); border-radius:10px; background:rgba(53,201,143,.07); }
.confirm-label { font-size:12px; font-weight:650; }
.confirm-cash { display:inline-flex; gap:6px; flex-wrap:wrap; }
.button.confirm { background:var(--green); }
.button.confirm:hover { background:color-mix(in srgb, var(--green) 85%, black); box-shadow:0 3px 12px color-mix(in srgb, var(--green) 45%, transparent); }
/* ---- Prospects: heat, why, follow-up ------------------------------------ */
.heat-bar { display:flex; flex-direction:column; gap:8px; }
.heat-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.heat-stars { display:inline-flex; gap:1px; }
.heat-star { font:inherit; font-size:19px; line-height:1; border:0; background:none; padding:0 1px; cursor:pointer;
  color:var(--line-strong, #9aa3af); transition:transform .1s ease, color .1s ease; }
.heat-star.on { color:#e8a33d; }
.heat-star.sug { color:#c9ced6; }
.heat-stars:not(.ro) .heat-star:hover { transform:scale(1.18); color:#e8a33d; }
.heat-stars.ro .heat-star { cursor:default; font-size:15px; }
.heat-sug-note { max-width:52ch; }
.heat-when { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:650; color:var(--text); }
.heat-when input { width:150px; }
/* "Remind me in" -- the fast path, so it sits above the date picker. The
   whole point is that it is one press, which means no calendar, no typing and
   nothing to confirm. Stefan, 10-13-2026, on setters losing leads in the gap:
   "open up Prospect inbox card click follow up in eight hours or 12 hours or
   24 hours and it will notify the setter." */
.heat-soon { gap:5px 8px; }
/* The six intervals break as a unit, never individually: 3d and 1w used to
   drop to a line of their own and the control read as two controls. The label
   is what moves now if the card is narrow. Tighter than a normal chip because
   six of them have to sit in a panel, and "4h" needs less room than a word. */
.heat-soon-chips { display:flex; flex-wrap:nowrap; gap:4px; min-width:0; }
.heat-soon .chip { padding:4px 8px; font-size:11px; line-height:1.35;
  font-variant-numeric:tabular-nums; }
.heat-soon-lbl { font-size:12px; font-weight:650; color:var(--text); margin-right:2px;
  white-space:nowrap; }
/* What you actually bought, in the reader's own clock. Green because it is a
   thing now set, not a thing to do. */
.heat-armed { display:flex; align-items:center; gap:7px; font-size:12px; font-weight:600;
  color:var(--ink-good); background:var(--tint-green);
  border:1px solid color-mix(in srgb, var(--green) 35%, transparent);
  border-radius:9px; padding:7px 10px; }
.heat-armed .gap { flex:1; }
.heat-armed svg { flex:0 0 auto; }
.heat-armed .link-btn { color:inherit; }
.heat-note { margin-top:0; }
.heat-rate { margin-top:4px; }
/* The rating drawn on its own, above the brief. The Brain suggests the number
   in the same pass that writes the brief, so the stars that confirm it belong
   under the reasoning -- Stefan, 10-17-2026. A hairline under it keeps it
   reading as a control above the brief rather than a line of the brief. */
.heat-rate-only { gap:5px; margin-bottom:12px; padding-bottom:11px; border-bottom:1px solid var(--line); }
.heat-rate-only .heat-rate { margin-top:0; }
.heat-lbl { font-size:12px; font-weight:650; color:var(--text); white-space:nowrap; }
.heat-who { font-size:11.5px; line-height:1.4; margin-top:-2px; }
.heat-who:empty { display:none; }
.heat-exits { padding-top:6px; border-top:1px dashed var(--line); margin-top:2px; }
.heat-exits .chip { color:var(--subtle); }
.heat-exits .chip:hover { color:var(--text); }
.brief-heat { padding:12px 14px; border-bottom:1px solid var(--line); background:var(--panel-2); flex:0 0 auto; }
.ps-table td { vertical-align:top; }
.ps-table .ps-actions { white-space:nowrap; }
.ps-table tr.ps-due td:first-child { box-shadow:inset 3px 0 0 rgba(245,174,78,.8); }
.ps-due-row { display:flex; justify-content:space-between; align-items:center; gap:12px; padding:9px 0; border-top:1px solid var(--line); }
.ps-due-row:first-child { border-top:0; }
/* ---- an SOP written in the app ------------------------------------------ */
.sop-doc { max-width:78ch; padding:24px 28px; font-size:13.5px; line-height:1.62; }
.sop-doc h2 { font-size:15px; margin:26px 0 8px; letter-spacing:-.01em; display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
.sop-doc h2:first-child { margin-top:0; }
.sop-who { font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--subtle);
  border:1px solid var(--line); border-radius:999px; padding:2px 9px; }
.sop-doc ol, .sop-doc ul { padding-left:22px; margin:6px 0 10px; }
.sop-doc li { margin:4px 0; }
.sop-why { border-left:3px solid var(--blue); background:var(--lift-1); padding:8px 12px; border-radius:0 10px 10px 0; margin:8px 0 12px; }
.sop-table { margin:8px 0 14px; }
.sop-table td, .sop-table th { vertical-align:top; }
.sop-dl dt { font-weight:700; margin-top:8px; }
.sop-dl dd { margin:2px 0 0 0; color:var(--muted); }
/* ---- the stage strip, pipeline and tags ---------------------------------- */
.brief-stage { padding:10px 14px; border-bottom:1px solid var(--line); flex:0 0 auto; }
.stage-bar { display:flex; flex-direction:column; gap:8px; }
.stage-row { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.stage-strip { display:inline-flex; flex-wrap:wrap; gap:3px; }
.stage-step { font:inherit; font-size:11px; font-weight:650; padding:4px 9px; border:1px solid var(--line); border-radius:999px;
  background:transparent; color:var(--subtle); cursor:default; }
.stage-step.done { background:var(--lift-2); color:var(--muted); }
.stage-step.now { background:var(--blue-solid); border-color:var(--blue-solid); color:var(--on-accent); }
.stage-step.can { cursor:pointer; border-style:dashed; color:var(--blue); }
.stage-step.can:hover { background:color-mix(in srgb, var(--blue) 12%, transparent); }
.stage-step:disabled { opacity:.85; }
.tag-row { gap:6px; }
.tag-list { display:inline-flex; flex-wrap:wrap; gap:4px; }
.tag-chip { display:inline-flex; align-items:center; gap:4px; font-size:11px; font-weight:650; padding:3px 8px; border-radius:999px;
  background:rgba(139,92,246,.14); color:var(--ink-violet, #6d45c9); }
.tag-x { border:0; background:none; color:inherit; cursor:pointer; font-size:13px; line-height:1; padding:0 1px; opacity:.7; }
.tag-x:hover { opacity:1; }
.tag-add { width:110px; font-size:11.5px; padding:4px 8px; }
.stage-form { border:1px dashed var(--line); border-radius:10px; padding:10px 12px; background:var(--lift-1); }
/* ---- the board ------------------------------------------------------------ */
.board { display:flex; gap:12px; overflow-x:auto; padding-bottom:10px; align-items:flex-start; }
.board-col { flex:0 0 250px; background:var(--lift-1); border:1px solid var(--line); border-radius:14px; padding:10px; min-height:140px; }
.board-col.over { border-color:var(--blue); background:color-mix(in srgb, var(--blue) 8%, transparent); }
.board-col h4 { margin:0 0 8px; font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle);
  display:flex; justify-content:space-between; }
.board-col h4 b { color:var(--muted); }
.board-card { background:var(--panel); border:1px solid var(--line); border-radius:11px; padding:9px 11px; margin-bottom:8px; cursor:pointer; }
.board-card[draggable="true"] { cursor:grab; }
.board-card:hover { border-color:var(--blue-line); }
.board-card .bc-name { font-weight:700; font-size:13px; }
.board-card .bc-meta { color:var(--subtle); font-size:11px; margin-top:3px; }
.board-card .bc-tags { margin-top:5px; display:flex; flex-wrap:wrap; gap:3px; }
.board-card .bc-tags span { font-size:10px; padding:1px 6px; border-radius:999px; background:rgba(139,92,246,.14); color:var(--ink-violet, #6d45c9); }
.board-card.due { box-shadow:inset 3px 0 0 rgba(245,174,78,.9); }
.board-empty { color:var(--subtle); font-size:12px; text-align:center; padding:14px 0; }
.prospect-controls { border:1px solid var(--line); border-radius:14px; padding:12px 14px; background:var(--panel-2); }
/* ---- the workspace: list · thread · person ------------------------------- */
.ws { display:grid; grid-template-columns:300px minmax(0,1fr) 360px; gap:14px; height:calc(100vh - 150px); min-height:520px; }
.ws-list, .ws-person { border:1px solid var(--line); border-radius:14px; background:var(--panel); display:flex; flex-direction:column; min-height:0; }
.ws-list-head { display:flex; gap:6px; padding:10px; border-bottom:1px solid var(--line); }
/* The search takes the row; the window picker is a small thing beside it. */
.ws-list-head input { flex:1 1 auto; min-width:0; }
.ws-list-head #wsDays { flex:0 0 auto; width:auto; min-width:0; padding:6px 26px 6px 8px; font-size:12px; }
/* Who wrote while you were elsewhere: the toast's green, held until you act.
   A row per person and the whole row opens them; the × leaves them unread. */
.ws-wrote { flex:0 0 auto; margin:8px 10px 2px; border:1px solid rgba(53,201,143,.5); border-left-width:4px;
  border-radius:9px; background:var(--panel); box-shadow:var(--shadow); overflow:hidden; }
.ws-wrote[hidden] { display:none; }
.ws-wrote-head { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:6px 6px 4px 10px;
  font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--green); }
.ws-wrote-x { border:0; background:none; color:var(--muted); font-size:16px; line-height:1; padding:0 4px; cursor:pointer; }
.ws-wrote-x:hover { color:var(--text); }
.ws-wrote-row { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline; gap:8px; width:100%;
  padding:6px 10px; border:0; border-top:1px solid var(--line); background:none; color:var(--text);
  font:inherit; font-size:12px; text-align:left; cursor:pointer; }
.ws-wrote-row:hover { background:rgba(53,201,143,.08); }
.ws-wrote-row b { max-width:140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws-wrote-row span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--muted); }
.ws-wrote-row i { font-style:normal; font-weight:700; color:var(--green); }
.ws-wrote-more { display:block; width:100%; padding:6px 10px; border:0; border-top:1px solid var(--line); background:none;
  color:var(--muted); font:inherit; font-size:12px; text-align:left; cursor:pointer; }
.ws-wrote-more:hover { color:var(--text); }
.ws-filters { display:flex; flex-wrap:wrap; gap:4px; padding:8px 10px 4px; }
.ws-stages { display:flex; flex-direction:column; padding:4px 6px 8px; border-bottom:1px solid var(--line); }
.ws-stage { font:inherit; font-size:11.5px; text-align:left; padding:4px 8px; border:0; border-radius:8px; background:transparent; color:var(--muted); cursor:pointer;
  display:flex; justify-content:space-between; }
.ws-stage b { color:var(--subtle); font-weight:600; }
.ws-stage:hover { background:var(--lift-2); }
.ws-stage.active { background:color-mix(in srgb, var(--blue) 12%, transparent); color:var(--blue); }
.ws-people { flex:1; overflow-y:auto; min-height:0; scrollbar-width:thin; }
.ws-row { display:block; width:100%; text-align:left; font:inherit; padding:9px 12px; border:0; border-bottom:1px solid var(--line); background:transparent; color:var(--text); cursor:pointer; }
.ws-row:hover { background:var(--lift-1); }
.ws-row.active { background:color-mix(in srgb, var(--blue) 10%, transparent); box-shadow:inset 3px 0 0 var(--blue); }
.ws-row.waiting .ws-row-name::before { content:""; display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--amber, #f5ae4e); margin-right:6px; vertical-align:middle; }
.ws-row-top { display:flex; justify-content:space-between; gap:8px; align-items:center; }
.ws-row-name { font-weight:700; font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws-row-when { color:var(--subtle); font-size:11px; flex:0 0 auto; }
.ws-row-prev { color:var(--muted); font-size:12px; margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* A reply started and left (window.ooDraft): the word in the warning ink, so it
   reads as yours-to-finish rather than as their last line. */
.ws-draft { color:var(--warn); font-weight:600; }
.ws-row-meta { display:flex; align-items:center; gap:5px; margin-top:5px; flex-wrap:wrap; }
.ws-row-meta .heat-stars.ro .heat-star { font-size:12px; }
.ws-pill { font-size:10px; font-weight:700; letter-spacing:.03em; padding:2px 7px; border-radius:999px; background:var(--lift-2); color:var(--muted); }
.ws-pill.s-booked, .ws-pill.s-client { background:rgba(53,201,143,.16); color:var(--green); }
/* A past client reads as a client, quieter: they are not in coaching now. */
.ws-pill.s-past_client { background:color-mix(in srgb, var(--green) 10%, var(--lift-2)); color:var(--muted); }
.ws-pill.s-pitched { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue-on-tint); }
.ws-pill.s-held, .ws-pill.s-follow_up { background:rgba(245,174,78,.18); color:var(--ink-warn, #b7791f); }
.ws-tag { font-size:10px; padding:1px 6px; border-radius:999px; background:rgba(139,92,246,.14); color:var(--ink-violet, #6d45c9); }
/* The ad keyword they came in on: the first DM, in the ad's own capitals. */
/* The word they DM'd. A button, because clicking it is the quickest way to
   see everyone who sent the same one. */
.ws-kw { font:inherit; font-size:9.5px; font-weight:800; letter-spacing:.04em; padding:1px 6px; border-radius:5px;
  border:1px solid transparent; background:var(--tint-amber); color:var(--ink-warn); cursor:pointer; }
.ws-kw:hover { border-color:color-mix(in srgb, var(--ink-warn) 45%, transparent); }
.ws-kw.on { background:var(--ink-warn); color:var(--panel); }
.ws-kw.unmatched { background:var(--lift-2); color:var(--muted); }
.ws-kw.unmatched:hover { border-color:var(--line-strong); }
.ws-kw.unmatched.on { background:var(--subtle); color:var(--panel); }
/* The keyword rail: one line each, the count on the right, and the tail
   behind one click so fifty-seven words never push the buckets off screen. */
.ws-rail-item.kw span u { text-decoration:none; font-weight:650; letter-spacing:.02em; }
.ws-rail-group .ws-rail-item.kw.active { background:var(--tint-amber); color:var(--ink-warn); }
.ws-rail-group .ws-rail-item.kw.active b { color:var(--ink-warn); }
.ws-rail-item.kw b.zero { opacity:.45; }
.ws-rail-more { display:block; width:100%; font:inherit; font-size:11px; font-weight:650; text-align:left;
  padding:5px 8px; margin-top:2px; border:0; border-radius:8px; background:transparent; color:var(--blue); cursor:pointer; }
.ws-rail-more:hover { background:var(--lift-2); }
/* The hooks that stopped bringing anyone: still reachable, not in the way. */
.ws-kw-older { width:100%; margin-top:4px; font:inherit; font-size:11.5px; color:var(--muted);
  padding:5px 26px 5px 8px; border:1px solid var(--line); border-radius:8px; background:var(--panel);
  background-image:var(--select-arrow); background-repeat:no-repeat;
  background-position:right 8px center; appearance:none; cursor:pointer; }
.ws-kw-older:hover { border-color:var(--line-strong); color:var(--text); }
/* The keyword in the list's sub-head, with its own way out. */
.ws-sub-kw { font:inherit; font-size:10.5px; font-weight:800; letter-spacing:.04em; padding:1px 6px;
  border:0; border-radius:5px; background:var(--tint-amber); color:var(--ink-warn); cursor:pointer; }
.ws-sub-kw:hover { background:var(--ink-warn); color:var(--panel); }
.ws-thread { display:flex; flex-direction:column; min-height:0; min-width:0; }
.ws-empty { padding:22px 24px; border:1px solid var(--line); border-radius:14px; background:var(--panel); overflow-y:auto; }
.ws-empty h3 { margin:0 0 4px; }
.ws-thread-inner { display:flex; flex-direction:column; min-height:0; height:100%; border:1px solid var(--line); border-radius:14px; background:var(--panel); }
.ws-thread-head { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid var(--line); }
/* The thread wears the internal inbox's clothes (.msg-log / .msg-turn / .msg-bubble). */
.ws-timeline { flex:1; max-height:none; border:0; border-radius:0; background:var(--panel); padding:22px 18px; gap:14px; }
.ws-timeline .msg-turn { max-width:76%; }
.ws-timeline .msg-time { display:flex; align-items:center; gap:6px; }
.msg-chan { display:inline-flex; align-items:center; gap:4px; }
/* What became of an outbound message: only a failure or a read receipt says anything. */
.msg-state { margin-left:6px; color:var(--subtle); }
.msg-state.bad { color:var(--red); font-weight:700; }
/* Why it never arrived, beside "Not delivered": the words, not the shout. */
.msg-state .msg-why { font-weight:500; }
/* Retry / Remove beside a message that did not go. Small, on the stamp line,
   so it reads as part of what happened to that message. */
.msg-fix { display:inline-flex; gap:4px; margin-left:6px; }
.msg-fix-btn { font:inherit; font-size:11px; font-weight:700; padding:2px 9px; border-radius:999px; cursor:pointer;
  border:1px solid var(--red); background:var(--red); color:#fff; }
.msg-fix-btn.quiet { background:transparent; color:var(--subtle); border-color:var(--line); font-weight:600; }
.msg-fix-btn:hover:not(:disabled) { filter:brightness(1.08); }
.msg-fix-btn.quiet:hover:not(:disabled) { color:var(--text); border-color:var(--line-strong); filter:none; }
.msg-fix-btn:disabled { opacity:.5; cursor:default; }
@media (pointer: coarse) { .msg-fix-btn { font-size:12px; padding:6px 12px; } }
.msg-chan .ch-ico { --ch:11px; }
.msg-turn .msg-bubble .tl-atts { margin-top:8px; }
.msg-turn .msg-bubble.has-att { min-width:min(292px, 100%); }
.msg-turn.mine .msg-bubble .mail-subject { border-bottom-color:rgba(255,255,255,.35); }
.msg-turn.mine .msg-bubble .mail-subject .ch-ico { color:#fff; }
.msg-turn.theirs .msg-bubble .mail-subject .ch-ico { color:var(--blue); }
.msg-turn.mine .msg-bubble .tl-att-link { color:#fff; }
.ws-event span { text-transform:none; letter-spacing:0; font-weight:600; }
.ws-compose { border-top:1px solid var(--line); padding:10px 12px; }
.ws-compose textarea { width:100%; resize:none; min-height:56px; max-height:220px; line-height:1.45; }
.ws-compose-row { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-top:6px; flex-wrap:wrap; }
/* Icon and word on one line, centred on each other: an inline SVG sits on the
   text's baseline and rides above the word, and a squeezed chip put the word
   under its icon. */
.ws-compose-row .action-row .chip { display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }
.ws-compose-row .action-row .chip svg { flex:0 0 auto; }
/* A narrow conversation (the card open beside it, a small window): the chips
   go to icons, the way they do on a phone, so Send stays on the row instead
   of dropping under it. Each still says what it is on hover. */
#wsReplyPane { container-type:inline-size; container-name:wsreply; }
@container wsreply (max-width: 520px) {
  .ws-compose-row .action-row .chip .ws-mic-label { display:none; }
  .ws-compose-row .action-row .chip { padding:6px 8px; }
}
.ws-person-scroll { overflow-y:auto; padding:12px; min-height:0; scrollbar-width:thin; }
.ws-h { margin:0 0 6px; font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); }
.ws-call { border:1px solid var(--line); border-radius:11px; padding:9px 11px; margin-bottom:8px; background:var(--lift-1); font-size:12.5px; }
/* The date reads as a date. In a 300px panel a no-wrap-less flex row
   squeezed "09-28-2026 · 4:00 PM" down to its minimum content width and
   broke it a token per line -- Stefan, 09-25-2026: "the date is all mushed
   up, and we need to make this look cleaner." The row wraps instead, and the
   card link keeps the right-hand edge. */
.ws-call-top { display:flex; align-items:center; gap:5px 8px; flex-wrap:wrap; }
.ws-call-top b { white-space:nowrap; }
.ws-call-top > :last-child { margin-left:auto; }
.ws-call .confirm-row { margin-top:8px; }
@media (max-width: 1100px) { .ws { grid-template-columns:260px minmax(0,1fr); } .ws-person { grid-column:1 / -1; height:auto; } }
@media (max-width: 760px) { .ws { grid-template-columns:1fr; height:auto; } .ws-list { max-height:40vh; } }
/* ---- the inbox, four columns --------------------------------------------- */
.ws4 { position:relative; display:grid; grid-template-columns:var(--ws-rail-w, 176px) 320px minmax(0,1fr) 360px; gap:0; height:calc(100vh - 118px); min-height:560px;

  border:1px solid var(--line); border-radius:14px; background:var(--panel); overflow:hidden; }
/* 176px, not 236. Stefan, 09-26-2026: "there's just a lot going on here ...
   can we shrink this left-hand side column? I just don't want to squish or
   wrap anything." Measured rather than guessed: with this padding the
   narrowest it goes before ANY label, hint or count clips is 172px -- "Past
   client", with its three-digit count, is the row that binds. 176 leaves four
   pixels of headroom and gives the other sixty to the conversation. */
.ws-rail { border-right:1px solid var(--line); background:var(--panel-2); overflow-y:auto; padding:10px 6px; scrollbar-width:thin; }
/* Drag the rail wider or narrower. Stefan, 09-26-2026: "can we click and drag
   to expand and shrink that? I want that to be the default, but just in case
   we wanted to expand." 176 stays the default; this is the exception, kept
   per browser like the folds are.

   It sits ON the border rather than beside it, so it costs no width, and it
   is a real separator: focus it and the arrow keys move it, Home resets it,
   which is the only way to reach this without a mouse. */
.ws-rail-grip { position:absolute; top:0; bottom:0; left:var(--ws-rail-w, 176px); width:9px;
  margin-left:-5px; z-index:6;
  cursor:col-resize; background:transparent; border:0; padding:0; touch-action:none; }
.ws-rail-grip::after { content:""; position:absolute; inset:0 3px; border-radius:2px;
  background:transparent; transition:background .12s ease; }
.ws-rail-grip:hover::after, .ws-rail-grip:focus-visible::after, .ws-rail-grip.dragging::after {
  background:color-mix(in srgb, var(--blue) 55%, transparent); }
.ws-rail-grip:focus-visible { outline:none; }
body.ws-resizing { cursor:col-resize; user-select:none; }
@media (max-width: 760px) { .ws-rail-grip { display:none; } }
.ws-rail-head { display:flex; justify-content:space-between; align-items:baseline; padding:4px 6px 8px; font-size:15px; }
.ws-rail-title { font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); padding:12px 6px 4px; display:flex; justify-content:space-between; gap:6px; }
/* A hairline above each section title: Team, Stages, Buckets read as three
   groups rather than one long list. */
.ws-rail-group + .ws-rail-title { margin-top:8px; border-top:1px solid var(--line); padding-top:12px; }
.ws-rail-title .helper { font-weight:500; letter-spacing:0; text-transform:none; font-size:10px; }
/* A section that folds: the title is the control. Stages never fold -- they
   are the job -- so only Clients, Keywords and Buckets carry this. */
.ws-fold { align-items:center; width:100%; font:inherit; font-size:10px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); text-align:left; border:0; background:transparent; cursor:pointer; }
.ws-fold:hover { color:var(--text); }
.ws-fold .ws-fold-name { flex:0 0 auto; }
.ws-fold .helper { flex:1 1 auto; min-width:0; text-align:right; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws-fold-chev { flex:0 0 auto; margin-right:5px; transition:transform .14s ease; color:var(--subtle); }
.ws-fold.shut .ws-fold-chev { transform:rotate(-90deg); }
/* What the section is holding while it is shut, so nothing disappears quietly. */
.ws-fold-n { flex:0 0 auto; margin-left:6px; padding:1px 6px; border-radius:999px; font-size:10px; font-weight:800;
  letter-spacing:0; background:var(--lift-2); color:var(--muted); }
.ws-fold-n[hidden] { display:none; }
.ws-rail-group { display:flex; flex-direction:column; gap:1px; }
.ws-rail-item { display:flex; justify-content:space-between; align-items:center; gap:6px; width:100%; font:inherit; font-size:12.5px; text-align:left;
  padding:6px 6px; border:1px solid transparent; border-radius:8px; background:transparent; color:var(--muted); cursor:pointer; }
.ws-rail-item span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws-rail-item span i { font-style:normal; display:inline-block; width:16px; color:var(--subtle); font-size:10.5px; }
.ws-rail-item b { font-weight:600; color:var(--subtle); font-size:11px; font-variant-numeric:tabular-nums; }
/* Stages: number · emoji · name in three columns, so a long name wraps
   under itself rather than under the number, and the count is a pill. */
.ws-rail-item.stage { align-items:flex-start; }
.ws-rail-item.stage span { display:grid; grid-template-columns:18px minmax(0,1fr); align-items:baseline; white-space:normal; overflow:hidden; flex:1 1 auto; min-width:0; }
.ws-rail-item.stage > b { flex:0 0 auto; }
.ws-rail-item.stage span i { width:auto; font-size:12px; font-weight:800; color:var(--text); font-variant-numeric:tabular-nums; }
.ws-rail-item.stage span i.em { font-size:13px; font-weight:400; line-height:1; }
.ws-rail-item:not(.stage) span { display:flex; flex-direction:column; white-space:normal; overflow:visible; }
.ws-rail-item:not(.stage) span u { text-decoration:none; }
.ws-rail-item:not(.stage) span small { color:var(--subtle); font-size:10.5px; line-height:1.2; font-weight:500; }
/* A bucket carrying unread shows two numbers -- how many wrote, then how many
   are filed there -- and they were the same grey at the same weight, so they
   read as one meaningless pair. Stefan, 09-26-2026: "Need more differentiation
   between these numbers. Maybe a '|' Or the first number needs to be more
   bold." Both: the unread count is the full text colour and heavier, and a
   hairline separates it from the total, which stays the quiet number it is.
   Not the red pill the stages wear -- 8 unread in Spam / bots is not an alarm. */
.ws-rail-item:not(.stage) b.fresh { color:var(--text); font-weight:750; }
.ws-rail-item:not(.stage) b.fresh + b { padding-left:6px; border-left:1px solid var(--line); }
.ws-rail-item:not(.stage).active b.fresh { color:inherit; }
.ws-rail-item.stage.active span i { color:var(--blue); }
.ws-rail-item.stage span u { text-decoration:none; line-height:1.3; }
.ws-rail-item.stage span small { grid-column:1 / -1; padding-left:18px; font-size:10.5px; color:var(--subtle); font-weight:500; line-height:1.25; margin-top:-1px; }
.ws-rail-item.stage b { min-width:22px; padding:1px 5px; border-radius:999px; text-align:center; background:var(--lift-2); color:var(--text); font-size:11px; font-weight:750; }
.ws-rail-item.stage b.zero { background:transparent; color:var(--subtle); font-weight:600; min-width:0; padding:1px 2px; }
.ws-rail-item.stage b.fresh { background:var(--red); color:#fff; margin-right:-3px; white-space:nowrap; font-size:10.5px; min-width:20px; padding:1px 5px; }
.ws-rail-item.stage.active b.fresh { background:var(--red); color:#fff; }
/* A long stage name ("Closed deal / client") shortens rather than wraps
   under its counts, and the emoji keeps its column. */
.ws-rail-item.stage span u { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block; min-width:0; }
/* NEW sits beside the name, in the top line, never over the preview. */
.ws-new { flex:0 0 auto; margin-left:6px; margin-right:auto; font-size:8.5px; font-weight:800; letter-spacing:.06em; padding:2px 5px; border-radius:4px;
  background:var(--red); color:#fff; line-height:1.1; align-self:center; }
.ws-row-top .ws-row-name { flex:0 1 auto; min-width:0; }
.ws-row-top .ws-row-star { margin-right:-2px; }
.ws-row-top .ws-row-when { margin-left:auto; }
.ws-rail-item.stage.active b { background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--blue); }
.ws-rail-item:hover { background:var(--lift-2); color:var(--text); }
.ws-rail-item.active { background:color-mix(in srgb, var(--blue) 12%, transparent); color:var(--blue-on-tint); }
.ws-rail-item.active b { color:var(--blue-on-tint); }
.ws-rail-item.over { border-color:var(--blue); background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue); }
.ws-list { border-right:1px solid var(--line); display:flex; flex-direction:column; min-height:0; }
/* The team row: All, then an avatar a person, a ring on the one picked. */
/* The quick reference. */
.ws-help-btn { width:28px; height:28px; padding:0; border-radius:50%; font-weight:800; flex:0 0 auto; }
/* The thread header's ? is a PHONE-ONLY copy of the one above the list.
   Stefan, 09-30-2026: "I think we already have one on the left-hand side
   next to the 30D. That's good enough ... I don't want to use that space on
   each client card." He is right on a desk, where both are on screen at once
   and one of them is clutter. On a phone the list column is not on screen at
   all while you are in a conversation, so this is the only way to the guide
   there -- same panel either way, reachable at each width, never two at once. */
.ws-help-thread { display:none; }
.ws-help-btn.active { background:color-mix(in srgb, var(--blue) 16%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue); }
/* Until somebody has opened the guide once, the ? breathes. Then it is a ?. */
.ws-help-btn.unseen { animation:guideBreathe 2.2s ease-in-out infinite; border-color:var(--blue-line); color:var(--blue); }
@keyframes guideBreathe { 0%, 100% { box-shadow:0 0 0 0 color-mix(in srgb, var(--blue) 35%, transparent); } 50% { box-shadow:0 0 0 6px transparent; } }
@media (prefers-reduced-motion: reduce) { .ws-help-btn.unseen { animation:none; } }

/* The guide, beside the page. The same panel the Brain uses, so it sits
   where every side panel sits, and the workspace shrinks to make room rather
   than being covered. */
.ws-guide { width:min(400px, 100vw); }
@media (min-width:1100px) { body.guide-open main, body.ws-page.guide-open main { padding-right:412px; } }
.ws-guide-q { display:inline-grid; place-items:center; width:22px; height:22px; border-radius:50%; font-weight:800; font-size:13px;
  background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--blue); }
.ws-guide-find { padding:10px 14px; border-bottom:1px solid var(--line); flex:0 0 auto; }
.ws-guide-find input { width:100%; font:inherit; font-size:13px; padding:8px 12px; border:1px solid var(--line); border-radius:10px;
  background:var(--field); color:var(--text); }
.ws-guide-find input:focus { outline:none; border-color:var(--blue); }
.ws-guide-body { flex:1 1 auto; min-height:0; overflow-y:auto; padding:14px 16px 28px; scrollbar-width:thin; }
.ws-guide-intro { margin:0 0 12px; font-size:13px; line-height:1.55; color:var(--muted); }
.ws-guide-toc { display:flex; flex-wrap:wrap; gap:5px; margin:0 0 16px; }
.ws-guide-toc a { font-size:11.5px; font-weight:650; text-decoration:none; color:var(--muted); padding:4px 9px; border-radius:999px;
  border:1px solid var(--line); background:var(--lift-1); }
.ws-guide-toc a:hover { color:var(--blue); border-color:var(--blue-line); }
.ws-guide-toc[hidden] { display:none; }
.ws-guide-sec { padding:12px 0 6px; border-top:1px solid var(--line); scroll-margin-top:8px; }
.ws-guide-sec:first-of-type { border-top:0; }
.ws-guide-sec[hidden] { display:none; }
.ws-guide-sec h4 { display:flex; align-items:center; justify-content:space-between; gap:10px; margin:0 0 6px;
  font-size:12px; font-weight:800; letter-spacing:.02em; color:var(--text); }
.ws-guide-sec p, .ws-guide-sec li { margin:0 0 9px; font-size:12.5px; line-height:1.6; color:var(--muted); }
.ws-guide-sec li { margin-bottom:5px; }
.ws-guide-sec ul, .ws-guide-sec ol { margin:0 0 8px; padding-left:20px; }
.ws-guide-sec b { color:var(--text); font-weight:650; }
.ws-guide-sec i { color:var(--text); font-style:normal; font-weight:600; }
.ws-guide-show { flex:0 0 auto; font:inherit; font-size:10.5px; font-weight:700; padding:2px 8px; border-radius:999px;
  border:1px solid var(--blue-line); background:transparent; color:var(--blue); cursor:pointer; }
.ws-guide-show:hover { background:var(--tint-blue); }
.ws-guide-none { font-size:12.5px; color:var(--muted); padding:16px 0; }
/* "Show me": the thing the section is about, ringed for a moment. */
.guide-ring { animation:guideRing 2.6s ease-out; border-radius:10px; }
@keyframes guideRing { 0% { box-shadow:0 0 0 0 color-mix(in srgb, var(--blue) 70%, transparent); }
  30% { box-shadow:0 0 0 5px color-mix(in srgb, var(--blue) 55%, transparent); }
  100% { box-shadow:0 0 0 12px transparent; } }
/* On a phone the guide is the screen, over the thread, like the other sheets. */
@media (max-width:700px) { .ws-guide { width:100vw; top:0; padding-top:env(safe-area-inset-top, 0px); } .ws-guide-show { display:none; } }
/* The printable page. */
.help-doc section { scroll-margin-top:20px; }
.help-toc { display:flex; flex-wrap:wrap; gap:6px; margin:0 0 18px; }
.help-toc a { font-size:12px; font-weight:650; text-decoration:none; color:var(--muted); padding:5px 10px; border-radius:999px; border:1px solid var(--line); }
.help-toc a:hover { color:var(--blue); border-color:var(--blue-line); }
@media print { .help-toc, .topbar, .sidebar, .page-heading .action-row, .tabbar { display:none !important; } }
.ws-team { display:flex; align-items:center; gap:6px; padding:7px 12px 5px; border-bottom:1px solid var(--line); overflow-x:auto; scrollbar-width:none; }
.ws-team::-webkit-scrollbar { display:none; }
.ws-team-all { flex:0 0 auto; border:1px solid var(--line); background:transparent; color:var(--muted); font:inherit; font-size:10.5px; font-weight:700;
  padding:0 9px; height:26px; border-radius:13px; cursor:pointer; }
.ws-team-all.on { border-color:var(--blue); color:var(--blue-on-tint); background:var(--tint-blue); }
.ws-team-one { flex:0 0 auto; position:relative; border:0; background:transparent; padding:0; cursor:pointer; border-radius:50%; opacity:.88;
  transition:opacity .12s ease, transform .12s ease; }
.ws-team-one .ws-avatar { width:26px; height:26px; flex-basis:26px; font-size:10px; box-shadow:0 0 0 2px var(--panel); }
.ws-team-one:hover { opacity:1; transform:translateY(-1px); }
.ws-team-one.on { opacity:1; }
.ws-team-one.on .ws-avatar { box-shadow:0 0 0 2px var(--panel), 0 0 0 4px var(--blue); }
.ws-team-one b { position:absolute; top:-4px; right:-6px; min-width:15px; height:15px; padding:0 4px; border-radius:8px; background:var(--panel-2);
  border:1px solid var(--line); color:var(--muted); font-size:9px; font-weight:750; line-height:13px; text-align:center; font-variant-numeric:tabular-nums; }
.ws-team-one.on b { background:var(--blue); border-color:var(--blue); color:#fff; }
.ws-list-sub { padding:6px 12px; font-size:11px; color:var(--subtle); border-bottom:1px solid var(--line);
  display:flex; align-items:center; justify-content:space-between; gap:8px; }
/* All · Unread · Read, on top of whatever else is picked. */
.ws-read-seg { display:inline-flex; gap:2px; padding:2px; border-radius:999px; background:var(--lift-1); border:1px solid var(--line); }
.ws-read-seg button { border:0; background:transparent; font:inherit; font-size:10.5px; font-weight:650; color:var(--subtle);
  padding:2px 8px; border-radius:999px; cursor:pointer; }
.ws-read-seg button b { font-weight:800; }
.ws-read-seg button.on { background:var(--panel); color:var(--text); box-shadow:var(--shadow-sm); }
.ws-row.unread .ws-row-name { font-weight:800; }
.ws-row.unread .ws-row-prev { color:var(--text); }
.ws-row-unread { position:absolute; right:10px; top:10px; width:22px; height:22px; border-radius:6px; border:0;
  background:var(--panel); color:var(--subtle); display:grid; place-items:center; opacity:0; cursor:pointer;
  transition:opacity .12s ease; box-shadow:var(--shadow-sm); }
.ws-row:hover .ws-row-unread { opacity:1; }
.ws-row:hover .ws-row-when { visibility:hidden; }   /* the time yields to the button, same corner */
.ws-row-unread:hover { color:var(--blue); }
/* ON A PHONE THE TWO CANNOT SHARE A CORNER. The rule above works by hover:
   the button fades in and the time hides for as long as the pointer is on the
   row. A touch screen has no hover, so the button sits there at half opacity
   FOREVER -- printed straight over "now" and "4d", which is what Stefan was
   looking at when he said mobile felt buggy. Neither was readable.

   So on touch they stop sharing: the button keeps the corner and the time
   steps aside for it. The time is the more useful of the two at a glance --
   it says how stale the conversation is -- so it is the one that stays
   legible, and marking unread is still one tap away beside it. */
@media (hover:none) {
  .ws-row-unread { opacity:.5; }
  .ws-row-top { padding-right:28px; }
}
/* The star sits under the unread button in the same corner. A starred row
   keeps it visible at rest -- the whole point is seeing the shortlist without
   hovering -- while an unstarred one only offers it on hover, so the list is
   not a wall of empty outlines. */
/* On the name line, at the left, where the eye already is — not in the
   top-right corner, which the time and the unread button already share. It
   holds its space whether or not it is showing, so a name never shifts as a
   row is hovered. */
.ws-row-star { flex:0 0 auto; width:16px; height:16px; padding:0; border:0; background:transparent;
  color:var(--subtle); display:grid; place-items:center; opacity:0; cursor:pointer;
  transition:opacity .12s ease, color .12s ease; }
.ws-row:hover .ws-row-star { opacity:.55; }
.ws-row-star:hover { opacity:1 !important; color:var(--star-gold); }
.ws-row-star.on { opacity:1; color:var(--star-gold); }
@media (hover:none) { .ws-row-star { opacity:.4; } .ws-row-star.on { opacity:1; } }
/* On the thread header it sits with the other actions, so it is in the same
   place whatever the person is called — a long name used to push it onto a
   second line, under the label pill. */
.ws-head-star { border:1px solid var(--line); background:var(--panel); border-radius:8px;
  width:32px; height:32px; padding:0; color:var(--subtle);
  display:inline-grid; place-items:center; cursor:pointer; flex:0 0 auto;
  transition:color .12s ease, border-color .12s ease; }
.ws-head-star:hover { color:var(--star-gold); }
.ws-head-star.on { color:var(--star-gold); border-color:color-mix(in srgb, var(--star-gold) 45%, transparent); }
/* The star beside the word "Starred", worn the way the stages wear their
   emoji: same size, same place, to the LEFT of the word. A view's span is a
   column (the label over its hint), which stacked the star on top of the
   word — this one row runs across. Scoped to .ws-rail-item because
   `.ws-rail-item span i` paints every icon in the rail var(--subtle) at
   10.5px, and out-specifies a bare class. */
.ws-rail-item.viewicon span { flex-direction:row; align-items:center; gap:6px; }
.ws-rail-item .rail-star { width:auto; font-size:13px; line-height:1; font-weight:400;
  font-style:normal; color:var(--star-gold); flex:0 0 auto; }
.ws-row { display:flex; gap:10px; align-items:flex-start; padding:10px 12px; border-bottom:1px solid var(--line); cursor:pointer; position:relative; }
.ws-row[draggable="true"] { cursor:grab; }
.ws-row:hover { background:var(--lift-1); }
.ws-row.active { background:color-mix(in srgb, var(--blue) 10%, transparent); box-shadow:inset 3px 0 0 var(--blue); }
.ws-row.waiting .ws-row-name { font-weight:800; }
.ws-row-body { flex:1; min-width:0; }
.ws-row-name::before { content:none; }
/* "They spoke last": a dot on the avatar's shoulder, where nothing else
   lives -- in the corner it sat on top of the channel icon. */
.ws-dot { position:absolute; left:39px; top:8px; width:9px; height:9px; border-radius:50%; background:#e5484d; box-shadow:0 0 0 2px var(--panel); }
.ws-row.active .ws-dot { box-shadow:0 0 0 2px var(--panel); }
.ws-chan { color:var(--subtle); font-size:11px; margin-left:auto; }
.ws-avatar { --hue:212; display:inline-grid; place-items:center; width:34px; height:34px; border-radius:50%; flex:0 0 34px; font-size:12px; font-weight:800;
  /* 32% read 4.0:1 against its own tint; 26% is 5.6:1 and the same colour. */
  color:hsl(var(--hue) 55% 26%); background:hsl(var(--hue) 70% 90%); }
.ws-avatar.big { width:44px; height:44px; flex-basis:44px; font-size:15px; }
.ws-thread-head { display:flex; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid var(--line); }
.ws-thread-name { font-size:14px; display:flex; gap:8px; align-items:center; }
/* Whose thread this is must never be squeezed out by the buttons beside it.
   A contact with the full set -- Client card, Details, Mark unread, Join
   Zoom, Their form, GHL, close -- wanted 236px of a 318px header and left
   the name block at ZERO, so "Jin Heo · first wrote 09-24-2026" wrapped one
   word per line down the side of the page (Stefan, 09-24-2026). The name
   keeps a floor and the buttons drop to their own line when they cannot fit
   beside it. */
.ws-thread-head { flex-wrap:wrap; row-gap:6px; }
.ws-thread-head > .grow.min0 { min-width:150px; }
.ws-thread-head > .action-row { margin-left:auto; justify-content:flex-end; }
.ws-thread-head #wsSub { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* The client's file trash: one row above the list, so getting in and out of
   it is a single obvious click rather than another page. */
.doc-trashbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:0 0 8px; border-bottom:1px solid var(--line); margin:0 0 8px; }
.min0 { min-width:0; }
.tl-turn.them { flex-direction:row; align-items:flex-end; gap:8px; max-width:82%; }
.tl-turn.them .ws-avatar { width:26px; height:26px; flex-basis:26px; font-size:10px; }
.tl-col { display:flex; flex-direction:column; min-width:0; }
.tl-turn.them .tl-col { align-items:flex-start; }
.ws-compose-top { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px; flex-wrap:wrap; }
.ws-compose-top .ws-channels { margin-bottom:0; }
.ws-compose-tabs { display:flex; gap:4px; }
.ws-tab { font:inherit; font-size:12px; font-weight:700; padding:4px 10px; border:0; border-radius:8px; background:transparent; color:var(--subtle); cursor:pointer; }
.ws-tab.active { background:var(--lift-2); color:var(--text); }
/* The framework's next line: one strip above the reply box. Ellipsised
   until clicked, so the thread keeps its height; opens to the full text
   and, with Teach it, the box to correct it. */
.ws-next { margin-bottom:6px; padding:5px 6px 5px 10px; border:1px dashed color-mix(in srgb, var(--blue) 45%, transparent); border-radius:10px; background:color-mix(in srgb, var(--blue) 6%, transparent);
  display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.ws-next-text { font:inherit; font-size:12px; color:var(--text); background:none; border:0; padding:0; cursor:pointer; text-align:left;
  flex:1 1 0; min-width:120px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ws-next.open .ws-next-text { white-space:normal; flex-basis:100%; line-height:1.5; }
.ws-next-text b { color:var(--blue); }
.ws-next-acts { display:flex; gap:4px; flex:0 0 auto; }
.ws-next .ws-teach { flex-basis:100%; }
.ws-card { border-left:1px solid var(--line); min-height:0; display:flex; flex-direction:column; }
.ws-card-top { display:none; align-items:center; justify-content:space-between; padding:10px 14px;
  border-bottom:1px solid var(--line); background:var(--panel-2); font-size:13px; }
/* Details: a sheet on a phone, a column that folds on a desk. Lit while the
   column is out, so the chip says which state you are in. */
.ws-card-btn { display:inline-flex; align-items:center; gap:4px; }
.ws-card-btn .ws-card-chev { transition:transform .14s ease; }
.ws4.cardless .ws-card-btn .ws-card-chev { transform:rotate(180deg); }
/* Up and down only. A long GHL tag list or an email address wraps; the card
   never grows a sideways scrollbar. */
.ws-card-scroll { overflow-y:auto; overflow-x:hidden; padding:12px; scrollbar-width:thin; }
.ws-card-id { display:flex; align-items:center; gap:10px; margin-bottom:8px; }
.ws-card-id b { font-size:14px; display:block; }
/* The name is the rename: click it, type, Enter. See wireRename in prospects.js. */
.ws-card-id #wsName2 { cursor:text; border-radius:4px; margin:-1px -4px; padding:1px 4px; }
.ws-card-id #wsName2:hover { box-shadow:0 0 0 1px var(--line-strong); }
.ws-name-edit { font:inherit; font-size:14px; font-weight:700; width:100%; padding:0 3px;
  border:1px solid var(--blue); border-radius:4px; background:var(--panel); color:var(--text); }
.ws-sec { border-top:1px solid var(--line); padding:8px 0; }
/* The SECTION heading only (>). As a descendant selector this also styled
   every fold nested inside a section -- the do-not-contact line and a call's
   transcript came out in capitals with a second arrow (10-02-2026). */
.ws-sec > summary { cursor:pointer; font-size:10.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--subtle); list-style:none; padding:4px 0; }
.ws-sec > summary::-webkit-details-marker { display:none; }
.ws-sec > summary::before { content:"▸ "; }
.ws-sec[open] > summary::before { content:"▾ "; }
.ws-sec > div { padding-top:6px; }
.ws-stage-now { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.ws-read { font-size:12.5px; line-height:1.5; margin:6px 0 0; }
.ws-kv { font-size:12px; border-collapse:collapse; width:100%; min-width:0; table-layout:fixed; }   /* the global table min-width is for data tables, not a 300px card */
.ws-kv td { padding:2px 8px 2px 0; vertical-align:top; overflow-wrap:anywhere; white-space:normal; border-bottom:0; color:var(--text); }
.ws-kv td:first-child { color:var(--subtle); white-space:nowrap; width:84px; }
.ws-kv .tag-list { gap:3px; }
.tag-chip.sug { background:var(--lift-2); color:var(--muted); }
/* GoHighLevel's own tags, told apart from ours: no colour, no remove. */
.tag-chip.ghl { background:transparent; border:1px solid var(--line); color:var(--muted); font-weight:600; padding:2px 7px; }
.ws-notes { font-size:12.5px; line-height:1.5; white-space:pre-wrap; overflow-wrap:anywhere; color:var(--text); }
/* A file in a team note: a picture as a thumbnail, anything else as its name. */
.ws-note-img { display:inline-block; margin:2px 0; }
.ws-note-img img { display:block; max-width:180px; max-height:140px; border-radius:8px; border:1px solid var(--line); }
.ws-note-file { color:var(--blue); }
.ws-notes-add { margin-top:6px; }
/* The workspace page: the window is the workspace. The tab strip is a size
   down and sits close under the top bar, the side margins go, and nothing is
   left below the grid for the page to scroll to. */
body.ws-page main { padding:0 12px 0; }
body.ws-page .subnav { margin:8px 0 0; padding:3px; }
body.ws-page .subnav-tab { padding:5px 12px; font-size:12.5px; }
body.ws-page .page { max-width:none; padding-top:10px; }
body.ws-page .app-footer { display:none; }
.ws-pill.s-connect, .ws-pill.s-goal, .ws-pill.s-situation, .ws-pill.s-urgency, .ws-pill.s-money, .ws-pill.s-needs { background:color-mix(in srgb, var(--blue) 10%, transparent); color:var(--blue-on-tint); }
.ws-pill.s-soft_pitch, .ws-pill.s-invited, .ws-pill.s-book_it { background:rgba(139,92,246,.14); color:var(--ink-violet, #6d45c9); }
.ws-pill.s-quiet, .ws-pill.s-dead, .ws-pill.s-other, .ws-pill.s-dq, .ws-pill.s-vendor, .ws-pill.s-fitpro, .ws-pill.s-personal, .ws-pill.s-spam { background:var(--lift-2); color:var(--subtle); }
/* Move to… opens a strip: where, one line on why, the tick. */
.ws-move { display:flex; flex-direction:column; gap:6px; margin-top:8px; padding:8px 10px; border:1px solid var(--line); border-radius:10px; background:var(--lift-1); }
.ws-move-to { font-size:12.5px; color:var(--muted); }
.ws-help-red { color:#fff; background:var(--red); border-radius:999px; padding:0 6px; font-size:11px; }
.ws-move-to b { color:var(--text); font-weight:700; }
.ws-move input { width:100%; font:inherit; font-size:12.5px; padding:6px 8px; border:1px solid var(--line); border-radius:8px; background:var(--panel); color:var(--text); }
.ws-move input:focus { outline:none; border-color:var(--blue); }
.ws-pill.s-no_close { background:rgba(245,174,78,.18); color:var(--ink-warn, #b7791f); }
/* Details, folded away: the conversation takes the column back. The chip in
   the thread's header brings it out again, and the choice is remembered. */
:is(.ws4.cardless, .ws4.guide-fold) .ws-card { display:none; }
:is(.ws4.cardless, .ws4.guide-fold) { grid-template-columns:var(--ws-rail-w, 176px) 320px minmax(0,1fr); }
/* The grid is measured against ITS OWN room, not the window's. It sits inside
   the app shell, inside whatever the sidebar left over, and -- in the Messages
   panel -- inside an iframe as well, so the viewport never had much to do with
   how wide it actually is: the same window is a comfortable four columns on
   the page and an unreadable four columns in the panel. The container is the
   section around it, because an element cannot query itself. Only on this
   page, so nothing else inherits the containment. Stefan, 09-29-2026, on the
   third go at this: "we just need the card to be bigger." */
body.ws-page .page { container-type:inline-size; container-name:ws; }

@media (max-width: 1560px) { .ws4 { grid-template-columns:var(--ws-rail-w, 176px) 262px minmax(0,1fr) 330px; }
  :is(.ws4.cardless, .ws4.guide-fold) { grid-template-columns:var(--ws-rail-w, 176px) 262px minmax(0,1fr); } }
@media (max-width: 1280px) { .ws4 { grid-template-columns:var(--ws-rail-w, 176px) 240px minmax(0,1fr) 300px; }
  :is(.ws4.cardless, .ws4.guide-fold) { grid-template-columns:var(--ws-rail-w, 176px) 240px minmax(0,1fr); } }
/* Below here the narrow layouts have to beat the ones above, and a plain
   .ws4 does not: `:is(.ws4.cardless, .ws4.guide-fold)` carries two classes,
   so the 1280px rule outranked the phone rule no matter how small the screen
   got. With the card folded -- which is the default -- a phone kept the
   desktop's three columns and gave the conversation 190px of a 375px screen,
   which is why the reply row came out as three slivers of icon. Stefan's
   phone, 09-26-2026. Same weight, further down, so these win. */
@media (max-width: 1100px) {
  :is(.ws4, .ws4.cardless, .ws4.guide-fold) { grid-template-columns:var(--ws-rail-w, 176px) 260px minmax(0,1fr); height:auto; }

  .ws-card { grid-column:1 / -1; border-left:0; border-top:1px solid var(--line); } }
@media (max-width: 760px) {
  :is(.ws4, .ws4.cardless, .ws4.guide-fold) { grid-template-columns:1fr; }
  .ws-rail, .ws-list { border-right:0; border-bottom:1px solid var(--line); max-height:40vh; } }
.ws-row.waiting .ws-row-name::before { content:none; }
.ws-channels { display:flex; align-items:center; gap:5px; flex-wrap:wrap; margin-bottom:6px; }
.ws-channels .chip:disabled { opacity:.45; cursor:not-allowed; }
.ws-chanbar { display:grid; grid-template-columns:repeat(4, 1fr); gap:4px; padding:8px 10px 6px; border-bottom:1px solid var(--line); }
.ws-chanbtn { font:inherit; font-size:12px; font-weight:700; padding:8px 4px; border:1px solid var(--line); border-radius:10px; background:var(--lift-1);
  color:var(--muted); cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:2px; line-height:1.1; }
.ws-chanbtn b { font-size:11px; font-weight:600; color:var(--subtle); }
.ws-chanbtn:hover { background:var(--lift-2); color:var(--text); }
.ws-chanbtn.active { background:color-mix(in srgb, var(--blue) 14%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue-on-tint); }
.ws-chanbtn.active b { color:var(--blue-on-tint); }
.ws-from { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
.ws-teach textarea { width:100%; }

/* Channel glyphs (common.js chanIcon): one colour per channel, Instagram's
   magenta, a text-message green, the app's blue for email, so a row or a
   bubble reads at a glance. */
.ch-ico { --ch:14px; display:inline-flex; width:var(--ch); height:var(--ch); vertical-align:-2px; flex:0 0 auto; }
.ch-ico svg { width:100%; height:100%; fill:none; stroke:currentColor; stroke-width:2.1; stroke-linecap:round; stroke-linejoin:round; }
/* Colour lives on the glyph only -- never on a bubble's words. */
.ch-ico.ch-ig { color:#d6336c; } .ch-ico.ch-sms { color:#2f9e5e; } .ch-ico.ch-email { color:#4387f5; }
.ch-ico.ch-fb { color:#1877f2; } .ch-ico.ch-wa { color:#25a55f; } .ch-ico.ch-call { color:#8b5cf6; } .ch-ico.ch-chat { color:#f59e0b; } .ch-ico.ch-all { color:var(--muted); }
.ws-chanbtn { flex-direction:row; flex-wrap:wrap; justify-content:center; gap:4px 6px; padding:8px 6px; }
.ws-chanbtn .ch-ico { --ch:15px; }
.ws-chanbtn b { flex-basis:100%; text-align:center; }
.ws-chan { margin-left:auto; display:inline-flex; }
.ws-channels .chip { display:inline-flex; align-items:center; gap:5px; }
.tl-meta { display:flex; align-items:center; gap:5px; }
.tl-meta .ch-ico { --ch:12px; }
/* Our bubbles wear the channel: Instagram's gradient, text-message blue, and
   an email as a card with its subject line. Theirs stay plain; the line
   under each says the channel. */
.tl-turn.us .tl-bubble.ch-sms { background:var(--blue-solid); }
.tl-turn .tl-bubble.mail { background:var(--panel); color:var(--text); border:1px solid var(--line); border-radius:12px; min-width:min(280px, 100%); }
.tl-turn.us .tl-bubble.mail { border-color:color-mix(in srgb, var(--blue) 50%, transparent); box-shadow:inset 3px 0 0 color-mix(in srgb, var(--blue) 60%, transparent); border-bottom-right-radius:12px; }
.tl-turn.them .tl-bubble.mail { border-bottom-left-radius:12px; }
.mail-subject { display:flex; align-items:center; gap:6px; font-weight:700; padding-bottom:6px; margin-bottom:6px; border-bottom:1px solid var(--line); }

/* AN EMAIL IN A THREAD IS A HEADING THAT OPENS. A text is a sentence and
   belongs open; an email is a page, and six of them push the conversation off
   the screen -- Stefan, 10-12-2026, asking for GoHighLevel's shape back. */
.msg-bubble.is-mail { padding:0; overflow:hidden; }
.mail-head { display:flex; align-items:flex-start; gap:7px; width:100%; padding:9px 12px;
  border:0; background:none; color:inherit; font:inherit; font-size:12.5px;
  font-weight:650; text-align:left; cursor:pointer; }
.mail-head:hover { background:rgba(127,127,127,.12); }
/* THE WORD, not just the @. Stefan, 10-12-2026: "maybe we could put something
   saying it's an email. That way, it doesn't get blended in since it's blue,
   just like the text messages." An outbound email and an outbound text are
   the same blue, so the icon alone was doing too much work at a glance. */
.mail-tag { flex:0 0 auto; margin-top:1px; font-size:9.5px; font-weight:800; letter-spacing:.09em;
  text-transform:uppercase; padding:2px 6px; border-radius:999px;
  background:rgba(255,255,255,.22); }
/* On a bubble that is not the brand blue -- an email they sent US -- the same
   pill has to read against a pale ground. */
.msg-turn.theirs .mail-tag { background:rgba(127,127,127,.20); }
/* Up to two lines, not one. Stefan, 10-12-2026: "the email preview could be a
   little bit longer." One ellipsised line cut most of these mid-sentence, and
   the sentence IS the answer to "what was this email". */
.mail-subject-t { flex:1; min-width:0; display:-webkit-box; -webkit-line-clamp:2;
  -webkit-box-orient:vertical; overflow:hidden; line-height:1.35; }
/* Points right when shut, down when open -- the one thing on the row that
   says it does anything. */
.mail-caret { flex:0 0 auto; opacity:.6; transition:transform .14s ease; }
.is-mail.open .mail-caret { transform:rotate(90deg); }
.mail-body { padding:0 12px 10px; border-top:1px solid rgba(127,127,127,.22); padding-top:9px; }

/* Playbook: the setter-training card. Four tiles, one per entry the AI reads. */
.train-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:10px; }
.train-tile { font:inherit; text-align:left; border:1px solid var(--line); border-radius:12px; background:var(--lift-1); padding:12px 14px; cursor:pointer; color:var(--text);
  display:flex; flex-direction:column; gap:4px; transition:background .14s ease, border-color .14s ease, transform .14s ease; }
.train-tile:hover { background:var(--lift-2); border-color:var(--line-strong); transform:translateY(-1px); }
.train-role { font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--blue); }
.train-title { font-size:13px; font-weight:700; }
.train-n { font-size:11px; font-weight:650; color:var(--muted); margin-top:2px; }

/* A person found by a word inside a message, not by name. */
.ws-row.hit .ws-row-prev { color:var(--text); }
.ws-row.hit mark { background:rgba(245,158,11,.28); color:inherit; border-radius:3px; padding:0 1px; }
.ws-row.hit .ws-row-prev { white-space:normal; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }

/* Nobody open: the thread and the person fold away; the list is the page.
   Rows go wide -- name and time on the left, two lines of the last message,
   stage, heat and tags on the right -- and a slim strip of numbers sits in
   the head. Opening someone brings the four columns back. */
.ws4.nobody { grid-template-columns:var(--ws-rail-w, 176px) minmax(0,1fr); }
.ws4.nobody .ws-thread, .ws4.nobody .ws-card { display:none; }
.ws4.nobody .ws-list { border-right:0; }
.ws4.nobody .ws-list-head { flex-wrap:wrap; }
.ws4.nobody #wsSearch { max-width:none; }
.ws-strip { display:none; }
.ws4.nobody .ws-strip { display:flex; gap:6px; flex-wrap:wrap; margin-left:auto; }
.ws-stat { font:inherit; font-size:11.5px; font-weight:600; color:var(--muted); border:1px solid var(--line); border-radius:999px; background:var(--lift-1); padding:5px 10px; cursor:pointer; }
.ws-stat b { color:var(--text); font-weight:800; font-variant-numeric:tabular-nums; }
.ws-stat:hover { background:var(--lift-2); color:var(--text); }
.ws-stat.active { background:color-mix(in srgb, var(--blue) 14%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue-on-tint); }
.ws-stat.active b { color:var(--blue-on-tint); }
.ws4.nobody .ws-chanbar { grid-template-columns:repeat(4, minmax(120px, 150px)); }
.ws4.nobody .ws-row { padding:10px 16px; }
.ws4.nobody .ws-row-body { display:grid; grid-template-columns:minmax(160px, 220px) minmax(0, 1fr) auto; column-gap:16px; align-items:center; }
.ws4.nobody .ws-row-top { flex-direction:column; align-items:flex-start; gap:1px; min-width:0; }
.ws4.nobody .ws-row-name { max-width:100%; }
.ws4.nobody .ws-row-prev { margin-top:0; white-space:normal; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; line-height:1.4;
  overflow-wrap:anywhere; }   /* a pasted Instagram link is one long word; without this it ran out of the row instead of wrapping */
.ws4.nobody .ws-row-meta { margin-top:0; justify-content:flex-end; flex-wrap:nowrap; }
.ws4.nobody .ws-row-meta .ws-tag { max-width:120px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws4.nobody .ws-dot { left:43px; top:8px; }
@media (max-width: 1100px) {
  .ws4.nobody .ws-row-body { grid-template-columns:minmax(140px, 180px) minmax(0, 1fr); }
  .ws4.nobody .ws-row-meta { grid-column:1 / -1; justify-content:flex-start; flex-wrap:wrap; margin-top:4px; }
}
@media (max-width: 760px) { .ws4.nobody { grid-template-columns:1fr; } .ws4.nobody .ws-list { max-height:none; } }
.bside-ctx { font-size:11.5px; color:var(--blue); font-weight:650; margin:0 0 6px; }

/* Voice memos in the reply box: a red dot and a clock while recording, a
   player and Send once stopped. */
.ws-memo { border:1px solid var(--line); border-radius:12px; background:var(--lift-1); padding:8px 10px; margin-bottom:6px; }
.ws-memo-rec, .ws-memo-ready { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.ws-memo-rec b, .ws-memo-ready b { font-variant-numeric:tabular-nums; }
.rec-dot { width:10px; height:10px; border-radius:50%; background:#e5484d; animation:rec-blink 1s ease-in-out infinite; }
@keyframes rec-blink { 0%, 100% { opacity:1; } 50% { opacity:.25; } }
@media (prefers-reduced-motion: reduce) { .rec-dot { animation:none; } }
.ws-memo-ready audio { height:34px; max-width:min(320px, 100%); }
.ws-memo .helper:empty { display:none; }
/* What came with a message. */
.tl-atts { display:flex; flex-direction:column; gap:6px; margin-top:6px; }
.tl-bubble.has-att:empty { padding:6px; }
.tl-atts audio { width:260px; max-width:100%; height:34px; }
.tl-bubble.has-att { min-width:min(292px, 100%); }
.tl-turn .tl-col { min-width:0; flex:0 1 auto; }
.tl-atts img { max-width:min(260px, 100%); max-height:320px; border-radius:10px; display:block; }
.tl-atts video { width:260px; max-width:100%; max-height:320px; border-radius:10px; background:#000; display:block; }
.tl-atts video.audio-only { height:34px; background:transparent; border-radius:999px; }
.tl-att-link { font-size:12px; text-decoration:underline; }
.tl-turn.us .tl-att-link { color:inherit; }
.tl-turn.us .tl-bubble.has-att { padding-bottom:9px; }

/* The week inside the Call Log: quick outcomes on every booking. */
.cal-meta { display:flex; gap:4px; flex-wrap:wrap; margin-top:3px; }
.cal-acts { display:flex; gap:3px; flex-wrap:wrap; margin-top:6px; }
.cal-act { font:inherit; font-size:10.5px; font-weight:650; padding:3px 7px; border:1px solid var(--line); border-radius:999px; background:var(--panel); color:var(--muted); cursor:pointer; }
.cal-act:hover { background:var(--lift-2); color:var(--text); }
.cal-act.on { background:color-mix(in srgb, var(--blue) 14%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue); }
.cal-act.pending { background:rgba(245,174,78,.18); border-color:rgba(245,174,78,.6); color:var(--text); }
.cal-move { display:inline-flex; align-items:center; gap:5px; margin-top:7px; padding:0; border:0; background:none;
  font:inherit; font-size:11px; font-weight:650; color:var(--subtle); cursor:pointer; }
.cal-move:hover { color:var(--blue); }
.cal-mover-zone b { color:var(--text); }
.cal-mover { margin-top:8px; padding:8px; border:1px dashed var(--line); border-radius:9px; background:var(--lift-1);
  display:flex; flex-direction:column; gap:7px; font-size:11.5px; }
.cal-mover-row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.cal-mover-row input { font-size:12px; padding:4px 7px; }
.cal-mover-row .dp-wrap input { width:120px; }
.cal-slots { display:flex; flex-wrap:wrap; gap:4px; }
.cal-slot { font:inherit; font-size:11px; font-weight:650; padding:3px 8px; border:1px solid var(--line); border-radius:999px;
  background:var(--panel); color:var(--muted); cursor:pointer; }
.cal-slot:hover { color:var(--text); background:var(--lift-2); }
.cal-slot.on { background:color-mix(in srgb, var(--blue) 14%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue); }
.cal-mover-acts { display:flex; align-items:center; gap:6px; }
.cal-confirm { display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin-top:6px; font-size:11.5px; }
.cal-confirm-label { flex-basis:100%; color:var(--muted); }
/* Confirming from the calendar's hover card. Named away from .cal-confirm,
   which is the outcome-confirm row on the same card. */
.cal-confirm-row { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.cal-okbtn.on { background:var(--tint-green); color:var(--ink-good);
  border-color:color-mix(in srgb, var(--ink-good) 40%, transparent); }
/* And its opposite on the same card: amber, because the hour being open is
   neither a confirmation nor a cancellation. Matches the Call Log's chip. */
.cal-freebtn.on { background:var(--tint-amber, color-mix(in srgb, var(--amber) 14%, transparent));
  color:var(--amber); border-color:color-mix(in srgb, var(--amber) 40%, transparent); }
.cal-note { flex-basis:100%; font-size:12px; padding:5px 8px; }
.cal-note[hidden] { display:none; }
.confirm-row .note-in { flex-basis:100%; }

/* Settings: one row per integration, A to Z, opened when needed. */
.int-list { display:flex; flex-direction:column; gap:8px; }
.int { border:1px solid var(--line); border-radius:12px; background:var(--panel); box-shadow:var(--shadow-sm); }
.int[hidden] { display:none; }
.int > summary { list-style:none; display:flex; align-items:center; gap:13px; padding:13px 15px; cursor:pointer; user-select:none; }
.int > summary::-webkit-details-marker { display:none; }
.int > summary:hover { background:var(--lift-1); border-radius:12px; }
.int[open] > summary { border-bottom:1px solid var(--line); border-radius:12px 12px 0 0; }
.int-name { font-weight:700; font-size:13.5px; min-width:0; display:flex; align-items:center; gap:8px; }
.int-status { font-size:11.5px; font-weight:650; color:var(--subtle); padding:3px 9px; border-radius:999px; background:var(--lift-1); border:1px solid var(--line); white-space:nowrap; }
.int-status.ok { color:var(--ink-good, #1f9d55); background:rgba(53,201,143,.14); border-color:rgba(53,201,143,.4); }
.int-status.warn { color:var(--ink-warn, #c98a2b); background:rgba(245,174,78,.16); border-color:rgba(245,174,78,.5); }
.int-status:empty { display:none; }
.int-chev { color:var(--subtle); font-size:18px; line-height:1; transition:transform .15s ease; }
.int[open] .int-chev { transform:rotate(90deg); }
.int-body { padding:14px 16px 16px; }
.int-body > .card-sub:first-child { margin-top:0; }

/* Settings -> Alerts: who is told when something fails quietly. A list of
   people, each saying what they can reach, rather than a column of bare
   checkboxes -- the thing worth knowing before you tick somebody is whether
   the alert's link will work for them. */
/* THE CONNECTION PILL. Every per-person integration on the Account page wears
   the same one, because they used to wear three different things: Fathom said
   "Connected" in green, Google and Zoom said it in plain body text, and the
   only way to tell them apart at a glance was to read all three. Stefan,
   10-13-2026: "Zoom is connected, but it's not green. Can we make it very easy
   to see that things are connected or disconnected."

   The dot is not decoration -- it carries the state a second way, so the pill
   still reads for somebody who cannot separate the red from the green. The
   word does the same job; colour is never the only signal. */
.conn { display:inline-flex; align-items:center; gap:7px; border-radius:20px;
  padding:4px 11px 4px 9px; font-size:12px; font-weight:600; letter-spacing:.01em;
  background:var(--lift-2); color:var(--muted); vertical-align:1px; }
.conn::before { content:""; width:7px; height:7px; border-radius:50%;
  background:currentColor; flex:0 0 auto; }
.conn.on   { background:var(--tint-green); color:var(--ink-good); }
.conn.off  { background:var(--tint-red);   color:var(--ink-bad); }
.conn.warn { background:var(--tint-amber); color:var(--ink-warn); }
/* Nothing an individual can fix -- an admin has not added the app yet. Grey
   rather than red, because red says "you have a job to do" and they do not. */
.conn.none { background:var(--lift-2); color:var(--muted); }
.conn-line { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.conn-line .helper { margin:0; }

.alert-who { display:flex; flex-direction:column; gap:1px; background:var(--line);
  border:1px solid var(--line); border-radius:8px; overflow:hidden; }
/* A row IS a <label>, which globally is 11px, bold, muted and letter-spaced --
   so every one of those is undone here. Left alone, the "cannot open a client
   card" note came out louder than the person's own name, which is backwards. */
.alert-row { display:grid; grid-template-columns:auto 1fr auto; gap:3px 10px;
  align-items:center; background:var(--card); padding:9px 12px; cursor:pointer;
  margin:0; font-size:13px; font-weight:400; letter-spacing:0; color:var(--text); }
.alert-row:hover { background:var(--row-hover, var(--bg)); }
.alert-row input[type="checkbox"] { width:15px; height:15px; margin:0; flex:none;
  accent-color:var(--brand); cursor:pointer; }
.alert-name { font-weight:500; color:var(--text); }
.alert-role { font-size:10.5px; font-weight:600; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); justify-self:end; }
.alert-note:not(:empty) { grid-column:2 / -1; font-size:11.5px; font-weight:400;
  letter-spacing:0; color:var(--muted); }
@media (max-width:480px) {
  .alert-row { grid-template-columns:auto 1fr; }
  .alert-role { grid-column:2; justify-self:start; }
}
.int-h4 { margin:0 0 6px; font-size:13px; }
/* Stored secrets: the field says a key is in and how it ends; the eye only
   ever reveals what you are typing, never what is stored. */
.secret-wrap .secret-eye[hidden] { display:none; }
.secret-tail { font-size:11px; color:var(--subtle); margin-top:4px; }

/* Sidebar foot: the install link sits above Reset menu order. */
.nav-install { display:flex; align-items:center; justify-content:center; gap:7px; width:100%; margin-bottom:8px; padding:8px 10px;
  border:1px solid var(--line); border-radius:9px; background:var(--panel); color:var(--muted); font-size:11.5px; font-weight:700;
  text-decoration:none; transition:background .14s ease, color .14s ease, border-color .14s ease; }
.nav-install:hover { background:var(--lift-2); color:var(--text); border-color:var(--line-strong); }
.nav-install[hidden] { display:none; }
.sidebar.collapsed .nav-install span { display:none; }
.tb-btn.busy .tb-svg svg { animation:tbspin 1s linear infinite; }
@keyframes tbspin { to { transform:rotate(360deg); } }

/* iMessage vs SMS, said the way the phone says it. */
.svc-chip { display:inline-block; font-size:9.5px; font-weight:800; letter-spacing:.02em; padding:1px 6px; border-radius:6px; line-height:1.5; vertical-align:middle; }
.svc-chip.imessage { background:rgba(59,130,246,.18); color:#2f6fe0; }
.svc-chip.sms { background:rgba(53,201,143,.18); color:#1f9d55; }
.svc-chip.rcs { background:rgba(20,184,166,.18); color:#0f8f83; }
.ws-chan { gap:5px; align-items:center; }

/* ============================================================================
   Phones (≤700px). Not the desktop shell squeezed: the sidebar becomes a drawer
   behind a menu button, the top bar is one row with the search underneath,
   and every page is one column. The 940px rule above turns the sidebar into a
   68px icon rail, which on a 390px screen was a column of glyphs and stars
   with the page crushed beside it -- so here it is undone and replaced.
   ======================================================================== */
.nav-toggle { display:none; }
.nav-backdrop { position:fixed; inset:0; z-index:70; background:rgba(15,18,28,.45);
  backdrop-filter:blur(2px); }
@media (max-width:700px) {
  .app-shell, .app-shell.collapsed { grid-template-columns:minmax(0,1fr); }
  .sidebar, .sidebar.collapsed { position:fixed; top:var(--asbar, 0px); left:0; bottom:0; height:auto;
    width:min(300px, 86vw); padding:18px 14px; gap:22px; z-index:80; overflow-y:auto;
    transform:translateX(-105%); transition:transform .22s ease; border-right:1px solid var(--line); }
  body.nav-open .sidebar { transform:none; box-shadow:0 24px 60px rgba(0,0,0,.35); }
  /* Everything the icon rail hid comes back: this is the full menu, in a drawer. */
  .sidebar .brand-copy, .sidebar .sidebar-note { display:block; }
  .sidebar .nav-label { display:flex; }
  .sidebar .nav-item span.nav-item-label { display:inline; }
  /* Back in the row, after the label, the way the desk shows it. */
  .sidebar .nav-badge { position:static; min-width:18px; height:18px; line-height:18px; font-size:10px; padding:0 5px; margin-left:-3px; box-shadow:none; }
  .sidebar .brand { justify-content:flex-start; }
  .sidebar .collapse { display:none; }
  .sidebar .nav-item { justify-content:flex-start; padding-inline:10px; }
  .nav-toggle { display:inline-flex; padding:0; width:38px; }

  main { padding:0 12px 0; }
  .topbar { min-height:56px; flex-wrap:wrap; gap:8px; padding:6px 12px; margin:0 -12px; }
  /* One row: menu · crumb · the buttons; the search underneath. The 680px
     rule further up put the crumb and the buttons on rows of their own. */
  .crumb { flex:1 1 0; order:0; min-width:0; font-size:12.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .crumb b { font-size:13px; }
  .topbar-actions { flex:0 0 auto; order:0; gap:4px; justify-content:flex-end; }
  .topbar-actions .theme-toggle { display:none; }   /* the phone follows the system; the toggle lives on the desktop */
  .gsearch { order:3; flex-basis:100%; max-width:none; }
  .page { padding-top:14px; max-width:none; }
  .page-heading { margin-bottom:14px; }
  .page-heading h1 { font-size:24px; }
  .lede { font-size:13.5px; }
  .metric-grid, .metric-grid.flow, .module-grid { grid-template-columns:1fr; }
  .card { padding:14px; }
  .subnav { display:flex; overflow-x:auto; flex-wrap:nowrap; max-width:100%; scrollbar-width:none; }
  .subnav::-webkit-scrollbar { display:none; }
  body.ws-page main { padding:0 8px 0; }
  body.ws-page .page { padding-top:8px; }
  /* minmax(0,1fr), not 1fr: a plain 1fr track will not shrink below its
     content's minimum, and on Safari a pasted Instagram link inside a
     clamped preview counts as one unbreakable word -- so the whole list
     column grew past the phone's edge, the channel tiles went with it, and
     nothing could be scrolled into view. The track is the screen, full stop. */
  .ws4, .ws4.nobody { grid-template-columns:minmax(0,1fr); min-height:0; }
  .ws-list, .ws-rail, .ws-thread, .ws-people { min-width:0; max-width:100%; }
  .ws4.nobody .ws-row-prev { word-break:break-all; }   /* Safari: a link breaks inside a clamped box only with this */
  .ws4.nobody { height:auto !important; }
  .ws-rail { max-height:none; }
  /* The rail is a strip of chips on a phone; the list is the page. */
  .ws-rail { display:flex; gap:6px; overflow-x:auto; padding:8px; border-right:0; border-bottom:1px solid var(--line); scrollbar-width:none; }
  .ws-rail-head, .ws-rail-title { display:none; }
  .ws-rail-group { flex-direction:row; gap:4px; flex:0 0 auto; }
  .ws-rail-item { width:auto; flex:0 0 auto; padding:5px 9px; border-radius:999px; background:var(--lift-1); font-size:12px; white-space:nowrap; }
  .ws-rail-item.stage span { display:flex; gap:5px; }
  .ws-rail-item.stage span small, .ws-rail-item:not(.stage) span small { display:none; }
  .ws-list { max-height:none; }
  /* Rows read like the narrow list, not the wide one: name and time, the
     line, the pills. */
  /* Nothing in the list column may be wider than the phone: the channel
     bar's four fixed-width tiles were, and they dragged the whole column
     -- and its text -- out past the edge. */
  .ws4.nobody .ws-chanbar, .ws-chanbar { grid-template-columns:repeat(4, minmax(0,1fr)); gap:4px; padding:8px; }
  .ws-chanbar button, .ws-chanbar .ch { min-width:0; padding-inline:4px; font-size:12px; }
  .ws-list-head { flex-wrap:wrap; }
  .ws-list-head input { flex:1 1 160px; }
  .ws4.nobody #wsSearch { max-width:none; }
  .ws4.nobody .ws-strip { margin-left:0; flex-basis:100%; }
  .ws4.nobody .ws-row { padding:10px 12px; }
  .ws-row-prev { min-width:0; overflow:hidden; text-overflow:ellipsis; }
  .ws4.nobody .ws-row-body { display:block; }
  .ws4.nobody .ws-row-top { flex-direction:row; justify-content:space-between; align-items:baseline; }
  .ws4.nobody .ws-row-prev { margin-top:2px; -webkit-line-clamp:2; overflow-wrap:anywhere; word-break:break-word; font-size:13px; line-height:1.35; }
  .ws4.nobody .ws-row-body { min-width:0; overflow:hidden; }
  /* The rail strip above already says 74 waiting · 4 mine · 0 due; the same
     numbers again under the search box only pushed the list down. */
  .ws4.nobody .ws-strip { display:none; }
  .ws4.nobody .ws-row-meta { justify-content:flex-start; margin-top:5px; flex-wrap:wrap; }
  /* Open a person and the thread is the screen; the × in its header is the
     way back to the list. The card follows the thread, one scroll down. */
  .ws4:not(.nobody) .ws-rail, .ws4:not(.nobody) .ws-list, .ws4:not(.nobody) .ws-card { display:none; }
  /* The person card, as a sheet: over the thread, under the top bar, above
     the tab bar, with its own scroll and its own ×. */
  .ws-card-btn { display:inline-flex; }
  body.ws-card-open .ws4:not(.nobody) .ws-card { display:flex; position:fixed; left:0; right:0;
    top:var(--asbar, 0px); bottom:calc(58px + env(safe-area-inset-bottom, 0px)); z-index:65;
    border:0; border-top:1px solid var(--line); background:var(--panel); animation:hpopIn .16s ease; }
  body.ws-card-open .ws-card-top { display:flex; }
  body.ws-card-open .ws-card-scroll { flex:1; min-height:0; padding:12px 14px 24px; }
  .ws4:not(.nobody) { grid-template-rows:minmax(0,1fr); overflow:hidden;
    height:var(--chat-h, calc(100dvh - 190px)); min-height:0; }
  .ws-thread { min-height:0; }
  body.ws-page .app-footer { display:none; }
}

/* The phone's tab bar, and the phone's type. Fingers, not cursors: rows and
   bubbles a size up, inputs at 16px (below that iOS zooms the page when one
   is tapped), targets no smaller than 44px where they matter. */
/* What the fixed bar at the bottom of a phone actually occupies: the tallest
   tab, its padding, its border, and whatever the device reserves below it.
   Anything laid out against the bottom of the window has to subtract this or
   it ends up underneath. */
:root { --tabbar-h: 0px; }
@media (max-width:700px) { :root { --tabbar-h: calc(67px + env(safe-area-inset-bottom, 0px)); } }
.tabbar { display:none; }
@media (max-width:700px) {
  .tabbar { position:fixed; left:0; right:0; bottom:0; z-index:60; display:flex; align-items:stretch;
    background:var(--panel); border-top:1px solid var(--line);
    padding:4px 4px calc(4px + env(safe-area-inset-bottom, 0px)); box-shadow:0 -6px 24px rgba(0,0,0,.08); }
  .tabbar .tab { position:relative; flex:1 1 0; min-height:50px; display:flex; flex-direction:column; align-items:center;
    justify-content:center; gap:3px; border:0; background:transparent; border-radius:10px; color:var(--subtle);
    font:inherit; font-size:10.5px; font-weight:650; letter-spacing:.01em; text-decoration:none; cursor:pointer; }
  .tabbar .tab svg { width:22px; height:22px; }
  .tabbar .tab.on { color:var(--blue); }
  .tabbar .tab:active { background:var(--lift-2); }
  /* SCOPED TO THE BAR. Both of these were written bare, so on a phone they
     caught every .tab-badge on the page -- including the count on a subnav
     tab, which is meant to sit inline after the words. Made absolute and
     dropped at the middle of the tab, the Follow-ups badge printed straight
     over its own label: "Follow-(8)s". Found 10-14-2026 auditing at phone
     width; it was wrong on every page with a counted tab. */
  .tabbar .tab-badge { position:absolute; top:4px; left:calc(50% + 6px); min-width:17px; height:17px; padding:0 5px;
    border-radius:9px; display:grid; place-items:center; font-size:10px; font-weight:800; background:#e5484d; color:#fff; }
  /* The drawer is behind Menu on a phone: a dot says something in it carries a count. */
  .tabbar .tab-dot { position:absolute; top:7px; left:calc(50% + 9px); width:9px; height:9px; border-radius:50%;
    background:#e5484d; box-shadow:0 0 0 2px var(--panel); }
  /* Room for the bar under every page. */
  main { padding-bottom:calc(74px + env(safe-area-inset-bottom, 0px)) !important; }
  body.ws-page main { padding-bottom:calc(64px + env(safe-area-inset-bottom, 0px)) !important; }

  input, select, textarea { font-size:16px; }
  /* The date rule above is more specific than this one, so it has to be said
     again here or a tapped date box zooms the whole page on iOS. */
  input[type="date"], input[type="time"], input[type="datetime-local"],
  input[type="month"], input[type="week"] { font-size:16px; }
  .button { min-height:42px; font-size:13.5px; }
  .chip { min-height:34px; font-size:12.5px; }
  .nav-item { min-height:46px; font-size:15px; }
  .nav-label { font-size:11px; }
  /* Messaging is what the phone is for. */
  .ws-row { padding:14px 12px; gap:12px; }
  .ws-row-name { font-size:15px; }
  .ws-row-prev { font-size:13.5px; }
  .ws-row-when { font-size:12px; }
  .ws-avatar { width:40px; height:40px; flex-basis:40px; font-size:13px; }
  .ws-dot { left:44px; top:11px; }
  .ws4.nobody .ws-dot { left:44px; top:11px; }
  .msg-bubble { font-size:15px; line-height:1.45; padding:10px 14px; }
  /* A phone is narrow: 64% of it is a column of two words. */
  .msg-turn { max-width:84%; }
  .msg-time, .msg-day span { font-size:11px; }
  .ws-compose-top .ws-tab { font-size:13px; padding:6px 12px; }
  .ws-chan-btn, .ws-channels button { min-height:38px; font-size:13px; }
  .msg-box textarea { font-size:16px; }
  .ibx-card { padding:14px; }
  .ws-rail-item { min-height:36px; font-size:13px; }
  .ws-chanbar button, .ws-chanbar .ch { min-height:44px; }
  .cal-act, .cal-slot { min-height:32px; font-size:12px; }
  /* Team chat on a phone, the way the Prospects tab works: the people are
     the screen you land on, a conversation is the next screen, and the
     notification queue is a third you go to from the chips at the top. No
     stack of cards, no dead space under them. */
  /* The height is measured (common.js sets --chat-h from visualViewport);
     the old constant was a guess that left a band of dead space under the
     message box, different on every phone. Audit/Stefan, 09-22-2026. */
  .ibx-shell { height:var(--chat-h, calc(100dvh - 188px)); min-height:0; gap:10px;
    grid-template-rows:minmax(0,1fr); }
  .ibx-rail { overflow-y:auto; min-height:0; }
  /* The queue lives behind the three chips; the people are what is up. */
  body:not(.ibx-queue):not(.ibx-open) #notifPane { display:none !important; }
  body.ibx-queue .ibx-rail { display:none; }
  body.ibx-queue #notifPane, body.ibx-queue .ibx-main { min-height:0; }
  .ibx-rail-head { padding-top:10px; margin-top:2px; }
  /* Inbox and one chip per kind: a row of chips, not a column. */
  /* Wraps rather than scrolls: six chips, and one past the edge is one
     nobody knows is there. */
  .ibx-nav-row { flex-direction:row; flex-wrap:wrap; gap:6px;
    padding-bottom:2px; margin:-2px -4px 2px; padding-inline:4px; }
  .ibx-nav-row::-webkit-scrollbar { display:none; }
  .ibx-nav { width:auto; flex:0 0 auto; font-size:13px; padding:8px 12px;
    border:1px solid var(--line); border-radius:999px; background:var(--panel); }
  .ibx-nav.on { border-color:transparent; }
  /* The people are the page: they take the room that is left and scroll. */
  #threadList { flex:1 1 auto; min-height:0; overflow-y:auto; }
  .ibx-rail { gap:0; }
  /* Title and the one action on the first line, the read filter under it --
     three things on one line is how it ran off the edge. */
  .ibx-head { padding:10px 12px; gap:8px 10px; flex-wrap:wrap; }
  .ibx-head h1 { font-size:15px; flex:1 1 auto; }
  .ibx-tabs { order:3; flex:1 1 100%; margin-left:0; justify-content:space-between; }
  .ibx-tab { flex:1 1 auto; padding:7px 10px; font-size:12.5px; }
  #ibxAllRead { flex:0 0 auto; padding:7px 11px; font-size:12px; }
  body.ibx-open .ibx-rail, body.ibx-open #notifPane { display:none !important; }
  body.ibx-open .ibx-shell { grid-template-rows:minmax(0,1fr); overflow:hidden; }
  body.ibx-open .msg-main { min-height:0; display:flex; flex-direction:column; }
  body.ibx-open #msgThread { flex:1; }
  body.ibx-open .app-footer { display:none; }
  /* Typing room: the box is the width of the screen and the tools sit under
     it, so a message is written on a line, not in a stamp. */
  .msg-box { padding:10px 12px 10px; }
  .msg-box textarea { font-size:16px; min-height:46px; }
  .msg-box-row .msg-note { display:none; }
  .msg-box-row .button { margin-left:auto; padding:9px 18px; }
  .msg-tool span { display:none; }
  .msg-tool { min-width:42px; min-height:38px; font-size:16px; }
  .msg-back { display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px;
    border-radius:8px; color:var(--muted); margin-left:-6px; }
  .msg-back:active { background:var(--lift-2); }
  .msg-head { padding:10px 12px; }
  .msg-log { padding:14px 12px; }
  .msg-box { padding:10px 12px 6px; }
}
.msg-back { display:none; }
@media (max-width:700px) { .msg-back { display:inline-flex; } }
/* On a desk the queue is simply up beside everything else -- nothing to go
   back from. */
#ibxBack { display:none; }
@media (max-width:700px) { body.ibx-queue #ibxBack { display:inline-flex; } }

/* The roster as cards, phones only. */
.client-cards { display:none; }
@media (max-width:700px) {
  .client-cards { display:flex; flex-direction:column; gap:8px; }
  .client-cards + .table-wrap, #clientCards ~ .table-wrap { display:none; }
  .section:has(#clientCards) .table-wrap, .section:has(#callCards) .table-wrap { display:none; }
  .ccard-group { display:flex; align-items:center; justify-content:space-between; width:100%; margin-top:10px;
    padding:8px 4px 4px; border:0; background:transparent; font:inherit; font-size:11px; font-weight:800;
    letter-spacing:.06em; text-transform:uppercase; cursor:pointer; text-align:left; }
  .ccard-group:first-child { margin-top:0; }
  .ccard { display:flex; flex-direction:column; gap:4px; width:100%; padding:12px 14px; border:1px solid var(--line);
    border-radius:14px; background:var(--panel); color:inherit; font:inherit; text-align:left; cursor:pointer;
    box-shadow:var(--shadow-sm); }
  .ccard:active { background:var(--lift-2); }
  .ccard-top { display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
  .ccard-top strong { font-size:15.5px; letter-spacing:-.01em; }
  .ccard-renew { font-size:12.5px; font-weight:650; flex:0 0 auto; }
  .ccard-line { font-size:13px; color:var(--muted); }
  .ccard-line.muted { color:var(--subtle); font-size:12.5px; }
  .ccard-chips { display:flex; flex-wrap:wrap; gap:4px; margin-top:4px; }
  .ccard-chips:empty { display:none; }
  .ccard-chips .badge, .ccard-chips .chip-overdue, .ccard-chips .chip-monthly { margin:0; }
}

/* Phone polish. */
@media (max-width:700px) {
  /* Under the notch, in the home-screen app: the top bar and the drawer start below it. */
  .topbar { padding-top:calc(6px + env(safe-area-inset-top, 0px)); }
  .sidebar, .sidebar.collapsed { padding-top:calc(18px + env(safe-area-inset-top, 0px)); }
  /* The page name is the crumb; "Operations /" is a desk-sized nicety. */
  .crumb > span, .crumb { font-size:0; }
  .crumb b { font-size:14px; }
  /* Settings lives in the Menu and under your avatar; the top bar keeps to what you tap all day. */
  .topbar-actions a.tb-btn[href="/settings"] { display:none; }
  /* Page ledes explain the page to someone at a desk; on a phone they cost a screen. */
  .page-heading .lede, .page-heading > div > p { display:none; }
  .page-heading h1 { font-size:22px; margin-bottom:0; }
  /* The small coach table fits without a sideways scroll. */
  table:has(> #byCoach) { min-width:0; }
  /* Two-column layouts written inline (the Playbook's entry list beside its
     editor) and header rows that put a button beside a paragraph: one column,
     one thing per line. */
  .main-grid, .two-col, .split { grid-template-columns:1fr !important; }
  .action-row { flex-wrap:wrap; }
  .card > .action-row > .grow { flex:1 1 100% !important; }   /* .grow is !important itself */
  #entryList { max-height:40vh !important; }
  /* The Ads filter panel folds behind one line; open it when you need it. */
  .fold-toggle { display:flex; align-items:center; justify-content:space-between; width:100%; padding:2px 0;
    border:0; background:transparent; font:inherit; font-size:14px; font-weight:700; color:var(--text); cursor:pointer; }
  .fold-toggle::after { content:"▸"; color:var(--subtle); font-size:12px; }
  .fold-toggle[aria-expanded="true"]::after { content:"▾"; }
  .ads-filters .filter-bar { display:none; margin-top:10px; }
  .ads-filters.open .filter-bar { display:block; }
  /* While the keyboard is up. */
  body.kb-open .tabbar { display:none; }
  /* The same thing without any JavaScript: a focused message box means the
     keyboard is up. Belt and braces, because on iOS a chat with the tab bar
     still showing is a chat with a bar floating across the middle of it. */
  body.chat-open:has(.msg-box textarea:focus, .ws-compose textarea:focus) .tabbar { display:none; }
  /* The app is pinned to what can actually be seen. Without this the page
     keeps the keyboard's height as scrollable room underneath, and the bars
     fixed to "the bottom" end up behind the keyboard or adrift in the
     middle of the screen -- which is exactly what a phone showed.
     The transform also makes this the containing block for the tab bar, so
     it rides with the app instead of with the page. */
  body.chat-open { position:fixed; top:0; left:0; right:0; width:100%;
    height:var(--vvh, 100dvh); transform:translateY(var(--vvtop, 0px)); }
  body.kb-open main { padding-bottom:12px !important; }
}
.fold-toggle { display:none; }
@media (max-width:700px) { .fold-toggle { display:flex; } }

/* Zoom: the join link GHL put on the booking, wherever the booking shows. */
.chip.zoom { background:#0b5cff; border-color:#0b5cff; color:#fff; font-weight:700; }
.chip.zoom:hover { background:#0a52e6; color:#fff; }
.chip.zoom.sm { padding:3px 8px; font-size:10.5px; margin-left:6px; }
.cal-zoom { display:inline-block; margin-top:5px; padding:3px 9px; border-radius:999px; background:#0b5cff; color:#fff;
  font-size:10.5px; font-weight:700; text-decoration:none; }
.cal-zoom:hover { background:#0a52e6; }
.ws-pill { white-space:nowrap; }
.ws-thread-name { flex-wrap:wrap; row-gap:2px; }

/* Tags on the person card: ours, then the Brain's, each a single row read
   left to right -- a short label in a fixed column, the chips beside it --
   so a guess is never mistaken for a GoHighLevel tag and nothing stacks. */
.tag-row { display:flex; align-items:flex-start; gap:8px; }
.tag-row + .tag-row { margin-top:6px; }
.tag-row-label { flex:0 0 46px; display:inline-flex; align-items:center; gap:4px; min-height:24px;
  font-size:10px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); cursor:default; }
.tag-row .tag-list { flex:1; min-width:0; align-items:center; }
.tag-row .tag-add { width:74px; }
.tag-brain { width:12px; height:12px; flex:0 0 auto; }
.tag-row.brain .tag-row-label { color:var(--blue); }
/* Still used by the link groups on Pricing & Links. */
.tag-group-label { display:flex; align-items:center; gap:5px; font-size:10.5px; font-weight:800; letter-spacing:.05em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:6px; }
.tag-chip.sug { background:color-mix(in srgb, var(--blue) 8%, transparent); color:var(--blue); border:1px dashed color-mix(in srgb, var(--blue) 45%, transparent); gap:5px; }
.tag-chip.sug .tag-brain { width:11px; height:11px; }
/* The card itself: one rhythm. Sections breathe the same amount, the
   summaries sit on a hairline, and the reading text inside is a size up
   from the labels so the labels stay labels. */
.ws-sec { padding:10px 0; }
.ws-sec > summary { padding:2px 0; }
.ws-sec > div { padding-top:8px; }
.ws-read { margin:4px 0 0; }
.ws-read + .ws-read { margin-top:6px; }
.ws-stage-now { margin-bottom:2px; }

/* The one button that adds: the brand gradient, the way the Hub's Add client reads. */
/* `brand` is now what every primary button already is. Kept as a no-op so the
   sixteen call sites carrying it do not have to change, and so nobody adds it
   again expecting it to do something. */
.button.brand { background:var(--btn-grad); border-color:transparent; color:#fff;
  font-weight:800; box-shadow:0 6px 18px rgba(79,70,229,.28); }
.button.brand:hover { filter:brightness(1.06); transform:translateY(-1px); }

/* Pricing & Links ----------------------------------------------------------- */
.pr-grid { display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:16px; align-items:start; }
.pr-group { font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin:18px 0 8px; }
.pr-group:first-child { margin-top:0; }
.pr-program + .pr-program { margin-top:12px; }
.pr-table { width:100%; min-width:0; border-collapse:separate; border-spacing:0; }
.pr-table th { font-size:10.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); text-align:left;
  padding:6px 8px; border-bottom:1px solid var(--line); white-space:nowrap; }
.pr-table th.num, .pr-table td.num { text-align:right; }
.pr-table td { padding:9px 8px; border-bottom:1px solid var(--line); vertical-align:top; white-space:normal; color:var(--text); font-size:13px; }
.pr-table tbody tr:last-child td { border-bottom:0; }
.pr-len b { font-size:13px; }
.pr-sub { font-size:11px; color:var(--subtle); margin-top:2px; line-height:1.35; }
.pr-cell.empty { color:var(--subtle); }
.pr-price { font-size:15px; font-weight:800; letter-spacing:-.01em; font-variant-numeric:tabular-nums; }
.pr-price small { font-size:11px; font-weight:600; color:var(--muted); }
.pr-links { display:flex; gap:4px; margin-top:5px; }
.pr-link { width:22px; height:22px; border-radius:6px; border:1px solid var(--line); background:var(--panel); font:inherit; font-size:10.5px;
  font-weight:800; cursor:pointer; color:var(--muted); display:grid; place-items:center; }
.pr-link.stripe { color:#635bff; border-color:rgba(99,91,255,.45); }
.pr-link.wave { color:#1c7ed6; border-color:rgba(28,126,214,.45); }
.pr-link:hover { background:var(--lift-2); }
.pr-cell.edit { min-width:150px; }
.pr-edit-nums, .pr-edit-links { display:flex; flex-direction:column; gap:3px; }
.pr-edit-links { margin-top:4px; }
.pr-in { width:100%; font-size:11.5px; padding:4px 6px; }
.pr-table-edit td { padding:6px 4px; }
.pr-edit-head { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.pr-edit-head .pr-name { flex:1 1 240px; font-weight:700; }
.pr-linkgroup + .pr-linkgroup { margin-top:12px; }
.pr-linkbtn { display:flex; flex-direction:column; gap:1px; width:100%; padding:8px 10px; margin-bottom:4px; border:1px solid var(--line);
  border-radius:10px; background:var(--panel); text-align:left; cursor:pointer; font:inherit; color:inherit; }
.pr-linkbtn:hover { background:var(--lift-2); }
.pr-linkname { font-size:12.5px; font-weight:650; }
.pr-linkurl { font-size:11px; color:var(--blue); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pr-linkbtn code { font-size:12px; font-weight:700; letter-spacing:.04em; color:var(--text); }
.pr-links-edit { display:flex; flex-direction:column; gap:6px; }
.pr-link-row { display:grid; grid-template-columns:1fr 1.2fr 1.6fr 1fr 1fr auto; gap:4px; }
.pr-link-row input { font-size:11.5px; padding:4px 6px; }
.pr-deliv { width:100%; min-width:0; }
.pr-deliv th { font-size:10.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); text-align:left; padding:6px 8px; white-space:normal; }
.pr-deliv td { padding:8px; font-size:12.5px; white-space:normal; vertical-align:top; }
.pr-deliv td.muted { color:var(--subtle); }
.pr-deliv input { width:100%; font-size:11.5px; padding:4px 6px; }
@media (max-width:1100px) { .pr-grid { grid-template-columns:1fr; } }
/* The picker in the reply box: every link with a name, filter as you type. */

/* My pay, the owner's view: year rows inside the month table, and folds
   with an h3 rather than the Finance folds' h2. */
.mp-year td { background: var(--lift-1); border-top: 1px solid var(--line-strong); }
.mp-fold > summary h3 { margin: 0; font-size: 14px; }
.mp-fold > summary .helper { margin-left: 10px; }
#mpOwnerTiles .stat { flex: 1 1 200px; }
#mpOwnerTiles .stat .helper { display: block; margin-top: 4px; }
#mpOwnerMonths .pr-tag { margin-left: 6px; }

/* Tasks: the star filter is a button wearing the same star as every task,
   and a column drags by its header -- the six dots show on hover there too. */
.tk-starfilter { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line-strong);
  border-radius: 9px; padding: 8px 12px; background: var(--panel); color: var(--muted);
  font: inherit; font-size: 12px; font-weight: 750; cursor: pointer;
  transition: background .14s ease, color .14s ease, border-color .14s ease; }
.tk-starfilter svg { flex: 0 0 auto; }
.tk-starfilter:hover { background: var(--lift-2); color: var(--text); }
.tk-starfilter[aria-pressed="true"] { background: var(--tint-amber); border-color: var(--amber);
  color: var(--amber); }
.tk-starfilter[aria-pressed="true"] span { color: var(--text); }
/* Naming a new list from inside the New task dialog: one line under the
   pickers, the field wide and the two buttons beside it. */
.tkq-newlist { display: flex; align-items: center; gap: 8px; }
.tkq-newlist[hidden] { display: none; }
.tkq-newlist input { flex: 1 1 auto; }
.tkq-newlist button { flex: 0 0 auto; }
/* The No close dialog on the Call Log: the follow-up question under the note. */
.nc-fu { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--lift-1); }
.nc-ask { display: flex; align-items: center; gap: 9px; font-size: 13px; font-weight: 600; cursor: pointer; margin: 0; }
.nc-ask input { width: 16px; height: 16px; accent-color: var(--blue-solid); }
.nc-fields { margin-top: 10px; }
.nc-qual { display: flex; flex-wrap: wrap; gap: 8px; }
.nc-qual .chip.on { background: var(--blue); border-color: var(--blue); color: #fff; }
.nc-grid { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
#ncNote.tk-grow { min-height: 74px; }
/* The onboarding summary on the client card: Stefan's headings, read top to
   bottom by the coach before the onboarding call. */
/* The whole profile shows, top to bottom -- it scrolls with the pane rather
   than in a box of its own, which is what Expand used to undo. */
.onb-body { font-size: 13px; line-height: 1.55; }
/* One line on top: where it came from, and Copy for Trainerize. */
.onb-top { display: flex; align-items: center; gap: 10px; padding-bottom: 10px; margin-bottom: 10px;
  border-bottom: 1px solid var(--line); }
.onb-top .onb-meta { flex: 1 1 auto; min-width: 0; margin: 0; }
.onb-top .onb-meta.ink-bad { color: var(--ink-bad); font-weight: 600; }
.onb-acts { position: relative; display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.onb-acts [hidden] { display: none; }
.onb-acts .cf-menu { top: calc(100% + 4px); right: 0; }
.onb-body h4 { font-size: 13.5px; margin: 14px 0 4px; letter-spacing: 0; }
.onb-body h4:first-child { margin-top: 4px; }
.onb-body p { margin: 2px 0; }
.onb-body ul { margin: 2px 0 4px; padding-left: 20px; }
.onb-body li { margin: 1px 0; }
.onb-body em { color: var(--muted); font-style: normal; font-weight: 600; }
/* Read receipts, in words: under our bubble in the thread, and at the end of
   the preview on the row. Green = seen, amber = delivered and not opened. */
.msg-state.seen { color: var(--ink-good); }
.msg-state.unseen { color: var(--ink-warn); }
.ws-receipt { display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em; vertical-align: 1px; }
.ws-receipt.seen { color: var(--ink-good); background: rgba(53,201,143,.12); }
.ws-receipt.unseen { color: var(--ink-warn); background: rgba(245,174,78,.16); }
.ws-receipt.bad { color: var(--ink-bad); background: var(--tint-red); }

/* Row / Grid: the board's two layouts, side by side so the one you are in is
   always on screen. Same segmented control as the calendar's Day / Week /
   Month, sized to stand level with the filters beside it. */
.tk-views { display: inline-flex; align-items: stretch; border: 1px solid var(--line-strong);
  border-radius: 9px; overflow: hidden; background: var(--panel); }
.tk-views button { display: inline-flex; align-items: center; gap: 6px; border: 0;
  background: none; color: var(--muted); font: inherit; font-size: 12px; font-weight: 750;
  padding: 8px 11px; cursor: pointer; transition: background .14s ease, color .14s ease; }
.tk-views button + button { border-left: 1px solid var(--line-strong); }
.tk-views button:hover { background: var(--lift-2); color: var(--text); }
.tk-views button.on { background: var(--lift-3); color: var(--text); }
.tk-views svg { flex: 0 0 auto; }
.tkc-head { cursor: grab; }
.tkc-head:active { cursor: grabbing; }
.tkc-grip { flex: 0 0 10px; color: var(--subtle); fill: currentColor; opacity: 0; transition: opacity .12s ease; }
.tkc:hover .tkc-grip, .tkc.is-dragging .tkc-grip { opacity: .7; }
.tkc.is-dragging { opacity: .45; }

/* ==========================================================================
   Phones: the messaging screens. Stefan's brief (09-16-2026): "just like I'm
   messaging somebody on iPhone" -- a conversation gets the whole screen, the
   reply box is one field with an arrow, and nothing above the thread costs
   more than one line. Desk defaults first; the phone rules follow.
   ========================================================================== */
.ws-chev, .ws-send-ico, .ws-card-act, .dlg-back, .meta-crm { display:none; }
.button .ws-send-ico { stroke:currentColor; }

@media (max-width:700px) {
  /* -- top bar: the Team tab IS the inbox on a phone, so no second door up
        here; the search pill grows to a thumb's size and the Brain gets its name back. */
  .topbar-actions #inboxBtn { display:none; }
  .gsearch { height:46px; padding:0 5px 0 14px; }
  .gsearch .gs-icon { width:18px; height:18px; }
  #gsInput { font-size:16px; }
  .gs-kbd { display:none; }
  .gs-brain { height:36px; padding:0 14px 0 12px; font-size:13.5px; border-radius:0 999px 999px 0; }
  .gs-brain-label { display:inline; }
  .gs-brain-ico { width:19px; height:19px; }

  /* -- under the notch: iOS paints the status bar over whatever scrolls
        beneath it, blurred, which read as "the top of the page is blurred".
        A solid band the height of the status bar sits there instead, in the
        page's own colour -- the panel's when a conversation has the screen. */
  body::before { content:""; position:fixed; top:0; left:0; right:0; height:env(safe-area-inset-top, 0px);
    background:var(--bg); z-index:95; pointer-events:none; }
  body.ws-open::before, body:has(.brain-overlay:not([hidden]))::before { background:var(--panel); }

  /* -- Prospects Inbox, a thread open: the thread is the screen. -------- */
  body.ws-open .topbar, body.ws-open .subnav, body.ws-open .tabbar, body.ws-open .asbar { display:none; }
  body.ws-open main { padding:0 !important; }
  body.ws-open .page { padding-top:0; }
  /* The thread is a fixed layer and the page behind it cannot move at all:
     no rubber-banding the header down or the reply box up, no drifting the
     bubbles sideways. Only the log scrolls, and it stops at its own ends
     instead of handing the pull on to the page -- iMessage's feel. The
     layer's height and top are only ever set while the keyboard is up
     (fitWorkspace); otherwise inset:0 is the whole screen. */
  html:has(body.ws-open) { overflow:hidden; height:100%; overscroll-behavior:none; }
  body.ws-open { overflow:hidden; height:100%; overscroll-behavior:none; position:fixed; inset:0; width:100%; }
  body.ws-open .ws4:not(.nobody) { position:fixed; inset:0; z-index:50; border:0; border-radius:0; background:var(--panel);
    grid-template-rows:minmax(0,1fr); overflow:hidden; }
  body.ws-open .ws-thread-inner { border:0; border-radius:0; }
  body.ws-open .ws-timeline { overflow-x:hidden; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
  body.ws-open .ws-timeline > * { max-width:100%; }
  body.ws-open .ws-timeline audio { width:100%; max-width:240px; }
  body.ws-open .ws-timeline .tl-bubble { max-width:100%; box-sizing:border-box; }
  body.ws-open .ws-card-scroll { overscroll-behavior:contain; }
  /* The head: ‹ · avatar · name and stage · Zoom if there is a call · Details. */
  .ws-thread-head { padding:calc(8px + env(safe-area-inset-top, 0px)) 10px 8px 6px; gap:8px; }
  .ws-thread-head #wsBack { order:-1; width:36px; height:36px; padding:0; border:0; background:transparent;
    display:inline-flex; align-items:center; justify-content:center; color:var(--blue); }
  .ws-thread-head #wsBack .ws-x { display:none; }
  .ws-thread-head #wsBack .ws-chev { display:block; }
  .ws-thread-head .ws-avatar { width:36px; height:36px; flex-basis:36px; font-size:12.5px; }
  /* The stage pill never shrinks, so on a phone it took the room and the
     name was cut to "Rand...". Let the pill drop under the name instead:
     whose thread this is matters more than which step they are on, and both
     fit when they are not fighting over one line. */
  .ws-thread-name { font-size:16px; flex-wrap:wrap; row-gap:2px; }
  .ws-thread-name b { min-width:0; flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ws-thread-name .ws-pill { font-size:10px; padding:2px 7px; flex:0 0 auto; }
  .ws-thread-head #wsSub { font-size:11.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* The chips' wrapper dissolves so ‹ can sit at the far left and Details at
     the far right of one row: ‹ · avatar · name · Zoom · Details. */
  .ws-thread-head .action-row { display:contents; }
  .ws-thread-head #wsZoom { order:1; }
  .ws-thread-head .ws-help-thread { display:inline-grid; place-items:center; order:2;
    width:32px; height:32px; min-height:32px; padding:0; }
  .ws-thread-head #wsCardBtn { order:3; }
  /* On a phone these two move into the card's own act row -- see
     #wsCardUnread and #wsCardIg -- because the thread header has no room. */
  .ws-thread-head #wsUnread, .ws-thread-head #wsIg { display:none; }
  .ws-thread-head .chip { min-height:32px; padding:4px 10px; font-size:12px; }
  .ws-card-act { display:inline-flex; }
  .ws-card-top { gap:6px; }
  .ws-card-top b { flex:1; }
  body.ws-open.ws-card-open .ws4:not(.nobody) .ws-card { top:0; bottom:0; padding-top:env(safe-area-inset-top, 0px); z-index:65; }
  /* The log: bubbles nearly edge to edge. */
  .ws-timeline { padding:12px 10px; gap:10px; }
  .ws-timeline .tl-turn, .ws-timeline .tl-turn.them { max-width:86%; }
  .tl-bubble { font-size:15px; line-height:1.4; padding:9px 13px; }
  /* The reply box: one line of chips, then the field with its arrow. */
  .ws-compose { padding:6px 8px calc(8px + env(safe-area-inset-bottom, 0px)); }
  .ws-next { margin-bottom:4px; }
  /* One row of four equal segments -- Instagram · Text · Email · Note --
     each a thumb high, filling the width. Reply is not a button here:
     picking a channel is replying. (Zoom link, when there is a call, rides
     along at its own width.) */
  .ws-compose-top { flex-wrap:wrap; align-items:stretch; gap:6px; margin-bottom:6px; }
  .ws-compose-top .ws-channels, .ws-compose-top .ws-compose-tabs { display:contents; }
  .ws-channels .chip[data-ch], .ws-compose-top .ws-tab[data-tab="note"] { flex:1 1 0; min-width:0; min-height:40px;
    display:inline-flex; align-items:center; justify-content:center; gap:4px; padding:4px 3px; font-size:12.5px; font-weight:700;
    border:1px solid var(--line); border-radius:999px; background:transparent; color:var(--muted); }
  .ws-channels .chip[data-ch] span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ws-compose-top .ws-tab[data-tab="note"] { flex:0 0 auto; padding:4px 15px; }   /* the three channels share what Note leaves */
  .ws-channels .chip.active, .ws-compose-top .ws-tab[data-tab="note"].active { background:color-mix(in srgb, var(--blue) 14%, transparent); border-color:color-mix(in srgb, var(--blue) 55%, transparent); color:var(--blue); }
  #wsCompose.noting .ws-channels .chip.active { background:transparent; border-color:var(--line); color:var(--muted); }
  /* Anything that is not a channel takes its own line rather than a quarter
     of this one. "Brain's suggestion" sat in the same four-way split and
     squeezed Instagram, Text and Email down to three slivers of icon --
     Stefan's phone, 09-26-2026. The row is the four things you press to
     reply; these ride underneath. */
  .ws-channels .ws-brain-chip, .ws-channels #wsZoomInsert {
    flex:1 1 100%; order:9; min-height:36px; font-size:12.5px; padding:4px 10px;
    display:inline-flex; align-items:center; justify-content:center; gap:6px; }
  .ws-channels #wsChannelNote { display:none; }
  .ws-compose-top .ws-tab[data-tab="reply"] { display:none; }
  #wsReplyPane { display:flex; flex-wrap:wrap; align-items:flex-end; gap:6px; }
  #wsFromRow, #wsSubject { flex:0 0 100%; order:-2; margin:0 !important; }
  /* flex-basis:100% is 100% of the LINE, which the row's own padding then
     pushed past the screen edge -- the "To" line was clipped on the right. */
  #wsToRow { flex:0 1 100%; order:-3; margin:0 !important; gap:6px; min-height:32px;
    min-width:0; max-width:100%; box-sizing:border-box; }
  #wsToRow #wsTo { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  #wsFromRow { gap:6px; }
  /* The From address needs the whole row: .pr-mini caps a select at 170px,
     which on a phone showed "Pursuit — stefan@pursuit-h" under the chevron. */
  #wsFromRow select { flex:1 1 auto; min-width:0; width:auto; max-width:none; font-size:16px; min-height:36px; padding:6px 28px 6px 10px; }
  /* 16px, never less: iOS zooms the whole page in when a smaller field is
     focused, which is what "everything else is cut off" was. */
  #wsSubject { min-height:36px; padding:6px 12px; font-size:16px; }
  .ws-list-head #wsDays, .ws-list-head input { font-size:16px; }
  #wsMemo { flex:0 0 100%; order:-1; }
  /* The box has the whole width; the buttons ride on the line under it,
     send at the right edge. They shared one line when it was mic and send;
     by 10-16-2026 it was six buttons and the box was a sliver at the left
     that a text wrapped to three words a line in (Stefan, 10-20-2026). */
  .ws-compose textarea { flex:1 1 100%; min-width:0; width:100%; min-height:40px; max-height:160px;
    padding:9px 14px; border-radius:20px; font-size:16px; line-height:1.35; }
  #wsReplyPane .ws-compose-row { flex:1 1 100%; margin:0; gap:6px; }
  #wsComposeNote, #wsNoteHint { display:none; }
  #wsReplyPane .ws-compose-row .action-row { gap:6px; flex-wrap:nowrap; width:100%; }
  #wsReplyPane .ws-compose-row #wsSend { margin-left:auto; }
  #wsMic { width:40px; height:40px; min-height:40px; padding:0; border-radius:50%; justify-content:center; font-size:17px; }
  .ws-mic-label { display:none; }
  #wsSend, #wsNoteSave { width:40px; height:40px; min-height:40px; padding:0; border-radius:50%;
    display:inline-flex; align-items:center; justify-content:center; }
  #wsSend .ws-send-label, #wsNoteSave .ws-send-label { display:none; }
  #wsSend .ws-send-ico, #wsNoteSave .ws-send-ico { display:block; }
  #wsNoteSave { background:var(--blue-solid); border-color:transparent; color:#fff; }

  /* -- a client, open: the conversation gets the screen too. --------------
        The dialog already fills the phone; here its head shrinks to one line
        each, the tab bar steps aside, and the notes log takes what is left
        instead of a 40vh window with a paragraph and two buttons above it. */
  body:has(.brain-overlay:not([hidden])) .tabbar { display:none; }
  /* The page behind a dialog is pinned, like the page behind an open thread. */
  body:has(.brain-overlay:not([hidden])) { position:fixed; inset:0; width:100%; overflow:hidden; overscroll-behavior:none; }
  .brain-overlay { overscroll-behavior:none; }
  .note-dialog .convo .msg-log, .note-dialog .brain-body, .note-dialog .tab-pane { overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
  .note-dialog .convo .msg-log { overflow-x:hidden; }
  .note-dialog { display:flex; flex-direction:column; }
  .note-dialog .brain-head { flex-wrap:nowrap; gap:8px; padding:calc(8px + env(safe-area-inset-top, 0px)) 10px 8px 6px; }
  .note-dialog .brain-head strong { max-width:none; font-size:16px; line-height:1.3; min-width:0; }
  .note-dialog .brain-head strong .badge, .note-dialog .brain-head strong .chip { font-size:10px; padding:2px 6px; }
  .note-dialog .brain-head #noteClose { order:-1; width:36px; height:36px; padding:0; border:0; background:transparent;
    display:inline-flex; align-items:center; justify-content:center; color:var(--blue); }
  .note-dialog .brain-head #noteClose .dlg-esc { display:none; }
  .note-dialog .brain-head #noteClose .dlg-back { display:block; }
  .note-dialog .brain-body { flex:1; min-height:0; display:flex; flex-direction:column; padding:6px 10px 0 !important; overflow:hidden; }
  .note-dialog .brain-body > * { flex:0 0 auto; }
  #noteMeta { font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.5; }
  #noteMeta.open { white-space:normal; }
  #noteMeta br { display:none; }
  #noteMeta .meta-line2::before { content:" · "; }
  #noteMeta .status-pick { font-size:12px; padding:1px 6px; }
  #noteMeta .meta-crm { display:inline; color:var(--blue); font-weight:700; text-decoration:none; }
  .note-dialog .brain-head strong .photo-chip { display:none; }   /* on the Onboarding tab; the head has one line */
  .note-dialog .tab-row { margin-top:6px; padding-bottom:6px; }
  /* Only the conversation pane fills; the others scroll as they always did. */
  .note-dialog .tab-pane { overflow-y:auto; min-height:0; }
  .note-dialog .tab-pane:is([data-pane="notes"], [data-pane="cnotes"]) {
    flex:1 1 0; display:flex; flex-direction:column; overflow:hidden; }
  .note-dialog .tab-pane:is([data-pane="notes"], [data-pane="cnotes"])[hidden] { display:none; }
  .note-dialog .convo-head { display:none; }
  .note-dialog .convo-split, .note-dialog .convo-split.open { flex:1 1 0; min-height:0; display:flex; flex-direction:column; gap:0; }
  .note-dialog .convo { flex:1 1 0; min-height:0; border:0; border-radius:0; }
  .brain-panel .convo .msg-log, .note-dialog .convo .msg-log { flex:1 1 0; max-height:none; padding:12px 8px; }
  .note-dialog .convo .msg-turn { max-width:86%; }
  .note-dialog .msg-bubble { font-size:15px; }
  .note-dialog .convo-panel { position:fixed; inset:0; z-index:8; background:var(--panel); padding-top:env(safe-area-inset-top, 0px); }
  .note-dialog .msg-box { padding:6px 4px calc(8px + env(safe-area-inset-bottom, 0px)); gap:6px; align-items:flex-end; }
  .note-dialog .msg-box textarea { min-height:40px; padding:9px 14px; border-radius:20px; font-size:16px; line-height:1.35; }
  .note-dialog .msg-box #noteSave { width:40px; height:40px; min-height:40px; padding:0; border-radius:50%;
    display:inline-flex; align-items:center; justify-content:center; }
  .note-dialog .msg-box #noteSave .ws-send-label { display:none; }
  .note-dialog .msg-box #noteSave .ws-send-ico { display:block; }
  .note-dialog .cc-tool-label, .note-dialog .cc-hint { display:none; }
  .note-dialog .cc-tool { width:40px; height:40px; padding:0; border-radius:50%; justify-content:center; }
  .note-dialog #reachSend { width:40px; height:40px; min-height:40px; padding:0; border-radius:50%;
    display:inline-flex; align-items:center; justify-content:center; }
  .note-dialog #reachSend .ws-send-label { display:none; }
  .note-dialog #reachSend .ws-send-ico { display:block; }
  .note-dialog #reachNote.ink-bad { display:block; flex:1 1 100%; order:-1; }
  .note-dialog .msg-note { display:none; }
  .note-dialog .reply-to { margin:0 4px; }
}

/* The P&L's entity switch: Pursuit or the whole LLC. */
.pnl-scope button { font-size:12px; padding:6px 12px; }
.pnl-head .action-row { align-items:center; gap:10px; }

/* A Fit Pro figure the feed is sure of but nobody has approved yet. */
.pnl-prov { color: var(--ink-warn); }
.pnl-prov i { font-style: normal; font-weight: 800; margin-left: 2px; }

/* The To line: who this goes to, in one quiet line above the box -- it is a
   check, not a headline, and the box under it wants the room. */
.ws-to { margin-bottom:3px; gap:6px; }
.ws-to .helper { width:auto; font-size:10.5px; }
.ws-to .ws-to-addr { font-size:11.5px; font-weight:600; color:var(--muted); line-height:1.3; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.ws-to .ws-to-addr.muted { color:var(--subtle); font-weight:500; }
.ws-to .ws-to-addr.ws-to-copy { cursor:pointer; }
.ws-to .ws-to-addr.ws-to-copy:hover { color:var(--blue); text-decoration:underline dotted; text-underline-offset:3px; }
.ws-from .helper { flex:0 0 auto; width:34px; }
@media (max-width:700px) { .ws-to .ws-to-addr { font-size:14px; } }

/* Attachments in a chat: the drop highlight, the chips waiting to go, and
   how a picture or a PDF sits inside a bubble. */
.att-over { outline:2px dashed var(--blue); outline-offset:-4px; background:var(--tint-blue) !important; }
.att-chips { display:flex; flex-wrap:wrap; gap:6px; padding:6px 0 4px; }
.att-chip { display:inline-flex; align-items:center; gap:6px; max-width:220px; padding:4px 6px 4px 4px; border:1px solid var(--line);
  border-radius:10px; background:var(--lift-1); font-size:12px; }
.att-chip img { width:36px; height:36px; object-fit:cover; border-radius:6px; }
.att-chip i { font-style:normal; font-size:15px; padding:0 3px; }
.att-chip b { font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.att-chip button { border:0; background:transparent; color:var(--subtle); font-size:15px; line-height:1; padding:2px 4px; cursor:pointer; border-radius:6px; }
.att-chip button:hover { color:var(--red); background:var(--tint-red); }
.msg-files { display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.msg-bubble.has-files:empty { display:none; }
.msg-img img { display:block; max-width:min(320px, 100%); max-height:320px; border-radius:10px; }
.msg-file { display:inline-flex; align-items:center; gap:4px; padding:6px 10px; border-radius:9px; background:rgba(255,255,255,.18);
  border:1px solid rgba(255,255,255,.35); color:inherit; text-decoration:none; font-size:12.5px; font-weight:600; }
.msg-turn.theirs .msg-file { background:var(--lift-1); border-color:var(--line); }
@media (max-width:700px) {
  #wsAttach, #wsSaved, #wsScripts { width:40px; height:40px; min-height:40px; padding:0; border-radius:50%; justify-content:center; font-size:17px; }
  .msg-img img { max-width:min(260px, 100%); max-height:260px; }
}

/* The thread's way into a client's card. Green because "closed deal / client"
   is; a finished client keeps the door but loses the colour. */
.chip.client-chip { background:rgba(53,201,143,.14); border-color:rgba(53,201,143,.55); color:var(--ink-good); font-weight:700; }
.chip.client-chip.past { background:transparent; border-color:var(--line); color:var(--muted); }
@media (max-width:700px) {
  .ws-thread-head #wsClientBtn { order:1; }
  /* The client panel takes the phone whole. */
  .client-panel { width:100vw; top:0; padding-top:env(safe-area-inset-top, 0px); }
  body.ws-open .client-panel { z-index:66; }
}

/* ---------------------------------------------------------- voice memos
   The speed pill beside every player, and the shelf of ready memos. */
.aud-wrap { display:flex; align-items:center; gap:2px; max-width:100%; }
.aud-wrap audio { flex:1 1 auto; min-width:0; }
.msg-att .aud-wrap, .msg-body .aud-wrap { width:100%; }
/* Legible at every speed, in both themes: ink on the panel at 1×, blue
   once you have changed it, and blue under the cursor either way. (It used
   to go grey at 1× and stay grey on hover -- "blends in".) */
.aud-rate { font: inherit; font-size:10.5px; font-weight:800; line-height:1; letter-spacing:.01em; padding:4px 7px; margin-left:4px;
  flex:0 0 auto; white-space:nowrap;
  border:1px solid var(--line-strong, var(--line)); border-radius:999px; background:var(--panel); color:var(--text); cursor:pointer;
  vertical-align:middle; font-variant-numeric:tabular-nums; min-width:36px;
  transition:background .12s ease, color .12s ease, border-color .12s ease; }
.aud-rate:not(.off) { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue); border-color:color-mix(in srgb, var(--blue) 35%, transparent); }
/* Hover is ink on paper, inverted -- near-black in the light theme, near-white
   in the dark -- so it stands out on a grey bubble, on the panel and on your
   own blue bubble alike. A blue hover vanished into the blue bubbles. */
.aud-rate:hover, .aud-rate.off:hover, .aud-rate:focus-visible { background:var(--text); color:var(--panel); border-color:var(--text); }
/* In your own bubble (blue, white text) the resting pill is white with the
   speed in blue, whichever speed it is; blue-on-blue was invisible. */
.msg-turn.mine .aud-rate, .msg-turn.mine .aud-rate:not(.off) { background:#fff; color:var(--blue-solid); border-color:transparent; }
.msg-turn.mine .aud-rate:hover, .msg-turn.mine .aud-rate.off:hover, .msg-turn.mine .aud-rate:focus-visible { background:#14161c; color:#fff; border-color:#14161c; }
.mbk-pop { position:fixed; z-index:80; max-height:min(460px, 72vh); display:flex; flex-direction:column; overflow:hidden;
  border:1px solid var(--line); border-radius:14px; background:var(--panel); box-shadow:var(--shadow); }
.mbk-head { display:flex; flex-direction:column; gap:2px; padding:10px 12px 8px; border-bottom:1px solid var(--line); }
.mbk-head b { font-size:13.5px; }
.mbk-list { overflow-y:auto; padding:6px; display:flex; flex-direction:column; gap:2px; }
.mbk-empty { padding:14px 10px; }
.mbk-row { display:flex; align-items:center; gap:9px; padding:8px 8px; border-radius:10px; }
/* The Discount code menu (prospects.js): the memo shelf's popover, two rows. */
.code-row { width:100%; border:0; background:none; color:var(--text); font:inherit; font-size:13.5px;
  text-align:left; cursor:pointer; }
.code-row:hover, .code-row:focus-visible { background:var(--lift-2); }
.code-pct { min-width:42px; padding:3px 0; border-radius:8px; text-align:center; font-size:12.5px;
  background:var(--tint-green); color:var(--ink-good); }
.mbk-row:hover { background:var(--lift-1); }
.mbk-play { flex:0 0 auto; width:28px; height:28px; border-radius:999px; border:1px solid var(--line); background:var(--panel);
  color:var(--text); font-size:11px; cursor:pointer; }
.mbk-play:hover { border-color:var(--blue); color:var(--blue); }
.mbk-what { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:1px; }
.mbk-what b { font-size:13px; }
.mbk-what small { font-size:11px; color:var(--muted); line-height:1.25; }
.mbk-meta { font-size:10.5px; color:var(--subtle); font-variant-numeric:tabular-nums; }
.mbk-audio { width:100%; height:32px; margin:0 8px 8px; }
.mbk-foot { border-top:1px solid var(--line); padding:8px 10px; }
.mbk-edit-box, .mbk-add { display:flex; flex-direction:column; gap:6px; width:100%; padding:8px; }
.mbk-edit-box input, .mbk-add input[type=text] { width:100%; font:inherit; font-size:12.5px; padding:6px 8px;
  border:1px solid var(--line); border-radius:8px; background:var(--panel); color:var(--text); }
.mbk-edit-box input:focus, .mbk-add input[type=text]:focus { outline:none; border-color:var(--blue); }
@media (max-width: 760px) { .mbk-pop { left:12px !important; right:12px; width:auto !important; } }
/* The same shelf as part of the Playbook page: no popover chrome, a taller list. */
.mbk-inline { display:flex; flex-direction:column; border:1px solid var(--line); border-radius:12px; background:var(--panel); overflow:hidden; }
.mbk-inline .mbk-list { max-height:560px; }
/* Scripts: the memo shelf's popover, with lines of words instead of memos. */
.snp-pop { max-height:min(560px, 78vh); }
.snp-q { width:100%; margin-top:6px; font:inherit; font-size:13px; padding:7px 10px; border:1px solid var(--line);
  border-radius:9px; background:var(--panel); color:var(--text); }
.snp-q:focus { outline:none; border-color:var(--blue); }
.snp-list { gap:0; padding:4px 6px 8px; }
.snp-source { position:sticky; top:-4px; z-index:1; margin:6px -6px 0; padding:6px 12px; background:var(--panel);
  border-bottom:1px solid var(--line); font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--subtle); }
.snp-group { padding:6px 0 2px; }
.snp-gh { display:flex; align-items:baseline; gap:8px; padding:2px 8px 4px; }
.snp-gh b { font-size:12.5px; }
.snp-open { margin-left:auto; font-size:11px; color:var(--muted); text-decoration:none; }
.snp-open:hover { color:var(--blue); }
.snp-line { display:flex; flex-direction:column; gap:2px; width:100%; text-align:left; padding:7px 9px; border:0; border-radius:9px;
  background:none; color:var(--text); font:inherit; cursor:pointer; }
.snp-line:hover, .snp-line:focus-visible { background:var(--lift-1); outline:none; }
.snp-line small { font-size:10.5px; font-weight:600; color:var(--muted); }
.snp-line span { font-size:12.5px; line-height:1.4; white-space:pre-line; display:-webkit-box; -webkit-line-clamp:4;
  -webkit-box-orient:vertical; overflow:hidden; }
.snp-ph { background:var(--tint-amber); color:inherit; border-radius:4px; padding:0 2px; }
.snp-saved { display:flex; align-items:flex-start; gap:4px; }
.snp-saved .snp-line { flex:1 1 auto; min-width:0; }
.snp-edit { flex:0 0 auto; margin-top:5px; }
.snp-empty { padding:6px 9px 8px; }
.snp-form textarea { width:100%; font:inherit; font-size:12.5px; padding:6px 8px; border:1px solid var(--line); border-radius:8px;
  background:var(--panel); color:var(--text); resize:vertical; }
.snp-form textarea:focus { outline:none; border-color:var(--blue); }
@media (max-width: 760px) { .snp-pop { left:12px !important; right:12px; width:auto !important; } }

/* ------------------------------------------------------ comment threads
   Under a setter's day card, and under its notification in the inbox. */
.day-comments { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 8px; }
.day-comments-btn { font-weight: 650; }
.day-thread { margin-top: 8px; }
.cmt { border: 1px solid var(--line); border-radius: 12px; background: var(--panel); overflow: hidden; }
.cmt .cmt-log { max-height: 360px; padding: 12px 14px; gap: 10px; }
.cmt .cmt-log .msg-turn { max-width: 88%; }
.cmt .msg-box { padding: 10px 12px; }
.cmt .msg-box textarea { min-height: 40px; font-size: 13px; }
.ibx-thread { flex: 1 1 100%; margin-top: 10px; }
.ibx-thread .cmt { border-radius: 10px; }
@media (max-width: 700px) {
  .cmt .cmt-log { max-height: 50vh; }
  .cmt .msg-box-row .msg-note { display: none; }
}
/* A message on its way: on screen the instant Send is pressed, a shade lighter until the server confirms it. */
.msg-turn.pending .msg-bubble { opacity:.72; }
/* The Brain's suggestion chip in the channel row: the wordmark gradient on
   the icon, blue when the suggestion is open under it. */
.ws-brain-chip { display:inline-flex; align-items:center; gap:5px; }
.ws-brain-chip .ws-brain-ico { width:14px; height:14px; color:var(--blue); }
.ws-brain-chip.on { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue); border-color:transparent; }
/* Notes on the prospect card: a line and a door. The thread is in the panel. */
.ws-notes-sum .helper { font-size:12.5px; }
/* A memo in a thread bubble: the player at a size a thumb can use, on every
   phone, with the file itself one tap away beside it. */
.tl-av { display:flex; align-items:center; gap:6px; width:100%; }
.tl-av .aud-wrap { flex:1 1 auto; min-width:0; }
.tl-atts audio { width:260px; max-width:100%; min-width:180px; height:40px; }
.tl-att-open { flex:0 0 auto; font-size:13px; text-decoration:none; opacity:.75; padding:2px 4px; }
.msg-turn.mine .tl-att-open { color:#fff; }
@media (max-width:700px) { .tl-atts audio { width:100%; min-width:200px; height:44px; } }
/* ---- the team's notes on a prospect: a panel beside the conversation -----
   Never under the messages: a note drawn there read as one more thing sent
   to the person. The panel takes a column of its own on a desk (the stage
   rail steps aside for it), the whole screen on a phone. Its entries are
   flat and yellow -- every one under its author's name, none of them the
   blue-on-the-right of a message to them. */
.ws4.noting { grid-template-columns:320px minmax(0,1fr) 340px 360px; }
:is(.ws4.noting.cardless, .ws4.noting.guide-fold) { grid-template-columns:320px minmax(0,1fr) 340px; }
.ws4.noting .ws-rail { display:none; }
.ws-npanel { --note-ground:var(--tint-yellow); --note-line:color-mix(in srgb, var(--ink-yellow) 38%, transparent);
  display:flex; flex-direction:column; min-height:0; min-width:0; border-left:1px solid var(--line); background:var(--panel-2);
  animation:panelIn .18s cubic-bezier(.2,.8,.3,1); }
.ws-npanel[hidden] { display:none !important; }
.ws-npanel-head { display:flex; align-items:flex-start; gap:8px; padding:12px 12px 10px 14px; border-bottom:1px solid var(--line); background:var(--panel); }
.ws-npanel-head strong { display:block; font-size:13px; letter-spacing:-.01em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ws-npanel-sub { display:block; font-size:10.5px; margin-top:1px; }
/* ooComments puts .cmt on the body itself: the log is its child. */
.ws-npanel-body, .ws-npanel-body.cmt { flex:1 1 auto; min-height:0; display:flex; flex-direction:column; border:0; border-radius:0; background:transparent; }
.ws-npanel-body .cmt-log { flex:1 1 auto; min-height:0; max-height:none; padding:14px 14px 10px; gap:12px; }
.ws-npanel-body .cmt-log .msg-blank { padding:26px 14px; font-size:12.5px; }
.ws-npanel-compose { border-top:1px solid var(--line); padding:10px 12px; background:var(--panel); }
.ws-npanel-compose textarea { width:100%; resize:none; min-height:56px; max-height:220px; line-height:1.45; }
.ws-npanel-compose .ws-compose-row { margin-top:6px; }
.ws-npanel-compose #wsNoteHint { flex:1 1 100%; font-size:10.5px; line-height:1.4; }
.ws-npanel-compose .action-row { margin-left:auto; }
/* Flat entries: name on every run, all down the left, on a sticky-note ground. */
.cmt-flat .msg-turn, .cmt-flat .msg-turn.mine { align-self:stretch; align-items:flex-start; max-width:100%; }
.cmt-flat .msg-turn .msg-bubble, .cmt-flat .msg-turn.mine .msg-bubble { background:var(--note-ground, var(--tint-yellow)); color:var(--text);
  border:1px solid var(--note-line, transparent); box-shadow:none; border-radius:12px; border-top-left-radius:6px; }
.cmt-flat .msg-turn.grouped .msg-bubble, .cmt-flat .msg-turn.grouped.mine .msg-bubble { border-top-left-radius:12px; border-top-right-radius:12px; }
.cmt-flat .msg-turn .msg-bubble.gone, .cmt-flat .msg-turn.mine .msg-bubble.gone { background:transparent; border-color:transparent; color:var(--subtle); }
.cmt-flat .msg-turn.mine .msg-bubble.editing, .cmt-flat .msg-turn.mine .msg-bubble.confirming { background:var(--panel-2); border-color:var(--line); }
.cmt-flat .msg-turn.mine .msg-bubble a, .cmt-flat .msg-turn.mine .msg-bubble .mention { color:var(--blue); text-decoration:none; }
.cmt-flat .msg-turn.mine .msg-bubble .mention:hover { background:var(--tint-blue); }
.cmt-flat .msg-turn.mine .msg-reacts { justify-content:flex-start; }
.cmt-flat .msg-turn.mine .msg-replies { align-self:flex-start; }
.cmt-flat .msg-turn.mine .tl-att-open { color:inherit; }
.cmt-flat .msg-time { align-self:flex-start; }
/* The Notes button beside the channels: a chip with the count, lit while the panel is up. */
.ws-tab[data-tab="note"] { display:inline-flex; align-items:center; gap:5px; border:1px solid var(--line); border-radius:999px;
  padding:5px 11px 5px 9px; font-size:11.5px; font-weight:700; color:var(--muted); background:transparent; }
.ws-tab[data-tab="note"]:hover { color:var(--text); background:var(--lift-2); }
.ws-tab[data-tab="note"].active { background:color-mix(in srgb, var(--blue) 16%, transparent); border-color:color-mix(in srgb, var(--blue) 50%, transparent); color:var(--blue); }
.ws-tab-n { min-width:17px; height:17px; padding:0 5px; border-radius:9px; display:inline-grid; place-items:center; font-size:10px; font-weight:800; background:var(--lift-3); color:var(--text); }
.ws-tab[data-tab="note"].active .ws-tab-n { background:var(--blue-solid); color:#fff; }
.ws-tab-n[hidden] { display:none !important; }
@media (max-width:1800px) { .ws4.noting { grid-template-columns:262px minmax(0,1fr) 320px 330px; }
  :is(.ws4.noting.cardless, .ws4.noting.guide-fold) { grid-template-columns:262px minmax(0,1fr) 320px; } }
/* Narrower: the list goes too while the notes are up. You know who you are writing about. */
@media (max-width:1600px) { .ws4.noting { grid-template-columns:minmax(0,1fr) 320px 330px; } .ws4.noting .ws-list { display:none; }
  :is(.ws4.noting.cardless, .ws4.noting.guide-fold) { grid-template-columns:262px minmax(0,1fr) 320px; }
  :is(.ws4.noting.cardless, .ws4.noting.guide-fold) .ws-list { display:flex; } }
/* A small desk: the notes take the card's column; the card is back the moment they close. */
@media (max-width:1280px) { .ws4.noting { grid-template-columns:minmax(0,1fr) 320px; } .ws4.noting .ws-card { display:none; } }
/* Under 1100 the card is already a row under the grid, so it stays. */
@media (min-width:701px) and (max-width:1100px) { .ws4.noting { grid-template-columns:minmax(0,1fr) 300px; } .ws4.noting .ws-card { display:flex; }
  :is(.ws4.noting.cardless, .ws4.noting.guide-fold) .ws-card { display:none; } }
@media (max-width:760px) { .ws4.noting { grid-template-columns:1fr; } }
/* A phone: the notes are a sheet over the thread, like the person card, and
   they follow the keyboard the way the thread does (fitWorkspace sizes .ws4). */
@media (max-width:700px) {
  .ws4:not(.nobody) .ws-npanel { display:none; }
  body.ws-notes-open .ws4:not(.nobody) .ws-npanel { display:flex; position:absolute; inset:0; z-index:66; border:0; background:var(--panel); animation:hpopIn .16s ease; }
  .ws-npanel-head { padding:calc(10px + env(safe-area-inset-top, 0px)) 10px 8px 14px; }
  .ws-npanel-head strong { font-size:16px; }
  .ws-npanel-head .msg-x { width:36px; height:36px; }
  .ws-npanel-body .cmt-log { padding:12px 10px; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
  .ws-npanel-compose { padding:6px 8px calc(8px + env(safe-area-inset-bottom, 0px)); display:flex; flex-wrap:wrap; align-items:flex-end; gap:6px; }
  .ws-npanel-compose #wsNoteAtts { flex:0 0 100%; }
  .ws-npanel-compose textarea { flex:1 1 0; min-width:0; width:auto; min-height:40px; max-height:160px; padding:9px 14px; border-radius:20px; font-size:16px; line-height:1.35; }
  .ws-npanel-compose .ws-compose-row { flex:0 0 auto; margin:0; gap:6px; }
  .ws-npanel-compose .action-row { margin:0; gap:6px; flex-wrap:nowrap; }
}

/* ClickUp on its way out: what the hourly watch found, on the Dashboard.
   Temporary by design -- it hides itself once nothing has been written over
   there for three weeks. */
.cu-list { margin: 8px 0 0; padding-left: 18px; }
.cu-list li { font-size: 13.5px; margin: 3px 0; }
.cu-diffs { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.cu-diff { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--panel-2); margin-top: 6px; }
.cu-diff-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cu-diff-who b { font-size: 14px; }
.cu-diff-who .helper { font-size: 12.5px; }
.cu-diff .action-row { flex: 0 0 auto; gap: 6px; }

/* Refresh, in the top bar of the installed app only: a browser already has
   one in its address bar, and a second would be clutter. Pages poll and redraw
   themselves when the app comes back to the front; this is for the moment
   somebody does not want to wait. */
.tb-reload { display: none; }
@media (display-mode: standalone) { .tb-reload { display: inline-flex; } }
html[data-standalone] .tb-reload { display: inline-flex; }

/* What a link points at, under the message that carries it.

   Small on purpose: a card the size of the message is a webpage in a chat.
   One line of site, one of title, one of description, and a thumbnail only
   when the page has a real one. It sits inside the bubble, so it is carried
   by the same colour and the same corner radius as the words above it. */
.lp-holder { display: block; }
.msg-bubble .lp, .msg-bubble .lp:hover {
  text-decoration: none; color: var(--text); }
.msg-bubble .lp .lp-title { text-decoration: none; }
.lp { display: flex; gap: 10px; align-items: stretch; margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  text-decoration: none; color: var(--text); max-width: 420px;
  transition: border-color .14s ease, transform .14s ease; }
.lp:hover { border-color: var(--line-strong); transform: translateY(-1px); }
.lp-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; justify-content: center; }
.lp-site { display: flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; color: var(--subtle);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lp-fav { width: 12px; height: 12px; border-radius: 3px; object-fit: contain; flex: 0 0 auto; }
.lp-title { font-size: 13px; font-weight: 650; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.lp-desc { font-size: 11.5px; line-height: 1.35; color: var(--muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.lp-img { position: relative; flex: 0 0 auto; width: 76px; border-radius: 7px; overflow: hidden; background: var(--lift-2); }
.lp-img img { display: block; width: 100%; height: 100%; min-height: 54px; max-height: 76px; object-fit: cover; }
/* A video says so: the still gets the triangle every player uses. */
.lp-play { position: absolute; inset: 0; display: grid; place-items: center; color: #fff;
  background: rgba(10,12,16,.28); }
.lp-play svg { filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
/* How long it runs, where every player puts it. */
.lp-len { position: absolute; right: 3px; bottom: 3px; padding: 0 4px; border-radius: 4px;
  background: rgba(10,12,16,.82); color: #fff; font-size: 9.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.lp-len.flat { position: static; background: var(--lift-3); color: var(--muted); margin-left: 4px; }
/* The Brain's sentence, marked as the Brain's. */
.lp-brain { display: flex; gap: 5px; align-items: flex-start; }
.lp-brain-ico { flex: 0 0 auto; margin-top: 2px; color: var(--blue); }
.msg-turn.mine .msg-bubble .lp .lp-brain-ico { color: rgba(255,255,255,.85); }
.msg-turn.mine .msg-bubble .lp {
  background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.28); color: #fff; }
.msg-turn.mine .msg-bubble .lp .lp-title { color: #fff; }
.msg-turn.mine .msg-bubble .lp .lp-site, .msg-turn.mine .msg-bubble .lp .lp-desc { color: rgba(255,255,255,.82); }
.msg-turn.mine .msg-bubble .lp:hover { border-color: rgba(255,255,255,.5); }
/* Notes are flat and yellow; the card follows the ground it is on. */
.cmt-flat .msg-bubble .lp { background: var(--panel); border-color: var(--line); }
@media (max-width: 700px) { .lp { max-width: none; } .lp-img { width: 64px; } }

/* While the guide is open on a desk under 1750px there is not room for the
   rail, the list, the conversation, the card and the guide: the card folds
   for the duration (guide-fold) and comes back when the guide closes, without
   touching what the person had chosen. The rail stays because the guide is
   mostly about the rail, but it and the list take their compact widths so the
   conversation keeps a readable column. */
@media (min-width:1100px) { body.guide-open .ws4.guide-fold:not(.noting):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 240px minmax(0,1fr); } }
/* With nobody open the list keeps the whole room, guide or no guide. */
.ws4.nobody.guide-fold { grid-template-columns:var(--ws-rail-w, 176px) minmax(0,1fr); }

/* The ClickUp strip, on a day when ClickUp has nothing to say. It keeps its
   two buttons -- "Check now" and "Compare statuses" are the reason it exists
   -- and gives up the heading, the explanation and most of its height. */
#cuSection.cu-quiet { margin-bottom: 10px; }
#cuSection.cu-quiet .section-head { margin-bottom: 0; align-items: center; }
#cuSection.cu-quiet h2 { display: none; }
#cuSection.cu-quiet #cuSub { display: block; font-size: 12px; margin: 0; color: var(--subtle); }
#cuSection.cu-quiet .action-row .button { font-size: 12px; padding: 3px 9px; }

/* Who may look at your day, on the Account page's Google card. */
.gcal-share { display:flex; align-items:center; gap:10px; padding:4px 0; }
.gcal-share > span { flex:1; font-size:13px; }
.gcal-share select { width:auto; min-width:190px; font-size:12px; padding:5px 8px; }

/* The playback-speed menu on a voice memo: Slack's, because Slack's is right.
   Click the pill, see every speed at once, pick one. */
.aud-menu { position:fixed; z-index:95; min-width:148px; padding:6px; background:var(--panel);
  border:1px solid var(--line-strong); border-radius:11px; box-shadow:var(--shadow-lg); }
.aud-menu-h { font-size:11px; letter-spacing:.04em; color:var(--subtle); padding:3px 9px 6px; }
.aud-menu-i { display:flex; align-items:center; gap:8px; width:100%; text-align:left; border:0;
  background:none; color:var(--text); font:inherit; font-size:13px; padding:6px 9px;
  border-radius:7px; cursor:pointer; }
.aud-menu-i:hover { background:var(--lift-3); }
.aud-menu-i.on { color:var(--blue); font-weight:600; }
.aud-tick { width:12px; flex:none; color:var(--blue); }

/* Asking for the follow-up note the moment a date is set: a ring, not a
   dialog, so somebody in a hurry can still ignore it. */
.heat-note-ask { border-color: var(--blue) !important;
  box-shadow: 0 0 0 3px var(--tint-blue); animation: heat-note-pop .4s ease; }
@keyframes heat-note-pop { from { box-shadow: 0 0 0 0 var(--tint-blue); } }

/* The follow-up board on the Sales Floor. One table, read top to bottom, with
   a rule between the bands -- the four separate cards it started as were four
   headings and a lot of scrolling for a list nobody reads out of order. */
.fu-tabs { display:inline-flex; gap:3px; padding:4px; border:1px solid var(--line);
  border-radius:12px; background:var(--lift-1); margin:0 0 12px; flex-wrap:wrap; }
.fu-tab { display:inline-flex; align-items:center; gap:7px; border:0; background:none;
  color:var(--muted); font:inherit; font-size:13px; font-weight:600; padding:7px 13px;
  border-radius:9px; cursor:pointer; }
.fu-tab b { font-size:11.5px; font-weight:700; padding:1px 6px; border-radius:999px;
  background:var(--lift-3); color:var(--muted); }
.fu-tab:hover { color:var(--text); }
.fu-tab.on { background:var(--panel); color:var(--text); box-shadow:var(--shadow-sm); }
.fu-tab.on b { background:var(--tint-blue); color:var(--blue); }
.fu-tab.over b { background:var(--tint-red); color:var(--ink-bad); }
.fu-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:0 0 14px; }
.fu-bar .gap { flex:1; }

.fu-card { padding:0; overflow:hidden; }
.fu-table { width:100%; }
.fu-table th { font-size:11px; letter-spacing:.05em; text-transform:uppercase; color:var(--subtle); }
.fu-table td { padding:9px 12px; vertical-align:middle; }
.fu-sep td { padding:7px 12px 5px; font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--subtle); background:var(--lift-1); border-top:1px solid var(--line); }
.fu-sep.over td { color:var(--ink-bad); background:var(--tint-red); }
.fu-row { border-top:1px solid var(--line); transition:opacity .18s ease, transform .18s ease; }
.fu-row:hover { background:var(--lift-1); }
.fu-row.fu-gone { opacity:0; transform:translateX(14px); }

.fu-c-tick { width:40px; }
.fu-tick { border:0; background:none; color:var(--subtle); cursor:pointer; padding:2px;
  display:flex; align-items:center; border-radius:50%; }
.fu-tick:hover { color:var(--green); background:var(--tint-green); }
/* The whole board fits in one view, so nothing is read by scrolling sideways.
   Every td in the app is nowrap, which is right for a figure and wrong for a
   sentence -- and a follow-up's "what" IS a sentence, sometimes a long one
   ("Follow up about Black Friday special. He canceled the call the day before
   because his wife said..."). On one line it pushed Owner, Set and Due off the
   right-hand edge entirely. Stefan, 10-08-2026: "let's wrap the text. That way
   we can fit everything in one view without having to scroll left to right or
   top to bottom."

   So the two text columns wrap and take the room that is left, and the three
   short columns keep their natural width. */
.fu-table { table-layout:auto; }
.fu-table td.fu-what, .fu-table td.fu-who { white-space:normal; }
.fu-what { width:auto; min-width:0; }
.fu-who { width:22%; min-width:140px; }
.fu-what b { display:block; font-size:13.5px; font-weight:650; overflow-wrap:anywhere; }
.fu-what span { display:block; font-size:12.5px; color:var(--muted); overflow-wrap:anywhere; }
.fu-nonote { color:var(--subtle); }
.fu-who b { font-size:13.5px; cursor:pointer; }
.fu-who b:hover { color:var(--blue); text-decoration:underline; }
.fu-who .badge { margin-left:6px; }
.fu-who .helper { display:block; font-size:11.5px; }
.fu-c-own { white-space:nowrap; }
.fu-c-set, .fu-c-due { white-space:nowrap; }
.fu-c-set b, .fu-c-due b { display:block; font-size:13px; }
.fu-c-set span, .fu-c-due span { font-size:11.5px; color:var(--muted); }
.fu-c-set b { font-weight:500; color:var(--muted); }
/* Overdue is red, date and all -- not just the little line underneath it. */
.fu-row.late .fu-c-due b, .fu-row.late .fu-c-due span { color:var(--ink-bad); font-weight:600; }
.fu-c-act { white-space:nowrap; text-align:right; }
.fu-heat { color:var(--amber); font-size:11px; letter-spacing:1px; margin-left:6px; }

/* The two follow-up fields on the client card, stacked. */
.heat-fu { display:flex; flex-direction:column; gap:6px; }

@media (max-width:860px) {
  .fu-table thead { display:none; }
  .fu-table tr { display:grid; grid-template-columns:40px minmax(0,1fr) auto; }
  .fu-table .fu-what { grid-column:2; }
  .fu-table .fu-who { grid-column:2; }
  .fu-c-own, .fu-c-set { display:none; }
  .fu-c-due, .fu-c-act { grid-column:3; text-align:right; }
  .fu-sep td { grid-column:1 / -1; }
}

/* A due count on a subnav tab — same red as the rail's, so it reads the same
   wherever you meet it. */
/* It had only the margin: the red lived in a bare .tab-badge rule that was
   scoped to the phone's tab bar on 10-14, so on a desktop the count went plain
   and "Follow-ups 1" read as a retitled tab (Stefan, 10-19-2026). */
.subnav-tab .tab-badge { display:inline-block; vertical-align:1px; margin-left:7px; min-width:18px; height:18px;
  padding:0 5px; border-radius:9px; background:var(--red,#fb6472); color:#fff; font-size:10px; font-weight:800;
  line-height:18px; text-align:center; font-variant-numeric:tabular-nums; }
.subnav-tab .tab-badge[hidden] { display:none; }
/* On the selected tab it sits on the blue, so a white ring keeps it apart. */
.subnav-tab.on .tab-badge { box-shadow:0 0 0 1.5px #fff; }

/* Given to others: who it went to, and how many of theirs are late. A task you
   hand over leaves your board, so this column is the only place it is ever
   seen again — it has to say when one has gone past its date. */
.tkc-late { font-size:11px; font-weight:700; padding:2px 8px; border-radius:999px;
  background:var(--alarm-bg); color:var(--alarm-ink); margin-left:6px; white-space:nowrap; }
.tkc-who-filter { display:flex; gap:4px; flex-wrap:wrap; padding:0 2px 8px; }
.tkw { display:inline-flex; align-items:center; gap:5px; border:1px solid var(--line);
  background:var(--panel); color:var(--muted); border-radius:999px; font:inherit;
  font-size:11.5px; padding:3px 9px; cursor:pointer; }
.tkw:hover { background:var(--lift-2); color:var(--text); }
.tkw b { font-size:10.5px; font-weight:700; padding:0 5px; border-radius:999px;
  background:var(--lift-3); color:var(--muted); }
.tkw.on { background:var(--tint-blue); border-color:var(--blue-line); color:var(--blue); font-weight:600; }
.tkw.on b { background:var(--blue-solid); color:#fff; }
.tkw.late b { background:var(--tint-red); color:var(--ink-bad); }
.tkw.late.on b { background:var(--alarm-bg); color:var(--alarm-ink); }

/* Task columns drag by their grip, and look while dragging exactly like the
   sidebar's entries do — same faded original, same blue wash on what is about
   to be displaced. One drag gesture in the app, not two. */
.tkc-grip { display:none; color:var(--subtle); cursor:grab; fill:currentColor;
  margin-right:2px; align-items:center; }
.tkc-grip:active { cursor:grabbing; }
.tkc-head:hover .tkc-grip { display:inline-flex; opacity:.55; }
.tkc-grip:hover { opacity:1 !important; color:var(--blue); }
.tkc-dragging { opacity:.35; }
.tkc-drag-over { background:color-mix(in srgb, var(--blue) 8%, transparent); border-radius:12px; }
/* The late count on the "Assigned to others" toggle. */
.tk-late-n { margin-left:6px; font-size:10.5px; font-weight:700; padding:1px 6px;
  border-radius:999px; background:var(--alarm-bg); color:var(--alarm-ink); }

/* Grid layout for the task board: the same columns, wrapped onto as many rows
   as they need. Three lists of two tasks each should not cost a sideways
   scroll — Stefan, 09-19-2026: "if I only have two to three tasks in each of
   those, I'd like ... to stack them top to bottom as well." */
#tkBoard.tk-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(268px, 1fr));
  align-items:start; gap:14px; overflow-x:visible; }
#tkBoard.tk-grid .tkc { width:auto; min-width:0; flex:none; }
/* A long list must not make its whole row tall: it scrolls inside itself. */
#tkBoard.tk-grid .tkc-body { max-height:46vh; overflow-y:auto; }
#tkBoard.tk-grid .tkc-new { align-self:start; }

/* List: the board read straight down instead of across — every list a
   heading, every task a full-width row with its dates and people on the right
   and its description underneath. Stefan, 09-19-2026: "not rows ... column
   like the task list on GHL layout (like our resource page kind of)". The
   same rows, the same drag, the same everything the cards do: only the shape
   changes. */
#tkBoard.tk-list { display:block; overflow-x:visible; max-width:1060px; }
#tkBoard.tk-list .tkc { width:auto; min-width:0; flex:none; margin:0 0 14px;
  padding:0; border:0; border-radius:0; background:none; box-shadow:none;
  overflow:visible; }
/* A section is not a card: no corner glow bleeding out at the margin, and it
   does not lift under the cursor — the rows inside it do. */
#tkBoard.tk-list .tkc::after { display:none; }
#tkBoard.tk-list .tkc:hover { transform:none; box-shadow:none; }
/* A section heading, not a card title: the name, then what is in it, then the
   controls out at the right where the rows end. */
#tkBoard.tk-list .tkc-head { padding:0 2px 4px; margin-bottom:5px; border-bottom:0; }
#tkBoard.tk-list .tkc-head h2 { flex:0 0 auto; font-size:11.5px; font-weight:750;
  letter-spacing:.05em; text-transform:uppercase; color:var(--muted); }
#tkBoard.tk-list .tkc-head [data-rename] { margin-left:auto; }
#tkBoard.tk-list .tkc-assigned .tkc-head h2 { color:var(--blue); }
#tkBoard.tk-list .tkc-body { max-height:none; overflow:hidden; gap:0; min-height:0;
  background:var(--panel); border:1px solid var(--line); border-radius:11px; }
#tkBoard.tk-list .tkt { align-items:center; padding:7px 11px; border-radius:0; }
#tkBoard.tk-list .tkt + .tkt { border-top:1px solid var(--line); }
#tkBoard.tk-list .tkt:hover { background:var(--lift-2); }
#tkBoard.tk-list .tkt-grip, #tkBoard.tk-list .tkt-tick, #tkBoard.tk-list .tkt-star { margin-top:0; }
/* Title and dates share the first line, the description sits under them —
   GHL's shape, and the shelf's. */
#tkBoard.tk-list .tkt-body { display:flex; align-items:center; flex-wrap:wrap; gap:1px 14px; }
#tkBoard.tk-list .tkt-text { flex:1 1 55%; font-size:13px; }
#tkBoard.tk-list .tkt-meta { order:2; flex:0 0 auto; margin-top:0; flex-wrap:nowrap; }
#tkBoard.tk-list .tkt-note { order:3; flex:1 1 100%; margin-top:0; -webkit-line-clamp:1; }
#tkBoard.tk-list .tkt-link { order:4; flex:1 1 100%; }
#tkBoard.tk-list .tkc-add { width:auto; padding:3px 6px; margin-bottom:4px; }
#tkBoard.tk-list .tkc-empty { padding:7px 11px; }
#tkBoard.tk-list .tkc-done { margin-top:6px; }
#tkBoard.tk-list .tkc-new { max-width:380px; }
#tkBoard.tk-list .tkc-input { max-width:640px; }

/* ------------------------------------------------------------ applications */
.ap-stats { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:10px; }
.ap-stat { background:var(--lift-1); border:1px solid var(--line); border-radius:var(--radius); padding:12px 14px; }
.ap-stat b { display:block; font-size:22px; font-weight:750; letter-spacing:-.02em; }
.ap-stat span { display:block; margin-top:2px; font-size:11.5px; color:var(--muted); }
.ap-funnel { margin-top:12px; display:grid; gap:6px; }
.ap-bar { display:grid; grid-template-columns:minmax(160px, 34%) 1fr 110px; gap:10px; align-items:center; font-size:12.5px; }
.ap-bar-l { color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ap-bar-t { height:14px; background:var(--lift-1); border-radius:4px; overflow:hidden; }
.ap-bar-t i { display:block; height:100%; background:var(--blue-solid); border-radius:4px; transition:width .4s ease; }
.ap-bar.done .ap-bar-t i { background:var(--green); }
.ap-bar-n { font-variant-numeric:tabular-nums; font-weight:650; }
.ap-bar-n small { color:var(--muted); font-weight:500; margin-left:4px; }
.ap-bar-n em { color:var(--red, #d64545); font-style:normal; font-weight:600; margin-left:6px; font-size:11px; }
.ap-table td, .ap-table th { vertical-align:top; }
.ap-row { cursor:pointer; }
.ap-row:hover td { background:var(--lift-1); }
.ap-row.is-new td:first-child { box-shadow:inset 3px 0 0 var(--blue-solid); }
.ap-dec { max-width:220px; }
.pill.warn { background:color-mix(in srgb, var(--blue) 16%, transparent); color:var(--ink-info); }
.ap-notes { display:block; margin-top:16px; font-size:11.5px; font-weight:650; color:var(--muted); }
.ap-notes textarea { display:block; width:100%; margin-top:4px; font:inherit; font-size:13px; color:var(--text); background:var(--lift-1); border:1px solid var(--line); border-radius:8px; padding:8px 10px; resize:vertical; }

/* ----------------------------------------------------------------- website */
.ws-two { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:14px; }
@media (max-width: 900px) { .ws-two { grid-template-columns:1fr; } }
.ws-days { display:flex; align-items:flex-end; gap:6px; height:160px; padding-top:8px; overflow-x:auto; }
.ws-day { position:relative; flex:1 0 22px; height:100%; display:flex; flex-direction:column; justify-content:flex-end; align-items:center; }
.ws-day i { display:block; width:100%; background:var(--blue-solid); border-radius:3px 3px 0 0; opacity:.55; min-height:2px; }
.ws-day u { position:absolute; left:0; right:0; bottom:18px; display:block; background:var(--blue-solid); border-radius:3px 3px 0 0; min-height:0; }
.ws-day b { position:absolute; left:0; right:0; bottom:18px; display:block; background:var(--green); border-radius:3px 3px 0 0; min-height:0; }
.ws-day span { margin-top:4px; font-size:9.5px; color:var(--muted); white-space:nowrap; }
.ap-stat b small { font-size:12px; color:var(--muted); font-weight:500; margin-left:4px; }

/* ------------------------------------------------------- apps & forms: which form */
/* The pages and the forms: one row each -- what it is, a line on it, the
   link, Copy -- rather than a block apiece. Stefan, 09-21-2026: "I like that
   the descriptions are there with the links, but I don't know if it needs
   these big, huge blocks."
   One page or form per row, shaped like a shelf item on SOPs & Resources --
   Stefan, 09-24-2026: "we need to make the rows a little bit cleaner. I'm
   thinking something kind of like the SOPs and resources page." Hairline
   separators inside one slab became a row with its own edge that lifts under
   the cursor, which is what makes a list read as a list of THINGS rather than
   as a table. The kind is a chip at the front, so Page / Client page / Legal
   is legible without reading the words after it. */
/* ---- one row, two pages ---------------------------------------------------
   Apps, Forms & Links and Booking pages both list "a thing with an address",
   so they are the same row: name on the left, what it is in the middle, the
   address and its buttons on the right. It was .ap-form, which named the page
   it happened to be written for rather than the thing it is. Stefan,
   09-28-2026, on the booking list: "I want it to kind of look like the apps
   and form links." */
.oo-rows { display:flex; flex-direction:column; gap:5px; }
/* The middle column carries a floor, because `auto` on the link column takes
   whatever it wants: on Apps & Forms, where the row also has an Edit button,
   it squeezed the description to nothing at all and the line explaining what
   each form IS rendered at zero pixels wide. */
.oo-row { display:grid; grid-template-columns:minmax(150px, 250px) minmax(180px, 1fr) minmax(0, auto);
  gap:4px 18px;
  align-items:center; padding:4px 12px; border:1px solid var(--line); border-radius:10px;
  background:var(--panel); transition:border-color .14s ease, box-shadow .14s ease; }
.oo-row:hover { border-color:var(--line-strong); box-shadow:0 2px 10px rgba(0,0,0,.14); }
.oo-row.is-one { border-color:var(--blue); background:color-mix(in srgb, var(--blue) 6%, transparent); }
/* One line, always. The row's height was set by whichever title was longest:
   "Book your call (pre-call page with the calendar)" wrapped to three lines
   and made a 62px row out of a 38px one. Stefan, 09-24-2026: "make the text
   smaller there so it doesn't wrap, so we can keep those blocks thinner."
   So the head never wraps, and the full name is on the row's tooltip for the
   rare one that still has to shorten. */
.oo-row-head { display:flex; align-items:center; gap:8px; flex-wrap:nowrap; min-width:0; }
.oo-row-head h3, .oo-row-head h2.as-h3 { margin:0; font-size:12.5px; font-weight:700;
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* A floor, not a width: every chip takes the same slot so the nine names
   start at the same x instead of stepping in and out behind Page / Legal /
   Client. A recurring element in a list belongs in the same place on each row. */
.oo-row-meta { margin:0; font-size:11.5px; color:var(--muted); line-height:1.4; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.oo-row-meta b { color:var(--text); font-weight:600; }
.oo-row-after { display:none; }
/* Filled to the track and pushed right from the inside, NOT `justify-self:end`.
   A grid item aligned to the end of its track is sized shrink-to-fit, which
   resolves to max-content and is allowed to overflow -- so at around 1024px
   the address hung out of its column to the left and was drawn straight over
   the description beside it. Filling the track keeps the shrinking where it
   belongs, on the address. */
.oo-row-end { display:flex; align-items:center; gap:6px; justify-content:flex-end;
  width:100%; white-space:nowrap; min-width:0; }
/* The address gives way before the description does: it can be read in full
   from the link itself or the Copy button, and a URL laid over the sentence
   next to it is worse than a shortened URL. */
.oo-row-end a { font-size:11.5px; font-weight:600; color:var(--ink-info); max-width:260px;
  min-width:0; flex:0 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* A 38px button inside a 38px row IS the row. Beside the name and the address
   these are the secondary thing, so they take the smaller size. */
.oo-row-end .tb-btn, .oo-row-end button { flex:0 0 auto; height:24px; min-width:0;
  padding:0 8px; border-radius:7px; font-size:11px; }
.oo-row-meta:hover, .oo-row-meta:focus-within { white-space:normal; }
/* Three columns plus an address plus two buttons need about a thousand
   pixels of row. Below that they stack, rather than the link overflowing its
   track and being drawn across the description -- which is what a narrow
   desktop window did. Stacked, the name may use as many lines as it likes:
   the height it was costing is only a problem when the rows are one-liners. */
@media (max-width:1020px) {
  .oo-row { grid-template-columns:1fr; gap:6px; padding:9px 12px; }
  .oo-row-head { flex-wrap:wrap; }
  .oo-row-head h3, .oo-row-head h2.as-h3 { white-space:normal; }
  .oo-row-end { justify-content:flex-start; flex-wrap:wrap; white-space:normal; }
  .oo-row-end a { max-width:100%; }
}
/* The same stack while a panel has pushed the page over -- the booking log
   takes 620px, and the row it was opened from is then a column a third of
   the width, with its buttons drawn over its own description. */
body.cp-open .oo-row { grid-template-columns:1fr; gap:6px; padding:9px 12px; }
body.cp-open .oo-row-head { flex-wrap:wrap; }
body.cp-open .oo-row-end { justify-content:flex-start; flex-wrap:wrap; white-space:normal; }
body.cp-open .oo-row-end a { max-width:100%; }
.ap-scope { margin:0 0 10px; font-size:15px; font-weight:750; letter-spacing:-.01em; }
.ap-scope span { font-weight:500; color:var(--muted); }
.ap-range { display:inline-flex; align-items:center; gap:6px; }
.ap-range input[type=date] { width:150px; }
.ap-range[hidden] { display:none; }

/* ---- Form builders: the check-in builder (form_builder.html) and the
   application editor (application_editor.html) share one grammar -- a bar,
   a palette, the form itself, and the settings of what is selected. ---- */
.fb { display:flex; flex-direction:column; height:calc(100vh - 108px);
  min-height:560px; }
.fb-bar { display:flex; align-items:center; gap:14px; padding:0 0 14px; }
.fb-back { font-size:18px; color:var(--muted); text-decoration:none; padding:4px 8px;
  border-radius:8px; }
.fb-back:hover { background:var(--lift-2); color:var(--text); }
.fb-title { flex:1; min-width:0; }
.fb-title input { width:100%; max-width:420px; font-size:17px; font-weight:800;
  background:transparent; border:1px solid transparent; padding:4px 8px; margin-left:-8px; }
.fb-title input:hover { border-color:var(--line); }
.fb-title input:focus { border-color:var(--blue); background:var(--field); }
.fb-meta { display:flex; align-items:center; gap:9px; margin-top:2px; padding-left:1px; }
.fb-state { font-size:9.5px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  padding:2px 7px; border-radius:20px; background:var(--lift-3); color:var(--muted); }
.fb-state.live { background:var(--tint-green); color:var(--ink-good); }
.fb-dirty { font-size:10px; font-weight:700; color:var(--ink-warn); }
.fb-actions { display:flex; align-items:center; gap:8px; }
.fb-seg { display:flex; background:var(--lift-2); border-radius:9px; padding:2px; }
.fb-seg button { border:0; background:transparent; color:var(--muted); font-size:11px;
  font-weight:700; padding:5px 10px; border-radius:7px; cursor:pointer; }
.fb-seg button.on { background:var(--panel); color:var(--text); box-shadow:var(--shadow-sm); }

.fb-panes { flex:1; display:grid; grid-template-columns:216px minmax(0,1fr) 288px;
  gap:12px; min-height:0; }
.fb-palette, .fb-settings { background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); padding:14px; overflow-y:auto; }
.fb-palette h3, .fb-settings h3 { margin:0 0 12px; font-size:10.5px; font-weight:800;
  letter-spacing:.09em; text-transform:uppercase; color:var(--muted); }
.fb-group { margin-bottom:14px; }
.fb-group > span { display:block; font-size:9.5px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:6px; }
.fb-chip { display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  padding:8px 10px; margin-bottom:5px; border-radius:9px; cursor:grab;
  border:1px solid var(--line); background:var(--lift-1); color:var(--text);
  font-size:12px; font-weight:600; transition:border-color .13s, background .13s; }
.fb-chip:hover { border-color:var(--blue); background:var(--lift-2); }
.fb-chip i { color:var(--muted); font-style:normal; font-size:13px; width:15px;
  text-align:center; }
.fb-hint { font-size:10.5px; color:var(--subtle); line-height:1.5; margin-top:4px; }

.fb-canvas { overflow-y:auto; padding:2px; }
.fb-sheet { max-width:640px; margin:0 auto; background:var(--panel);
  border:1px solid var(--line); border-radius:var(--radius); padding:24px 26px 30px; }
.fb-sheet.mobile { max-width:392px; }
.fb-sheet-head { margin-bottom:18px; padding-bottom:16px; border-bottom:1px solid var(--line); }
.fb-formname { width:100%; font-size:20px; font-weight:800; letter-spacing:-.02em;
  background:transparent; border:1px solid transparent; padding:3px 6px; margin-left:-6px; }
.fb-intro { width:100%; resize:vertical; background:transparent; font-size:12.5px;
  color:var(--muted); border:1px solid transparent; padding:3px 6px; margin-left:-6px; }
.fb-formname:hover, .fb-intro:hover { border-color:var(--line); }
.fb-formname:focus, .fb-intro:focus { border-color:var(--blue); background:var(--field); }

.fb-q { position:relative; border:1.5px solid transparent; border-radius:11px;
  padding:11px 12px; margin-bottom:4px; cursor:pointer;
  transition:border-color .13s, background .13s; }
.fb-q:hover { background:var(--lift-1); }
.fb-q.on { border-color:var(--blue); background:var(--lift-1); }
.fb-q.drag { opacity:.4; }
.fb-q.over { box-shadow:0 -2px 0 0 var(--blue); }
.fb-q-tools { position:absolute; top:6px; right:8px; display:flex; gap:2px; opacity:0;
  transition:opacity .13s; }
.fb-q:hover .fb-q-tools, .fb-q.on .fb-q-tools { opacity:1; }
.fb-q-tools button { border:0; background:var(--panel); color:var(--muted);
  width:22px; height:22px; border-radius:6px; cursor:pointer; font-size:11px;
  box-shadow:var(--shadow-sm); }
.fb-q-tools button:hover { color:var(--text); background:var(--lift-3); }
.fb-q-tools button.rm:hover { color:var(--ink-bad); }
.fb-grip { position:absolute; left:2px; top:12px; color:var(--subtle); cursor:grab;
  font-size:11px; letter-spacing:-1px; opacity:0; transition:opacity .13s; }
.fb-q:hover .fb-grip { opacity:1; }

.fb-label { font-size:12.5px; font-weight:700; margin-bottom:3px; }
.fb-label .req { color:var(--ink-bad); margin-left:3px; }
.fb-desc { font-size:11px; color:var(--muted); margin-bottom:7px; line-height:1.45; }
.fb-section-h { font-size:16px; font-weight:800; letter-spacing:-.015em; margin:14px 0 2px; }
.fb-q.is-section { border-top:1px solid var(--line); border-radius:0; margin-top:14px;
  padding-top:16px; }
.fb-q.is-section:first-child { border-top:0; margin-top:0; }
.fb-fake { width:100%; border:1px solid var(--line); border-radius:9px;
  background:var(--field); padding:9px 11px; font-size:12px; color:var(--subtle); }
.fb-fake.tall { min-height:64px; }
.fb-opt { display:flex; align-items:center; gap:9px; border:1px solid var(--line);
  border-radius:9px; padding:8px 11px; margin-bottom:5px; font-size:12px; }
.fb-opt span.box { width:13px; height:13px; border:1.5px solid var(--line-strong);
  border-radius:3px; flex:none; }
.fb-opt span.box.round { border-radius:50%; }
.fb-scale { display:flex; gap:5px; flex-wrap:wrap; }
.fb-scale b { min-width:31px; text-align:center; border:1px solid var(--line);
  border-radius:8px; padding:6px 0; font-size:12px; font-weight:700; background:var(--field); }
.fb-scale-ends { display:flex; justify-content:space-between; font-size:10.5px;
  color:var(--subtle); margin-top:5px; }
.fb-cond { display:inline-flex; align-items:center; gap:4px; font-size:9.5px;
  font-weight:700; color:var(--ink-info); background:var(--tint-blue);
  padding:1px 7px; border-radius:20px; margin-left:6px; }
.fb-empty { text-align:center; color:var(--subtle); font-size:12.5px; padding:34px 0; }

.fb-field { margin-bottom:12px; }
.fb-field > label { display:block; font-size:9.5px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); margin-bottom:5px; }
.fb-field input, .fb-field select, .fb-field textarea { width:100%; font-size:12px; }
.fb-field textarea { resize:vertical; }
.fb-note { font-size:10px; color:var(--subtle); line-height:1.5; margin-top:4px; }
.fb-key { font-family:ui-monospace,Menlo,monospace; font-size:11px; color:var(--muted);
  background:var(--lift-2); padding:5px 8px; border-radius:7px; word-break:break-all; }
.fb-row { display:flex; gap:8px; }
.fb-row .fb-field { flex:1; }
.fb-check { display:flex; align-items:center; gap:7px; font-size:12px; margin-bottom:12px; }
.fb-check input { width:auto; }
.fb-opts-edit textarea { min-height:88px; font-family:ui-monospace,Menlo,monospace;
  font-size:11.5px; }
.fb-danger { width:100%; margin-top:6px; border:1px solid var(--ink-bad);
  background:transparent; color:var(--ink-bad); border-radius:9px; padding:8px;
  font-size:11.5px; font-weight:700; cursor:pointer; }
.fb-danger:hover { background:var(--tint-red); }

/* The consent line has stopped saying something the mobile carriers were told
   it says. Loud on purpose: it is the one edit on this page that can cost us
   the right to text anybody at all. */
.fe-warn { border:1px solid var(--ink-warn); background:var(--tint-amber);
  border-radius:9px; padding:9px 11px; margin:2px 0 12px; font-size:11px; line-height:1.55; }
.fe-warn strong { display:block; color:var(--ink-warn); font-size:11.5px; margin-bottom:5px; }
.fe-warn ul { margin:0 0 6px; padding-left:15px; }
.fe-warn li { margin:2px 0; }
.fe-warn span { color:var(--subtle); }
.fb-none { font-size:11.5px; color:var(--subtle); line-height:1.55; }

.fb-modal { position:fixed; inset:0; background:rgba(0,0,0,.5); z-index:70;
  display:flex; align-items:center; justify-content:center; padding:24px; }
.fb-preview { width:min(620px,100%); max-height:88vh; display:flex; flex-direction:column;
  background:var(--panel); border:1px solid var(--line); border-radius:16px;
  box-shadow:var(--shadow-lg); overflow:hidden; }
.fb-preview header { display:flex; align-items:baseline; gap:10px; padding:14px 18px;
  border-bottom:1px solid var(--line); }
.fb-preview header .link-btn { margin-left:auto; }
.fb-preview-body { padding:22px 26px 28px; overflow-y:auto; }
.fb-pv-bar { height:3px; border-radius:3px; background:var(--lift-3); margin-bottom:20px; }
.fb-pv-bar i { display:block; height:100%; border-radius:3px; background:var(--blue); }
.fb-pv-h { font-size:17px; font-weight:800; letter-spacing:-.015em; margin:22px 0 3px; }
.fb-pv-h:first-child { margin-top:0; }
.fb-pv-sub { font-size:11.5px; color:var(--muted); margin-bottom:10px; line-height:1.5; }
.fb-pv-q { margin-bottom:16px; }
.fb-pv-l { font-size:12.5px; font-weight:700; margin-bottom:6px; }
/* One column below this, and the viewport-height cage has to come off with
   it. Three panes stacked inside a fixed height give each one a sliver --
   the palette showed one field and the form showed only its title. Let the
   page scroll instead. */
@media (max-width:1100px) {
  .fb { height:auto; min-height:0; }
  .fb-panes { grid-template-columns:1fr; }
  .fb-palette, .fb-settings { max-height:none; overflow-y:visible; }
  .fb-canvas { overflow-y:visible; }
}

.oo-row-edit { text-decoration:none; }
.oo-row-head .pill { margin-left:2px; flex:0 0 auto; }

/* ---- The application form editor (application_editor.html) ---- */
.fe-h3-gap { margin-top:18px !important; }
.fe-sheet { max-width:680px; padding:18px 20px 24px; }
.fe-screen-tag { display:inline-block; font-size:9.5px; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:6px; }
.fe-eyebrow { font-size:10px; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-bad); margin-bottom:6px; }
.fe-headline { font-size:18px; font-weight:800; letter-spacing:-.02em; line-height:1.2; margin:0 0 6px;
  text-wrap:balance; }
.fe-para { font-size:12px; color:var(--muted); line-height:1.5; margin:0 0 6px; }
.fe-para.strong { color:var(--text); font-weight:600; }
.fe-wants { margin:4px 0 8px; padding:0; list-style:none; }
.fe-wants li { font-size:12px; line-height:1.5; padding-left:16px; position:relative; }
.fe-wants li::before { content:"✓"; position:absolute; left:0; color:var(--ink-bad); font-weight:800; }
.fe-pill { display:inline-block; font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  background:var(--ink-bad); color:#fff; padding:6px 12px; border-radius:20px; margin-top:6px; }
.fe-pill.quiet { background:var(--lift-3); color:var(--muted); }
.fe-n { font-size:10.5px; font-weight:800; color:var(--ink-bad); letter-spacing:.06em; margin-right:6px; }
.fe-opt-end { margin-left:auto; font-size:9.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-warn); }
.fe-fields { display:grid; grid-template-columns:1fr auto; gap:6px 12px; margin-top:6px; }
.fe-fields > div { grid-column:1 / -1; }
.fe-fields > div.short { grid-column:auto; width:110px; }
.fe-fields > div:has(+ div.short) { grid-column:auto; }
.fe-fields b { display:block; font-size:9.5px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted);
  margin-bottom:3px; }
.fe-consent { font-size:10.5px; color:var(--subtle); line-height:1.45; margin-top:8px; padding-left:20px; position:relative; }
.fe-consent::before { content:""; position:absolute; left:0; top:2px; width:11px; height:11px; border:1.5px solid var(--line-strong); border-radius:3px; }
.fe-steps { margin:6px 0 0; padding-left:18px; font-size:12px; line-height:1.5; }
.fe-steps li { margin-bottom:4px; }
.fe-steps li span { color:var(--muted); }
.fe-divider { font-size:9.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--subtle);
  margin:16px 0 8px; padding-top:12px; border-top:1px solid var(--line); }
.fe-history { display:grid; gap:6px; }
.fe-version { display:flex; align-items:center; gap:8px; font-size:11.5px; padding:7px 9px;
  border:1px solid var(--line); border-radius:9px; background:var(--lift-1); }
.fe-version span { flex:1; min-width:0; line-height:1.35; }
.fe-version small { display:block; color:var(--subtle); font-size:10px; }
.fe-version button { border:0; background:var(--panel); color:var(--text); font-size:10.5px; font-weight:700;
  padding:4px 8px; border-radius:7px; cursor:pointer; box-shadow:var(--shadow-sm); }
.fe-version button:hover { background:var(--lift-3); }
.fe-sub { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
.fe-sub b { font-size:11.5px; flex:1; }
.fe-sub button { border:0; background:var(--lift-2); color:var(--muted); width:22px; height:22px; border-radius:6px;
  cursor:pointer; font-size:11px; }
.fe-sub button:hover { color:var(--text); background:var(--lift-3); }
.fe-sub button.rm:hover { color:var(--ink-bad); }
.fe-subcard { border:1px solid var(--line); border-radius:10px; padding:10px 10px 4px; margin-bottom:8px; background:var(--lift-1); }
.fe-add { width:100%; border:1px dashed var(--line-strong); background:transparent; color:var(--muted);
  border-radius:9px; padding:7px; font-size:11.5px; font-weight:700; cursor:pointer; margin-bottom:12px; }
.fe-add:hover { color:var(--text); border-color:var(--blue); }
.fe-preview { width:min(1040px, 100%); height:min(860px, 92vh); }
.fe-preview header { align-items:center; }
.fe-preview header .fb-seg { margin-left:auto; }
.fe-preview header .link-btn { margin-left:0; }
.fe-preview-body { flex:1; min-height:0; background:#07090a; display:flex; justify-content:center; }
.fe-preview-body iframe { width:100%; height:100%; border:0; background:#07090a; }
.fe-preview-body.mobile iframe { width:392px; }

.msg-person-last.is-new { color:var(--text); font-weight:650; }

/* Apps & Forms: who has opened an application, and its note thread. */
.ap-seen { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:12px; }
.ap-seen-one { display:inline-flex; align-items:baseline; gap:5px; font-size:11.5px; font-weight:700; padding:4px 9px; border-radius:20px;
  background:var(--lift-2); color:var(--muted); border:1px solid var(--line); }
.ap-seen-one.is-seen { background:var(--tint-green); color:var(--ink-good); border-color:transparent; }
.ap-seen-one small { font-weight:500; font-size:10.5px; opacity:.85; }
.ap-seen-mini { display:inline-block; font-size:11px; font-weight:700; color:var(--ink-good); white-space:nowrap; }
.ap-oldnote { margin:14px 0 0; font-size:12px; color:var(--muted); padding:8px 10px; background:var(--lift-1); border-radius:8px; }
.ap-notes-h { margin:18px 0 2px; font-size:13px; }
#apNoteThread { margin-top:8px; }
#apNoteThread .cmt-log { max-height:320px; overflow-y:auto; }
.ap-status-row { display:inline-flex; align-items:center; gap:8px; }
.ap-meta { margin-top:10px; font-size:11px; }
.ap-unreviewed { font-size:10.5px; font-weight:700; color:var(--ink-warn); }
.ap-row.is-unreviewed td:first-child { box-shadow: inset 3px 0 0 var(--ink-warn); }

/* Weekly scorecard: the week's numbers, from the data. */
.sc-nums { display:grid; grid-template-columns:repeat(auto-fit, minmax(120px, 1fr)); gap:8px; margin-top:12px; }
.sc-num { background:var(--lift-1); border:1px solid var(--line); border-radius:var(--radius); padding:10px 12px; }
.sc-num b { display:block; font-size:20px; font-weight:750; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
.sc-num span { display:block; margin-top:2px; font-size:11px; color:var(--muted); }

/* The contract schedule's table, the one survivor of the client record
   page that was deleted on 09-30-2026 -- client_panel.js still draws
   `.cd-table cp-sched` on the Payments tab. */
.cd-table th, .cd-table td { padding:6px 8px; font-size:12px; }

/* Skip to the content --------------------------------------------------
   The sidebar, favourites and the search pill are 60 tab stops before the
   first control on the page. Hidden until it is focused, which is the one
   moment anybody wants it. Audit, 09-22-2026. */
.skip-link { position:absolute; left:-9999px; top:0; z-index:200; }
.skip-link:focus { left:12px; top:12px; width:auto; height:auto; padding:10px 16px;
  border-radius:10px; background:var(--blue-solid); color:#fff; font-size:13px;
  font-weight:700; box-shadow:var(--shadow-lg); outline:2px solid #fff; outline-offset:2px; }

/* Said by a screen reader, seen by nobody: a heading a sighted person gets
   from the layout, a column header that is only a row of buttons. */
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0; }

/* A button that reads as text ------------------------------------------
   Used where a row cannot be the control but its name can: the inbox thread
   list, the prospect list. Looks exactly like the span it replaced; behaves
   like the button it now is. Audit, 09-22-2026. */
.as-text { appearance:none; border:0; background:none; padding:0; margin:0; font:inherit;
  color:inherit; text-align:left; cursor:pointer; letter-spacing:inherit; }
.as-text:hover { text-decoration:underline; text-underline-offset:2px; }
/* The Documents fold's Upload, no longer inside the summary. */

/* A link inside a sentence -------------------------------------------
   Colour alone does not distinguish it for anyone who cannot see the
   difference, and the blue against the body ink was 1.8:1. Underlined where
   it sits in running text; buttons, chips and nav links are unaffected
   because they are not in a text block. Audit, 09-22-2026. */
.card a:not(.button):not(.chip):not(.tb-btn):not(.as-text),
td a:not(.button):not(.chip), p a:not(.button):not(.chip),
.caveat a:not(.button):not(.chip), .hint a:not(.button):not(.chip),
.helper a:not(.button):not(.chip) {
  text-decoration:underline; text-underline-offset:2px;
  text-decoration-color:color-mix(in srgb, currentColor 45%, transparent); }

/* A chat is the screen, on a phone -------------------------------------
   The thread fills what is there and the box sits on the bottom edge; while
   the keyboard is up the tab bar steps out of the way and the box rides on
   top of the keyboard, which is what every messaging app does. The heights
   come from common.js, which measures the visual viewport rather than
   guessing at the browser's bars. Stefan, 09-22-2026. */
@media (max-width:700px) {
  /* Nothing behind the chat scrolls: the page itself is still, and only the
     thread moves -- otherwise the top bar slides away mid-conversation. */
  body.chat-open { overflow:hidden; }
  body.chat-open main { padding-bottom:0; }
  body.kb-open .tabbar { display:none; }
  /* The person card sheet ends at the keyboard rather than under it. */
  body.kb-open.ws-card-open .ws4:not(.nobody) .ws-card { bottom:0; }
  /* No page-bottom padding stealing room from the box. */
  body.chat-open .app-footer, body.chat-open .subnav { display:none; }
  body.chat-open.ibx-open .ibx-head { padding-block:8px; }
  /* iOS zooms a page when an input under 16px is tapped, which is its own
     kind of white space when it zooms back out. */
  .ws-compose textarea, .msg-box textarea { font-size:16px; }
}

/* ---------------------------------------------- the client card, beside the calls
   Stefan, 09-22-2026: "when opening up the client card on this page, it should
   push everything to the left and not cover it. Just like the brain operates."
   So it is the Brain's arrangement exactly -- fixed to the right, main padded
   by its width -- rather than an overlay like the brief panel, which hides the
   list you were just reading. Below 1100px there is no room to push anything,
   so it covers, the way the Brain does. */
/* ONE number for how wide the client panel is. It was two: the panel was
   widened from 440 to 620 on 09-24 ("need to resize this card so everything
   fits") and the rule that pushes the page over was left at 456 -- so the
   panel overlapped the list it had pushed, by 164px. Stefan, 09-25-2026:
   "it's not opening up like the brain. It's covering the card when it should
   be pushing it to the left." Both rules read this now, so they cannot drift
   apart again. */
:root { --cp-w: min(620px, calc(100vw - 40px)); }

.cp-panel {
  /* 440px was too narrow for what this card holds: a five-column payment
     schedule with two date pickers in it. Stefan, 09-24-2026: "need to
     resize this card so everything fits." A sliver of the page stays visible
     behind it, so it still reads as a panel over the list rather than a page
     of its own. */
  position: fixed; top: 0; right: 0; bottom: 0;
  width: var(--cp-w);
  background: var(--panel, var(--card, #fff)); border-left: 1px solid var(--line);
  z-index: 40; display: flex; flex-direction: column; overflow: hidden;
}
.cp-panel[hidden] { display: none !important; }
/* ABOVE THE PHONE'S TAB BAR, not under it. The bar is fixed at z-index 60 and
   the panel at 40, so on a phone the bar was drawn over the bottom of every
   panel -- the client card's last lines, and the Team panel's Save button,
   which could not be pressed at all. Found 10-15-2026. --tabbar-h is 0 on a
   desktop, so nothing changes there. */
.cp-panel { bottom: var(--tabbar-h, 0px); }
.cp-panel-head {
  display: flex; align-items: flex-start; gap: 12px; justify-content: space-between;
  padding: 16px 18px 12px; border-bottom: 1px solid var(--line);
}
.cp-panel-head h3 { margin: 0; }
.cp-panel-body { flex: 1 1 auto; overflow-y: auto; padding: 16px 18px 28px; }
/* The same body, drawn into a card on the client's own page instead of into
   the panel. It keeps the field grid and the schedule's styling and drops
   the panel's chrome: a card does not scroll inside itself, and the card
   already has the padding. */
.cp-panel-body.cp-inline { flex: none; overflow: visible; padding: 0; }
.cp-panel-body.cp-inline > h4:first-child { margin-top: 0; }
.cp-panel-body .field-grid { display: grid; grid-template-columns: 1fr 1fr;
  gap: 10px 12px; align-items: start; }
/* ONE COLUMN ON A PHONE. The two never collapsed, so on a 375px screen the
   agreement form -- the programme, the payment type, the processor, every
   date -- ran about 25px past the right edge and was clipped there, with no
   way to scroll to it. Half of every dropdown on the right was simply
   missing. Found 10-14-2026 auditing the app at phone width. */
@media (max-width:700px) {
  .cp-panel-body .field-grid { grid-template-columns: 1fr; }
}
/* Three quiet headings over the groups, with a hairline above each. What made
   this form read as "crudely copied and pasted" was that it was one flat run
   of eighteen boxes -- so a heading every few rows is most of the fix, and it
   costs a line of type. */
.cp-panel-body .field-grid .cp-sub {
  grid-column: 1 / -1; margin: 6px 0 -2px; padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 10px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--subtle); }
/* The sentence beside Length. Aligned to the bottom of the row so it sits
   level with the select rather than floating above it. */
.cp-panel-body .cp-termnote { align-self: end; display: flex; flex-direction: column;
  gap: 3px; padding-bottom: 2px; }
.cp-panel-body .cp-termnote p { margin: 0; }
.cp-panel-body .cp-termnote p:empty { display: none; }
/* The save row. The status sits beside the button, not under it, so "unsaved
   changes" is read in the same glance as the thing that fixes it. */
.cp-saverow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cp-saverow .helper { margin: 0; }
/* And while there is something to save, the button says so. */
.cp-needs-save { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink-warn) 28%, transparent); }
/* How they're paying: four answers across the width, labelled like every
   other box, with the sentence under them saying what the pick means. */
.cp-panel-body .field-grid .cp-label { display: block; font-size: 12px; margin-bottom: 3px; }
.cp-arr { display: flex; width: 100%; }
.cp-arr button { flex: 1 1 auto; }
.cp-panel-body #cpArrSaid { display: block; margin-top: 5px; }
/* After a close is saved, the next thing: their agreement, while they are
   still on the call. */
.cp-next { margin-top: 12px; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 8px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: color-mix(in srgb, var(--blue) 6%, var(--panel)); }
.cp-next[hidden] { display: none; }
.cp-next p { margin: 0; flex: 1 1 240px; font-size: 13px; }
.cp-panel-body .field-grid > .cp-wide { grid-column: 1 / -1; display:flex; flex-direction:column;
                                        gap:2px; padding:2px 0; }
.cp-panel-body .field-grid .full { grid-column: 1 / -1; }
.cp-derived {
  display: flex; gap: 18px; flex-wrap: wrap; margin: 12px 0 4px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2, transparent);
}
.cp-derived b { display: block; font-size: 18px; font-variant-numeric: tabular-nums; }
.cp-derived span { font-size: 12px; color: var(--muted); }
/* The panel's width plus main's own 26px gutter, so the list stops where
   the panel starts instead of sliding under it. */
@media (min-width: 1100px) { body.cp-open main { padding-right: calc(var(--cp-w) + 26px); } }

/* THE CARD MOVES OVER FOR THE PANEL.

   That rule shifts `main`, and the client card is not in `main` -- it is a
   fixed overlay centred on the window. So opening an agreement over an open
   card put the panel across the card's right-hand edge, esc included.
   Stefan, 10-01-2026: "it's kind of cutting off the right side of the card,
   like the escape button ... maybe we make the agreement box a little more
   narrow, or move the client card over to the left on that view."

   Both, by width. Wide enough for the pair (620 panel + 760 card + gaps) and
   the card slides left with everything still readable. Narrower than that and
   it cannot slide far enough, so the card gives up width instead -- which is
   the right thing to lose, because the agreement is the thing being filled
   in. Below 1100px the panel is nearly the whole screen anyway and the card
   is behind it, which is the phone behaviour and already correct. */
@media (min-width: 1420px) {
  body.cp-open #noteOverlay { padding-right: calc(var(--cp-w) + 26px); }
}
@media (min-width: 1100px) and (max-width: 1419.98px) {
  body.cp-open .note-dialog { width: min(760px, calc(100vw - var(--cp-w) - 56px)); }
}

/* The Prospects Inbox is a special case, because it is four columns and
   three of them are a FIXED width -- 236 + 320 + 360. Pushing the page over
   by the panel left those 916px of column about 600px to share, so they
   landed on top of each other. Stefan, 09-25-2026: "when we open up Jin's
   full card, it lays over and puts everything out of order ... I do want the
   full card view. I just wanted to push everything to the left properly."

   While the card is open the inbox IS the conversation -- he has just closed
   a deal in it, and the folder rail, the list and the person card are all
   ways of getting to a thread he is already in. They come back with the
   card. Only where the push applies at all, or a phone would lose its inbox
   to a panel that is covering the screen anyway. */
@media (min-width: 1100px) {
  /* !important, deliberately. The inbox sets its own columns from about a
     dozen rules across five breakpoints and four folded states -- cardless,
     guide-fold, nobody, noting -- several of them carrying a four-class
     :not() chain that outranks anything reasonable written from here. This
     is a PAGE STATE overriding a component's own layout for as long as the
     state lasts, which is the case !important is actually for; matching
     every one of those selectors would be a dozen more rules that go stale
     the next time a fold is added. */
  body.cp-open .ws4 { grid-template-columns: minmax(0, 1fr) !important; }
  body.cp-open .ws4 .ws-rail,
  body.cp-open .ws4 .ws-list,
  body.cp-open .ws4 .ws-card { display: none !important; }
}

/* The payment schedule inside the client panel. Narrow on purpose: it lives
   in a 440px column, so the inputs earn their width and nothing wraps. */
.cp-sched { font-size: 13px; }
.cp-sched th, .cp-sched td { padding: 6px 6px 6px 0; vertical-align: middle; }
.cp-sched td:first-child { width: 18px; color: var(--muted); }
.cp-sched input { width: 100%; min-width: 0; padding: 5px 7px; font-size: 13px; }
.cp-sched input[type="date"] { padding-right: 2px; }
.cp-sched td:last-child { width: 26px; }
/* The schedule fits the panel. It was laying out at 650px inside a 403px
   panel and scrolling 249px sideways, which put Collected and Remove off
   the edge where nobody would ever find them -- Stefan, 09-24-2026, could
   see two columns of a five-column table. Fixed layout, so the two inputs
   share whatever is left after the fixed bits rather than demanding their
   natural width. */
/* The global `table { min-width: 650px }` is what was really doing it: a
   fixed layout and width:100% cannot beat a min-width, so the table sat at
   650px in a 403px panel however the columns were sized. Inside this panel
   the schedule has to fit the panel. */
.cp-panel-body .cp-sched { width: 100%; min-width: 0; table-layout: fixed; }
.cp-panel-body .cp-sched th:first-child,
.cp-panel-body .cp-sched td:first-child { width: 16px; padding-right: 4px; }
/* Both date columns get a real date's worth of room, and the amount takes
   what is left. Splitting Amount and Due evenly gave Due 189px it did not
   need while Collected sat at 118 and clipped its own value -- Stefan,
   09-24-2026: "still cut off a bit ... make it smaller, pinch it in". A
   native date control needs room for MM-DD-YYYY AND its picker button. */
.cp-panel-body .cp-sched th:nth-child(3),
.cp-panel-body .cp-sched td:nth-child(3),
.cp-panel-body .cp-sched th:nth-child(4),
.cp-panel-body .cp-sched td:nth-child(4) { width: 150px; }
/* And the whole thing a shade tighter, which buys the dates their room. */
.cp-panel-body .cp-sched { font-size: 12.5px; }
.cp-panel-body .cp-sched th, .cp-panel-body .cp-sched td { padding: 5px 5px 5px 0; }
.cp-panel-body .cp-sched input { padding: 4px 6px; font-size: 12.5px; }
/* 24px, with a 27px button in it, was the last 3px of sideways scroll. */
.cp-panel-body .cp-sched th:last-child,
.cp-panel-body .cp-sched td:last-child { width: 30px; padding-right: 0; }

/* The full-card overlay that briefly lived here is gone: the card opens in
   the roster's own push-left panel now (common.js), which is the one Stefan
   works in. Its `.cc-head` also collided with Contract check's, which is a
   different thing entirely. */
.cp-panel-body .cp-sched input[type="checkbox"] { width: auto; }
.cp-panel-body .cp-sched th { font-size: 9.5px; letter-spacing: .05em; }
/* Label heights differ -- "Then how many after the deposit" wraps to three
   lines while "One every" is one -- so without this the two fields in a row
   sat 21px apart. Stefan: "needs to be in alignment ... move down". Pushing
   every field to the bottom of its cell lines up every pair in the grid,
   whatever the label above it does. */
.cp-panel-body .field-grid > div { display: flex; flex-direction: column; }
.cp-panel-body .field-grid > div > input,
.cp-panel-body .field-grid > div > select { margin-top: auto; }
.cp-panel-body .field-grid label { display: block; font-size: 12px; margin-bottom: 3px; }
.cp-panel-body .field-grid label .helper { font-weight: 400; }
.cp-panel-body .field-grid input, .cp-panel-body .field-grid select { width: 100%; }
/* ...except a tick, which is not a field you fill in. The 100% above was
   making the "Onboarding call already booked with the coach" checkbox 403px
   wide -- the whole row -- which pushed its own label clean out of the panel
   and gave the panel a horizontal scrollbar. Stefan, 09-24-2026: "I shouldn't
   have to have the option to scroll left and right. This text is also pushed
   out way to the right." */
.cp-panel-body .field-grid input[type="checkbox"],
.cp-panel-body .field-grid input[type="radio"] { width: auto; flex: 0 0 auto; }
.cp-panel-body .field-grid label.hstack,
.cp-panel-body .field-grid label.pr-check { display: flex; align-items: flex-start;
  gap: 8px; flex-wrap: nowrap; }
.cp-panel-body .field-grid label.hstack > span,
.cp-panel-body .field-grid label.pr-check > span { min-width: 0; }
/* And nothing in the panel may make it scroll sideways. A form that slides
   under your cursor is a form you mis-read. */
.cp-panel-body { overflow-x: hidden; }
.cp-panel-body .table-wrap { max-width: 100%; }
.cp-panel-body .pr-check { display: flex; gap: 8px; align-items: flex-start; font-size: 12.5px; }
.cp-panel-body .pr-check input { width: auto; margin-top: 2px; }

/* A saved contract should be unmistakable: the panel takes the accent for a
   beat. Respects reduced motion, where the flash is simply skipped. */
.cp-panel.cp-just-saved { box-shadow: inset 4px 0 0 var(--green, #1f7a4d); transition: box-shadow .2s; }
@media (prefers-reduced-motion: reduce) { .cp-panel.cp-just-saved { transition: none; } }

/* A field OfferOps filled in rather than a person: marked, so it is obvious
   what to check before saving, and it stops looking filled once touched. */
.edit-row input.prefilled, .edit-row select.prefilled {
  background: color-mix(in srgb, var(--blue) 7%, transparent);
  border-color: color-mix(in srgb, var(--blue) 35%, var(--line));
}
.edit-row input.prefilled:focus, .edit-row select.prefilled:focus { background: transparent; }
.edit-row .prefill-note { margin: 0 0 10px; }

/* An attribution OfferOps worked out rather than one somebody typed: same
   badge, dotted edge, so the card never passes a derivation off as a record.

   And carrying the weekly scorecard's keyword treatment -- blue, heavy,
   uppercase, letter-spaced. Stefan, 09-30-2026: "make the keywords pop a
   little more similar to the Ads weekly scorecard." It is the same fact on
   both pages and it was reading as another grey chip here, lost in a row of
   five. The dashed edge stays: popping is about finding it, not about
   pretending it was typed. */
.badge.ad-from { border-style: dashed; }
/* The pop is for a badge that NAMES something -- a keyword we have, which is
   also the one you can press. A creative whose keyword we never caught says
   "Unlabeled", and in blue it drew the eye hardest of anything on the row to
   the one badge carrying no answer. A gap should not shout. */
.badge.ad-from.is-filter {
  border-color: color-mix(in srgb, var(--blue) 42%, transparent);
  color: var(--blue); background: color-mix(in srgb, var(--blue) 10%, transparent);
  font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }
/* The ones that filter the board get the hand and a solid edge on hover, so
   pressable and merely-blue are not the same look. */
.badge.ad-from.is-filter:hover { border-style: solid;
  background: color-mix(in srgb, var(--blue) 18%, transparent); }

/* The one form card (formcard.js), over the page rather than beside it: it
   is read and put down. Above the side panels (a brief is 65) because it is
   opened from inside them; under the date picker and the @-mention list,
   which open from inside it. */
/* The applicant's name, as the way to their lead card. Reads as the heading
   it replaces, with the chevron saying it goes somewhere. */
.fv-name-link { font:inherit; color:inherit; background:none; border:0; padding:0;
  cursor:pointer; display:inline-flex; align-items:center; gap:6px; text-align:left; }
.fv-name-link span { color:var(--muted); font-weight:500; transition:transform .14s ease; }
.fv-name-link:hover { color:var(--blue); }
.fv-name-link:hover span { color:var(--blue); transform:translateX(2px); }
.fv-name-link:focus-visible { outline:2px solid var(--blue); outline-offset:3px; border-radius:4px; }
.formview { position: fixed; inset: 0; z-index: 70; background: rgba(8,10,14,.45);
  display: grid; place-items: center; padding: 24px; }
.formview[hidden] { display: none !important; }
.formview-card { background: var(--panel, #fff); border: 1px solid var(--line); border-radius: 12px;
  width: min(680px, 100%); max-height: min(86vh, 920px); display: flex; flex-direction: column; overflow: hidden; }
.formview-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;
  padding: 16px 20px 12px; border-bottom: 1px solid var(--line); }
.formview-head h3 { margin: 0; }
.formview-body { overflow-y: auto; padding: 16px 20px 22px; }
.formview-qa { margin: 0; }
.formview-qa dt { font-size: 12.5px; color: var(--muted); margin-top: 14px; }
.formview-qa dt:first-child { margin-top: 0; }
.formview-qa dd { margin: 3px 0 0; font-size: 15px; color: var(--text); white-space: pre-wrap; }
@media (max-width: 600px) { .formview { padding: 0; } .formview-card { max-height: 100%; height: 100%; border-radius: 0; } }
/* How to reach them, in the card's head so it stays put while the answers
   scroll. One click on a value takes all of it, for the day the clipboard
   refuses. */
.fv-reach { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; }
.fv-reach-item { display: inline-flex; align-items: center; gap: 8px; min-width: 0; max-width: 100%; }
.fv-reach-k { font-size: 10.5px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--subtle); }
.fv-reach-v { font-size: 14px; color: var(--text); user-select: all; overflow-wrap: anywhere; min-width: 0; }
.fv-copy { cursor: pointer; flex: 0 0 auto; }
.fv-from { margin: 3px 0 0; }
.fv-from b { color: var(--text); font-weight: 600; }
.fv-from a { color: var(--ink-info); }
.fv-switch { margin-top: 10px; }
/* Status, then whatever needs saying about it, then GHL at the far end. */
.fv-work { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; margin: 2px 0 6px; }
.fv-work .ap-status-row { font-size: 12px; color: var(--muted); }
.formview-body .formview-qa { margin-top: 16px; }
/* The form above, the notes about it below: a rule the full width of the card
   (the body's 20px side padding, undone), with room on both sides of it. */
.fv-notes { margin: 28px -20px 0; padding: 22px 20px 0; border-top: 1px solid var(--line); }
.fv-notes .ap-notes-h { margin: 0 0 2px; font-size: 15px; }
.fv-notes .ap-oldnote { margin: 10px 0 12px; }
/* Pasting a client profile in: the box stands in for the profile itself, so
   it gets the room the profile would have had. */
.onb-paste { width: 100%; min-height: 240px; font: 12.5px/1.6 var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--panel); color: var(--text); resize: vertical; }
.onb-paste:focus { outline: none; border-color: color-mix(in srgb, var(--blue) 55%, transparent); }
/* A preference row on the Account page: the checkbox, then what it means. */
.pushpref { display: flex; align-items: flex-start; gap: 9px; cursor: pointer; font-size: 13px; }
.pushpref input { margin-top: 2px; flex: 0 0 auto; }
.pushpref .helper { display: block; margin-top: 2px; }
/* A closed form: when, who, and what for, in place of the status. Green
   because it is the good outcome, the same tint as a "viewed" tick. */
.fv-closed { margin: 0 0 12px; padding: 12px 14px 13px; border-radius: 10px; background: var(--tint-green);
  border: 1px solid color-mix(in srgb, var(--ink-good) 24%, transparent); }
.fv-closed-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; font-size: 12.5px; color: var(--muted); }
.fv-closed-mark { font-size: 13px; font-weight: 700; color: var(--ink-good); }
.fv-closed-head a { font-weight: 600; color: var(--ink-info); text-decoration: none; }
.fv-closed-head a:hover { text-decoration: underline; }
.fv-closed-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, .85fr) minmax(0, .85fr);
  gap: 10px 16px; margin: 10px 0 0; }
.fv-closed-grid dt { font-size: 10.5px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--subtle); }
.fv-closed-grid dd { margin: 3px 0 0; font-size: 14px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.fv-closed-grid dd small { display: block; margin-top: 1px; font-size: 11.5px; font-weight: 500; color: var(--muted); }
@media (max-width: 600px) { .fv-closed-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ap-closed-on { font-size: 10.5px; font-weight: 600; color: var(--muted); white-space: nowrap; }

/* ------------------------------------------------ Prospects Inbox: the dock
   The person card folds to a strip of icons, so the conversation has the
   room; each icon opens its one section, the last opens the whole card.
   Stefan, 09-23-2026, pointing at GoHighLevel's right-hand rail -- "not
   exactly like GHL... a cleaner UI". The card and its dock share the grid's
   last column: 48px folded, the card plus 48px open. Tablets and phones keep
   the sheet they had. */
.ws-card { flex-direction:row; }
.ws-card-main { flex:1 1 auto; min-width:0; min-height:0; display:flex; flex-direction:column; }
.ws-dock { flex:0 0 48px; display:flex; flex-direction:column; align-items:center; gap:4px;
           padding:10px 0; border-left:1px solid var(--line); background:var(--panel-2); }
.ws-dock-btn { position:relative; width:36px; height:36px; display:grid; place-items:center; padding:0;
               border:0; border-radius:10px; background:transparent; color:var(--muted); cursor:pointer;
               transition:background .12s ease, color .12s ease; }
.ws-dock-btn:hover { background:var(--lift-2); color:var(--text); }
.ws-dock-btn:focus-visible { outline:2px solid var(--blue); outline-offset:1px; }
.ws-dock-btn.active { background:color-mix(in srgb, var(--blue) 14%, transparent); color:var(--blue); }
.ws-dock-badge { position:absolute; top:0; right:-2px; min-width:15px; height:15px; padding:0 4px;
                 border-radius:8px; font-size:9.5px; font-weight:750; line-height:13px; text-align:center;
                 background:var(--panel); color:var(--muted); border:1px solid var(--line-strong);
                 font-variant-numeric:tabular-nums; pointer-events:none; }
.ws-dock-badge.warn { background:var(--ink-warn, #c77a12); color:#fff; border-color:transparent; }
.ws-dock-badge.dot { min-width:0; width:8px; height:8px; padding:0; top:5px; right:5px; }
.ws-dock-badge.dot:not(.warn) { background:var(--blue); border-color:transparent; }
/* "Client view" on the check-in Forms list: the real client page in a frame,
   at the width a client actually holds it. Phone first, because that is where
   a check-in gets filled in -- on a shift, on bad signal. */
.ci-view { margin-top:18px; border:1px solid var(--line); border-radius:12px;
  background:var(--card); overflow:hidden; }
.ci-view-head { display:flex; align-items:flex-start; justify-content:space-between;
  gap:14px; flex-wrap:wrap; padding:14px 16px; border-bottom:1px solid var(--line); }
.ci-view-head h3 { margin:0; }
.ci-view-head .card-sub { margin:3px 0 0; }
.ci-seg { display:inline-flex; border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.ci-seg button { border:0; background:transparent; color:var(--muted); font:inherit;
  font-size:12.5px; padding:6px 12px; cursor:pointer; }
.ci-seg button.on { background:color-mix(in srgb, var(--blue) 15%, transparent); color:var(--blue); }
.ci-view-stage { display:grid; place-items:center; padding:20px 16px;
  background:var(--lift-1, rgba(255,255,255,.02)); }
.ci-view-stage iframe { width:390px; height:760px; max-width:100%; border:1px solid var(--line-strong);
  border-radius:22px; background:#07090a; display:block; }
/* Desktop: the same page at a width a laptop gives it. The frame loses the
   phone's rounded shell, because that shell is a lie about a browser window. */
.ci-view-stage[data-w="desktop"] iframe { width:100%; max-width:1040px; height:720px;
  border-radius:8px; }
@media (max-width:520px) { .ci-view-stage iframe { height:620px; } }

.ws-dock-sep { width:22px; height:1px; background:var(--line); margin:4px 0; }
/* The client card's dock, in two halves.
   Above the line is the admin -- who is talking to them, what they signed,
   what they paid. Below it is the coaching. Stefan, 10-07-2026: "there's
   coordination and color separation ... the internal messaging already has the
   gradient blue that I'm looking for."

   So both halves wear the SAME treatment -- the app's 135deg gradient, at the
   same weight -- in two hues, and which half you are in is readable without
   hovering anything. The admin half is the blue-to-violet the brand mark and
   every grad-outline control already use; the coaching half slides green
   towards that same blue, so the two read as siblings rather than as two
   design systems sharing a strip. */
.ws-dock-btn.admin-side { color:color-mix(in srgb, var(--blue) 68%, var(--muted)); }
.ws-dock-btn.admin-side:hover {
  background:linear-gradient(135deg, color-mix(in srgb, var(--blue) 15%, transparent),
                                     color-mix(in srgb, var(--violet) 15%, transparent));
  color:var(--blue); }
.ws-dock-btn.admin-side.active {
  background:linear-gradient(135deg, color-mix(in srgb, var(--blue) 20%, transparent),
                                     color-mix(in srgb, var(--violet) 20%, transparent));
  color:var(--blue); }

.ws-dock-btn.coach-side { color:color-mix(in srgb, var(--ink-good) 68%, var(--muted)); }
.ws-dock-btn.coach-side:hover {
  background:linear-gradient(135deg, color-mix(in srgb, var(--ink-good) 15%, transparent),
                                     color-mix(in srgb, var(--blue) 13%, transparent));
  color:var(--ink-good); }
.ws-dock-btn.coach-side.active {
  background:linear-gradient(135deg, color-mix(in srgb, var(--ink-good) 20%, transparent),
                                     color-mix(in srgb, var(--blue) 17%, transparent));
  color:var(--ink-good); }
.ws-dock-btn.coach-side:focus-visible { outline-color:var(--ink-good); }
/* The app's own tooltip (see [data-tip] above), hung to the left of the
   icon instead of under it -- under it, the dock's labels ran off the right
   edge of the window. Wraps, because these say what the section does. */
.ws-dock-btn[data-tip]::after {
  top:50%; bottom:auto; left:auto; right:calc(100% + 10px);
  transform:translate(4px, -50%); white-space:normal; width:max-content; max-width:240px;
  line-height:1.35; font-weight:500; text-align:left; padding:7px 10px; z-index:80; }
.ws-dock-btn[data-tip]::before {
  top:50%; bottom:auto; left:auto; right:calc(100% + 1px); transform:translateY(-50%);
  border:5px solid transparent; border-left-color:var(--line-strong); z-index:81; }
.ws-dock-btn[data-tip]:hover::after, .ws-dock-btn[data-tip]:focus-visible::after { transform:translate(0, -50%); }
@media (prefers-reduced-motion: reduce) {
  .ws-dock-btn[data-tip]::after, .ws-dock-btn[data-tip]:hover::after { transition:none; transform:translate(0, -50%); } }
/* The dock's own edge must not clip the label that sticks out of it. */
.ws-card, .ws-dock { overflow:visible; }
.ws-card-focus { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:12px 14px 2px; }
.ws-card-focus b { font-size:13px; letter-spacing:-.01em; }
.ws-card-focus[hidden], .ws-sec[hidden] { display:none; }
.ws-card[data-focus] .ws-card-id { display:none; }
.ws-card[data-focus] .ws-sec { border-top:0; }
.ws-card[data-focus] .ws-sec > summary { display:none; }

@media (min-width:1101px) {
  /* Folded: only the dock. */
  .ws4.cardless:not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 320px minmax(0,1fr) 48px; }
  .ws4.cardless:not(.guide-fold):not(.nobody) .ws-card { display:flex; }
  .ws4.cardless:not(.guide-fold) .ws-card-main { display:none; }
  .ws4.noting.cardless:not(.guide-fold):not(.nobody) { grid-template-columns:320px minmax(0,1fr) 340px 48px; }
  /* Open: the card with its dock inside the width it always had -- the dock
     comes out of the card, not out of the conversation. */
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 320px minmax(0,1fr) 360px; }
  .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) { grid-template-columns:320px minmax(0,1fr) 340px 360px; }
}
@media (min-width:1101px) and (max-width:1800px) {
  .ws4.noting.cardless:not(.guide-fold):not(.nobody) { grid-template-columns:262px minmax(0,1fr) 320px 48px; }
  .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) { grid-template-columns:262px minmax(0,1fr) 320px 330px; }
}
@media (min-width:1101px) and (max-width:1560px) {
  .ws4.cardless:not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 262px minmax(0,1fr) 48px; }
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 262px minmax(0,1fr) 330px; }
}
@media (min-width:1101px) and (max-width:1280px) {
  .ws4.cardless:not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 240px minmax(0,1fr) 48px; }
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) { grid-template-columns:var(--ws-rail-w, 176px) 240px minmax(0,1fr) 300px; }
}
@media (max-width:1100px) {
  /* The Prospects Inbox's person card drops its dock here: below 1100px that
     card is a sheet over the thread and the sections are already stacked, so
     a 48px rail is 48px of nothing.

     The CLIENT card borrows these classes and must not go with it. Its dock
     is its ONLY navigation -- notes, payments, the agreement, the coach are
     all behind it -- so hiding it leaves a card open on Internal notes with
     no way to anything else. That is exactly what the roster card looked like
     inside the 780px panel a close opens, which is what made it read as a
     worse card than the one it replaced. It is not; it had no dock.

     `.cc-split .cc-dock` rather than `.cc-dock`: two classes beat the one in
     `.ws-dock` whatever the source order, which is the lesson from the phone
     rule 5,000 lines up this file. The ≤640px block styles this same selector
     and sets no display, so the phone still lays the dock down as a row. */
  .ws-dock { display:none; }
  .cc-split .cc-dock { display:flex; }
  .ws-card { flex-direction:column; }
}

/* ------------------------------------------------- framing a profile picture
   Drag to move, scroll or pinch to zoom, and the circle is the crop. */
.crop-wrap { position:fixed; inset:0; z-index:200; display:grid; place-items:center;
             background:rgba(16,16,20,.55); padding:16px; }
.crop-card { width:min(380px, 100%); background:var(--panel); border:1px solid var(--line);
             border-radius:16px; padding:18px; box-shadow:var(--shadow-lg); }
.crop-card h3 { margin:0 0 4px; }
.crop-stage { position:relative; width:320px; max-width:100%; aspect-ratio:1; margin:14px auto 10px;
              border-radius:12px; overflow:hidden; background:var(--lift-2);
              cursor:grab; touch-action:none; }
.crop-stage:active { cursor:grabbing; }
.crop-stage canvas { display:block; width:100%; height:100%; }
/* The circle: everything outside it is dimmed, so the crop is obvious
   without drawing a second preview of the same face. */
.crop-ring { position:absolute; inset:0; pointer-events:none; border-radius:12px;
             box-shadow:0 0 0 999px rgba(16,16,20,.45) inset;
             -webkit-mask:radial-gradient(circle at 50% 50%, transparent 49.5%, #000 50%);
             mask:radial-gradient(circle at 50% 50%, transparent 49.5%, #000 50%); }
.crop-ring::after { content:""; position:absolute; inset:0; border-radius:50%;
                    box-shadow:0 0 0 2px rgba(255,255,255,.9) inset; }
.crop-tools { display:flex; align-items:center; gap:12px; }
#cropZoom { flex:1 1 auto; }
.crop-tools .link-btn { flex:0 0 auto; font-size:12px; white-space:nowrap; }
/* The circle, at the size it is actually used, so nothing has to be imagined. */
.crop-check { display:flex; align-items:center; gap:10px; margin-top:12px; }
.crop-preview { width:44px; height:44px; flex:0 0 auto; border-radius:50%;
                background-size:cover; background-position:center;
                box-shadow:0 0 0 1px var(--line); }
@media (max-width:420px) { .crop-stage { width:100%; } }

/* ==========================================================================
   A thread, on a phone, is its own screen.

   Stefan, 09-23-2026: "I tried to reply to that thread, and it's all messed
   up... things hide and don't show how they're supposed to." His screenshot
   showed why: at 900px the shell collapses to one column, and the thread
   panel -- written to sit BESIDE the conversation -- became just another
   block in the page, stacked under the composer with the conversation still
   running on beneath it. Two composers on one screen, and the replies
   nowhere near the message they belong to.

   So on a phone it takes the screen, the way opening a thread does in Slack:
   its own header with a way back, its own scrolling body, its own composer
   above the thumb. The conversation is still there, in its place, underneath.
   ========================================================================== */
@media (max-width:700px) {
  /* The app pins itself to the visible area when the keyboard is up
     (body.chat-open, which is also why this fixed box is measured from the
     body): the sheet uses the same height, so it never hangs below the
     screen or behind the keyboard. Fading rather than sliding -- a slide
     leaves the sheet offset if it is ever re-triggered mid-repaint, which
     put the Reply button two pixels off the bottom of the screen. */
  .msg-panel { position:fixed; inset:0; z-index:80; height:var(--vvh, 100dvh);
               max-height:none; border:0; border-radius:0;
               animation:sheetIn .16s ease-out; }
  .msg-panel .msg-panel-head { padding:12px 14px; padding-top:calc(12px + env(safe-area-inset-top, 0px));
                               position:sticky; top:0; }
  .msg-panel .msg-panel-body { flex:1 1 auto; min-height:0; overflow-y:auto;
                               -webkit-overflow-scrolling:touch; overscroll-behavior:contain; }
  .msg-panel .msg-box { flex:0 0 auto; border-top:1px solid var(--line);
                        padding-bottom:calc(10px + env(safe-area-inset-bottom, 0px)); }
  /* The close button is the way back out of a screen, so it is a target a
     thumb can hit rather than a 26px x in a corner. */
  .msg-panel .msg-x { width:38px; height:38px; }
  /* Nothing behind it should scroll while it is open. */
  body.thread-open { overflow:hidden; }
}
@keyframes sheetIn { from { opacity:0; } to { opacity:1; } }

/* An emoji answer from the mentions list -- no opening, no thread. The ones
   already there carry their count; the rest sit quiet until hovered, so a
   list of mentions does not look like a sticker sheet. */
.ibx-reacts { display:flex; flex-wrap:wrap; align-items:center; gap:4px; margin-top:7px; }
.ibx-react { display:inline-flex; align-items:center; gap:4px; padding:2px 7px; min-height:26px;
  border-radius:13px; border:1px solid var(--line); background:var(--panel);
  font-size:13px; line-height:1; cursor:pointer; color:var(--text);
  transition:background .12s ease, border-color .12s ease, opacity .12s ease; }
.ibx-react b { font-size:11px; font-weight:700; font-variant-numeric:tabular-nums; }
.ibx-react:hover { border-color:var(--line-strong); background:var(--lift-2); }
.ibx-react.on-1 { border-color:var(--blue); background:color-mix(in srgb, var(--blue) 12%, transparent); }
.ibx-react.quiet { border-color:transparent; background:transparent; opacity:.42; }
.ibx-card:hover .ibx-react.quiet, .ibx-react.quiet:focus-visible { opacity:1; }
.ibx-react.quiet:hover { opacity:1; border-color:var(--line); background:var(--lift-2); }
@media (hover:none) { .ibx-react.quiet { opacity:.75; } }

/* Talking to a client from their card: their thread, and a box to answer in.
   Their words on the left, ours on the right -- the same reading as every
   other conversation in the app. */
.reach-log { max-height:42vh; min-height:180px; overflow-y:auto; display:flex;
  flex-direction:column; gap:8px; padding:4px 2px 8px; scrollbar-width:thin; }
.reach-turn { display:flex; flex-direction:column; gap:3px; max-width:82%; min-width:0; }
.reach-turn.mine { align-self:flex-end; align-items:flex-end; }
.reach-turn.theirs { align-self:flex-start; }
/* overflow-wrap is not decoration here: GoHighLevel relays an email as text,
   and its picture arrives as a ninety-character address with no space in it.
   Without this the word runs straight out of the bubble and off the card. */
.reach-bubble { padding:9px 12px; border-radius:14px; font-size:13px; line-height:1.5;
  background:var(--lift-2); color:var(--text); overflow-wrap:anywhere; min-width:0;
  max-width:100%; }
.reach-turn.mine .reach-bubble { background:var(--blue); color:#fff; }
.reach-bubble a { color:var(--blue); text-decoration:underline; overflow-wrap:anywhere; }
.reach-turn.mine .reach-bubble a { color:#fff; }
.reach-stamp { font-size:10.5px; color:var(--subtle); }
.reach-file { display:inline-block; margin-top:4px; font-size:11.5px; text-decoration:underline; }

/* A picture that came inside a message, drawn where it stood. Capped so one
   marketing banner cannot fill the whole conversation, and a tap opens it. */
.msg-shot { display:block; margin:6px 0; max-width:min(260px, 100%); border-radius:10px;
  overflow:hidden; line-height:0; border:1px solid var(--line); background:var(--lift-1); }
.msg-shot img { display:block; width:100%; height:auto; }
.msg-shot:first-child { margin-top:0; }
.msg-shot:last-child { margin-bottom:0; }
.msg-shot-gone { display:inline-block; margin:4px 0; font-size:11.5px;
  text-decoration:underline; opacity:.85; }

/* A message too tall for the pane it is read in, folded. --bub is the colour
   behind it, so the fade at the bottom matches whichever bubble it is in. */
.reach-bubble { --bub:var(--lift-2); }
.reach-turn.mine .reach-bubble { --bub:var(--blue); }
.msg-bubble { --bub:var(--panel-2); }
.msg-turn.mine .msg-bubble { --bub:var(--blue-solid); }
.is-clipped { position:relative; overflow:hidden; max-height:var(--clip, 240px); }
.msg-unfold { position:absolute; inset:auto 0 0 0; width:100%; height:auto; border:0;
  cursor:pointer; font:inherit; font-size:11.5px; font-weight:600; letter-spacing:.01em;
  color:inherit; text-align:center; padding:22px 0 8px;
  /* The fade has to end in the bubble's own colour. Interpolating from the
     keyword `transparent` is interpolating from transparent BLACK, which
     leaves a grey bruise across a blue bubble -- hence the relative colour,
     and the flat strip for anything that cannot do it. */
  background:var(--bub); }
@supports (color: rgb(from white r g b / 1)) {
  .msg-unfold { background:linear-gradient(to bottom, rgb(from var(--bub) r g b / 0),
                                           var(--bub) 58%); }
}
.msg-unfold:hover { text-decoration:underline; }
:not(.is-clipped) > .msg-unfold { position:static; width:100%; display:block;
  padding:7px 0 0; background:none; opacity:.85; }
:not(.is-clipped) > .msg-unfold:hover { opacity:1; }

/* ---------------------------------------------------------- booking a call
   Stefan, 09-24-2026: the time is agreed by text before this opens, so the
   strip is three fields and a verdict -- never a grid of slots to browse.
   The verdict is the point: a blocked hour has to SAY what is in it, because
   "I had content blocked out ... but I'm open to having a sales call" is a
   judgement somebody makes, not one the calendar makes for them. */
.ws-book-bar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:10px; }
.ws-book-bar .helper { flex:1 1 180px; min-width:0; }
.ws-book { margin:8px 0 12px; }
.bk { border:1px solid var(--line); border-radius:10px; padding:10px; background:var(--panel-2); }
.bk-row { display:flex; gap:8px; flex-wrap:wrap; align-items:flex-end; }
.bk-f { display:flex; flex-direction:column; gap:3px; font-size:12px; color:var(--muted); min-width:0; }
.bk-f input, .bk-f select { min-width:0; }
/* A LOCKED FIELD IS NOT A BROKEN ONE. The calendar is fixed while moving a
   call -- the appointment stays where it was booked -- but it had no styling
   of its own, so the browser's default grey made it read as failed. MJ,
   10-13-2026: "the calendar options are grayed out and I can't choose the
   45 mins call." Full-strength text and a dashed edge say settled; the note
   under it (.bk-locked) says why, and what to do instead. */
.bk-f select:disabled { opacity:1; color:var(--text); background:var(--lift-2);
  border-style:dashed; cursor:default; }
.bk-locked { margin:6px 0 0; }
.bk-cal { flex:1 1 200px; }
.bk-f input[type="date"] { width:148px; }
.bk-f input[type="time"] { width:116px; }
.bk-go { margin-top:10px; align-items:center; }
.bk-notify { display:inline-flex; align-items:center; gap:5px; font-size:12px; color:var(--muted); }
/* The field grid's `input { width:100% }` catches these; a 403px tickbox is
   the bug that pushed a label clean out of the panel on 09-24. */
.bk-notify input[type="checkbox"] { width:auto; min-width:0; margin:0; }
.bk-verdict { margin-top:9px; font-size:12.5px; line-height:1.45; border-radius:8px;
  padding:8px 10px; border:1px solid var(--line); color:var(--muted); background:var(--lift-1); }
.bk-verdict.ok   { border-color:color-mix(in srgb, var(--ink-good) 45%, transparent); background:var(--tint-green); color:var(--text); }
.bk-verdict.warn { border-color:color-mix(in srgb, var(--ink-warn) 45%, transparent); background:var(--tint-amber); color:var(--text); }
.bk-verdict.bad  { border-color:color-mix(in srgb, var(--ink-bad) 45%, transparent); background:var(--tint-red); color:var(--text); }
.bk-clash { margin:6px 0 4px; padding-left:18px; }
.bk-clash li { margin:2px 0; }
.bk-note { margin:8px 0 0; }

/* The re-sign brief inside the booking dialog. It is a handover, not a field
   on a form, so it reads as its own panel with its own reason at the top --
   somebody being asked four questions they did not expect deserves to be
   told who is going to read them. Stefan, 09-25-2026: "when Mike books a
   re-sign call for a client, make him fill a required note template for
   Stefan that sends an internal notification." */
.bk-kind-row { margin-top:9px; }
.bk-kind-f select { width:170px; }
.bk-brief { margin-top:10px; padding:11px 12px 12px; border-radius:10px;
  border:1px solid var(--line); background:var(--lift-1); }
.bk-brief-why { margin:0 0 9px; font-size:12px; line-height:1.45; color:var(--muted); }
.bk-brief-fields { display:flex; flex-direction:column; gap:9px; }
.bk-bf { display:flex; flex-direction:column; gap:3px; font-size:12px; color:var(--text); }
.bk-bf > span { font-weight:650; }
.bk-bf em { font-style:normal; font-weight:400; color:var(--subtle); }
.bk-bf textarea { width:100%; min-width:0; resize:vertical; font:inherit;
  padding:7px 9px; border-radius:8px; border:1px solid var(--line);
  background:var(--field); color:var(--text); }
.bk-bf textarea:focus-visible { outline:2px solid var(--blue); outline-offset:1px; }

/* And the brief itself, read from the call it was written for. */
.rsb { display:flex; flex-direction:column; gap:12px; }
.rsb-who { margin:0; font-size:12px; color:var(--muted); }
.rsb-q { margin:0 0 3px; font-size:12px; font-weight:700; color:var(--text); }
.rsb-a { margin:0; font-size:13.5px; line-height:1.55; color:var(--muted); white-space:pre-wrap; }
@media (max-width: 560px) {
  .bk-cal, .bk-f { flex:1 1 100%; }
  .bk-f input[type="date"], .bk-f input[type="time"] { width:100%; }
}
.bk-open { margin:6px 0 2px; font-size:12px; color:var(--muted); }

/* The booking dialog: a person, then a time. Reached from the Sales Floor's
   blue button, where you are not standing on anybody in particular, so it
   opens on the search rather than on a calendar. */
.bkd-back { position:fixed; inset:0; z-index:90; background:rgba(15,18,24,.42);
  display:flex; align-items:flex-start; justify-content:center;
  padding:calc(64px + env(safe-area-inset-top, 0px)) 16px 24px; overflow:auto; }
.bkd { width:100%; max-width:560px; background:var(--panel); color:var(--text);
  border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-lg); }
.bkd-head { display:flex; align-items:center; gap:10px; padding:13px 14px;
  border-bottom:1px solid var(--line); }
.bkd-head b { flex:1; }
.bkd-body { padding:14px; }
.bkd-find { display:flex; flex-direction:column; gap:4px; font-size:12px; color:var(--muted); }
.bkd-find input { width:100%; }
.bkd-hits { margin-top:8px; border:1px solid var(--line); border-radius:9px; overflow:hidden; max-height:260px; overflow-y:auto; }
.bkd-hit { display:flex; align-items:baseline; gap:8px; width:100%; text-align:left;
  padding:8px 11px; background:none; border:0; border-bottom:1px solid var(--line);
  cursor:pointer; color:var(--text); font:inherit; }
.bkd-hit:last-child { border-bottom:0; }
.bkd-hit:hover, .bkd-hit:focus-visible { background:var(--lift-2); }
.bkd-kind { font-size:11.5px; color:var(--muted); }
.bkd-none { padding:10px 11px; margin:0; }
.bkd-who { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; margin-top:10px; }
.bkd-strip { margin-top:10px; }
.bkd-strip .bk { border:0; padding:0; background:none; }
.bkd-hits { background:var(--panel-2); }
@media (max-width: 560px) { .bkd-back { padding-top:calc(16px + env(safe-area-inset-top, 0px)); } }

/* ------------------------------------------------- the booking page editor
   The week had no styles of its own, so it inherited the field grid's
   `input { width:100% }` and became seven stacked blocks with "to" on a line
   of its own -- a page of scrolling to say "nine to five". One row a day. */
/* The editor is a form with a week in it, not a one-question dialog: 560px
   squeezed every day row down to a 160px time box. */
.bkd.bk-edit { max-width: 680px; }
/* A hint under the label, not tacked onto the end of it: inline, "Shown to
   them as" and "on the page and in the invitation" read as one long
   instruction with no punctuation between them. */
.bk-edit label .helper { display: block; font-size: 11.5px; line-height: 1.35; }
.bk-name { margin-bottom: 4px; }
.bk-name input { width: 100%; }

/* The editor, grouped.

   Three columns, and -- the part that actually fixes the alignment -- every
   label is one line, so every control starts at the same height whatever the
   field is called. align-items:start keeps a cell with a two-line hint from
   stretching its neighbours. */
.bk-group { margin: 16px 0 8px; font-size: 10.5px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--subtle);
  padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.bk-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px 14px; align-items: start; }
.bk-f { min-width: 0; }
.bk-f > label { display: block; margin-bottom: 4px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.bk-f > select, .bk-f > input { width: 100%; }
/* What the label used to have to carry, under the control where it cannot
   push anything around. */
.bk-hint { margin: 5px 0 0; font-size: 11px; line-height: 1.45; color: var(--subtle); }
/* The example invitation under the name: what the event will be called, kept
   apart from the hint above it so it reads as the thing itself rather than
   more advice about it. */
.bk-eg { margin-top: 7px; padding-left: 9px; border-left: 2px solid var(--line); }
.bk-eg b { color: var(--text); font-weight: 600; }
@media (max-width: 760px) {
  .bk-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  .bk-fields { grid-template-columns: minmax(0, 1fr); }
}
.bk-pair { display: flex; align-items: center; gap: 6px; }
.bk-pair select { flex: 1 1 auto; min-width: 0; }
.bk-pair input[type="number"] { width: 78px; flex: 0 0 auto; }
.bk-unit { flex: 0 0 auto; }
.bk-week { display: grid; gap: 4px; margin-top: 8px; }
.bk-day { display: grid; grid-template-columns: 76px minmax(0, 1fr) auto minmax(0, 1fr) auto;
  align-items: center; gap: 8px; padding: 5px 8px; border-radius: 8px; }
.bk-day.on { background: var(--lift-1); }
.bk-day-on { display: inline-flex; align-items: center; gap: 7px; margin: 0;
  font-size: 13px; color: var(--text); cursor: pointer; }
/* The field grid's input rule catches these too: a 400px tickbox is the bug
   that pushed a label clean out of a panel on 09-24. */
.bk-day-on input[type="checkbox"] { width: auto; min-width: 0; margin: 0; flex: 0 0 auto; }
.bk-day input[type="time"] { width: 100%; min-width: 0; }
.bk-day input[type="time"][disabled] { opacity: .45; }
.bk-to-l { font-size: 12px; color: var(--muted); }
.bk-same { font-size: 12px; white-space: nowrap; }
.bk-same[disabled] { opacity: .35; pointer-events: none; }
.bk-wk { margin-top: 8px; }
.bk-wk-row { font-size: 13px; color: var(--muted); }
@media (max-width: 560px) {
  .bk-day { grid-template-columns: 64px minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-areas: "d a t b" ". s s s"; row-gap: 2px; }
  .bk-same { grid-area: s; justify-self: start; }
}

/* ------------------------------------------------ the Call Pipeline card
   It was two columns held apart by space-between: the person on the left,
   the outcome buttons hard against the right edge, and on a wide screen a
   hand's width of nothing between them. One strip along the bottom instead
   -- what you can READ on the left, what you can LOG on the right -- so the
   gap closes as the window narrows instead of growing with it. */
.cl-card { margin-bottom: 9px; padding: 13px 14px; }

/* WHERE THE CALL STANDS, as a bar down the left edge: green they have
   confirmed, amber nobody has yet, red they cancelled, no-showed or ghosted.
   Stefan, 10-14-2026 -- a list he can read without reading.

   --card-color is the house hook for "this card means something"; setting it
   tints the corner glow the card already has, so the edge and the corner
   agree instead of being two unrelated colours.

   The bar is a ::before rather than a border-left because .card already clips
   to its own radius, which rounds the bar's ends for free. It sits INSIDE the
   card's own 14px of left padding and nothing is moved to make room, so a
   painted row and a plain one still line up down the list. */
.cl-card.cl-set { --c:var(--sw-green); --card-color:color-mix(in srgb, var(--sw-green) 22%, transparent); }
.cl-card.cl-ask { --c:var(--sw-amber); --card-color:color-mix(in srgb, var(--sw-amber) 22%, transparent); }
.cl-card.cl-off { --c:var(--sw-red);   --card-color:color-mix(in srgb, var(--sw-red) 18%, transparent); }
.cl-card.cl-set::before, .cl-card.cl-ask::before, .cl-card.cl-off::before {
  content:""; position:absolute; left:0; top:0; bottom:0; width:4px;
  background:var(--c); pointer-events:none; }

/* A call that is TODAY and confirmed breathes, because that is the one worth
   not forgetting. The glow spreads to the RIGHT of the bar: .card is
   overflow:hidden, so anything outside the edge is clipped away unseen. */
.cl-card.cl-soon::before { animation:cl-breathe 2.4s ease-in-out infinite; }
@keyframes cl-breathe {
  0%, 100% { box-shadow:0 0 0 0 transparent; }
  50%      { box-shadow:3px 0 10px 2px color-mix(in srgb, var(--c) 75%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .cl-card.cl-soon::before { animation:none;
    box-shadow:3px 0 9px 1px color-mix(in srgb, var(--c) 60%, transparent); }
}

/* Struck out, not deleted: the row still has to be readable, because the
   outcome, the note and the money on it are what you came to check. */
.cl-card.cl-off .cl-name { text-decoration:line-through;
  text-decoration-thickness:1.5px;
  text-decoration-color:color-mix(in srgb, var(--sw-red) 70%, transparent); }
.cl-card.cl-off .cl-name .linky { color:var(--muted); }
.cl-top { display: flex; gap: 14px; flex-wrap: wrap; }
.cl-who { min-width: 0; flex: 1 1 auto; }
.cl-strip { display: flex; align-items: center; justify-content: space-between;
  gap: 10px 14px; flex-wrap: wrap;
  margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--line-strong); }
.cl-links { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 0; padding: 0; border: 0; min-width: 0; }
.cl-strip .outcome-row { display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap; margin: 0; }
.cl-strip .out-choices { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* A native select beside four chips reads as a different kind of control.
   Same height, same radius, same ground -- it is one more choice, not a
   different sort of thing. */
.cl-strip .more-out { width: auto; min-width: 104px; height: 28px; padding: 0 26px 0 10px;
  font-size: 12px; line-height: 26px; border-radius: 999px;
  border: 1px solid var(--line-strong); background-color: var(--lift-1);
  background-position: right 9px center; color: var(--muted); }
.cl-strip .more-out:hover { background-color: var(--lift-2); color: var(--text); }
.cl-strip .edit-toggle { opacity: .65; }
.cl-strip .edit-toggle:hover { opacity: 1; }
@media (max-width: 720px) {
  /* Stacked, and in the SAME order as the desktop: what you can do to the
     call, then what you log about it.

     It used to put the outcome first, on the reasoning that "on a phone you
     are logging a call, not reading round it." Stefan, 10-15-2026, says what
     he actually does on a phone: "cancel and confirm calls ... if I hit
     confirm it means they've actually texted us and confirmed. Free the time
     means I'm pretty sure they're not going to show, so I want to open up
     that free time, but I don't want it to actually cancel them yet."

     Two of those three are in the action row, and the outcome row leads with
     Closed -- the most consequential button in the app, which mints a client
     and opens their card. Having it sit above Confirm on the one device he
     uses while walking around was the wrong way up. Logging an outcome
     happens after a call, usually at a desk; the actions happen on the move. */
  .cl-strip { flex-direction: column; align-items: stretch; }
}

/* ------------------------------------------------------------------- phone
   Figures in a column line up, and the audio player is a control rather than
   a full-width block that pushes the row apart. */
.ph-n { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
#callList audio { height:32px; max-width:min(300px, 52vw); vertical-align:middle; }
@media (max-width: 560px) { #callList audio { max-width:100%; width:100%; } }

/* A mouse is happy with a 31px row; a thumb is not. Anything driven by touch
   keeps the taller one, so tightening the desktop rail does not make the
   phone menu harder to hit. */
@media (pointer: coarse) {
  .nav-item { min-height: 38px; font-size: 13.5px; gap: 11px; }
  .nav-icon { width: 18px; flex: 0 0 18px; font-size: 15px; }
  .nav-section { gap: 4px; }
}

/* ------------------------------------------------- integrations, as cards
   Stefan, 09-25-2026: "Ideally it would be nice if there was just a connect
   button... seems cleaner and more professional." A row was a name, a pill
   and a chevron, and what the thing actually DID was hidden until you opened
   it. Now the row answers both questions without being opened: what this
   connects, and whether it is connected. */
.int-ico { flex:0 0 34px; width:34px; height:34px; border-radius:9px; display:grid;
  place-items:center; font-weight:800; font-size:13px; letter-spacing:-.02em;
  color:var(--text); background:var(--lift-2); border:1px solid var(--line); }
/* The real marks, in their own colours. These are each service's actual
   logo, not a glyph tinted to look like one -- Stefan, 09-25-2026: "I want
   the actual pictures of the icons, not icons that you made in your system."
   Full-colour PNG or SVG, drawn at 22px inside a neutral tile so a logo on a
   white ground and a logo on its own dark square both sit the same way.
   Provenance and refresh instructions are in SOURCE.md beside them. */
.int-ico { flex:0 0 34px; width:34px; height:34px; border-radius:9px; display:grid;
  place-items:center; font-weight:800; font-size:13px; letter-spacing:-.02em;
  color:var(--ico, var(--text));
  background:color-mix(in srgb, var(--ico, var(--text)) 12%, var(--lift-2));
  border:1px solid color-mix(in srgb, var(--ico, var(--text)) 26%, var(--line)); }
.int-ico.has-mark { font-size:0; background:var(--lift-2); border-color:var(--line); }
.int-ico.has-mark::before { content:""; display:block; width:22px; height:22px;
  background:var(--mark) center / contain no-repeat; }

.int-ico[data-ico="anthropic"] { --mark:url("/static/icons/int/anthropic.png"); }
.int-ico[data-ico="meta"],
.int-ico[data-ico="metasync"] { --mark:url("/static/icons/int/meta.png"); }
.int-ico[data-ico="instagram"] { --mark:url("/static/icons/int/instagram.png"); }
.int-ico[data-ico="google"]    { --mark:url("/static/icons/int/google.svg"); }
.int-ico[data-ico="zoom"]      { --mark:url("/static/icons/int/zoom.svg"); }
.int-ico[data-ico="stripe"]    { --mark:url("/static/icons/int/stripe.png"); }
.int-ico[data-ico="fathom"]    { --mark:url("/static/icons/int/fathom.png"); }
.int-ico[data-ico="plaid"]     { --mark:url("/static/icons/int/plaid.png"); }
.int-ico[data-ico="wave"]      { --mark:url("/static/icons/int/wave.png"); }
.int-ico[data-ico="ghl"]       { --mark:url("/static/icons/int/ghl.png"); }
.int-ico[data-ico="linq"]      { --mark:url("/static/icons/int/linq.png"); }
.int-ico[data-ico="twilio"]    { --mark:url("/static/icons/int/twilio.png"); }

/* The three that are ours rather than somebody's product keep a monogram.
   There is no Revenue-goal logo to go and find. */
.int-ico[data-ico="goal"]    { --ico:#35C98F; }
.int-ico[data-ico="nightly"] { --ico:#8B5CF6; }
.int-ico[data-ico="imports"] { --ico:#4387F5; }

.int-main { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px; }
.int-blurb { font-size:12px; color:var(--muted); line-height:1.35;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* One button, and it says what will happen. Closed and unconnected it
   offers; closed and connected it manages; open it backs out. */
.int-act { flex:0 0 auto; border-radius:8px; padding:7px 13px; font-size:12px;
  font-weight:650; white-space:nowrap; background:var(--blue-solid);
  color:var(--on-accent); border:1px solid transparent; transition:background .14s ease; }
.int-act::before { content:"+ Add connection"; }
.int > summary:hover .int-act { background:var(--accent-fill-2); }
.int.is-on .int-act { background:transparent; color:var(--muted); border-color:var(--line-strong); }
.int.is-on .int-act::before { content:"Manage"; }
.int[open] .int-act { background:transparent; color:var(--muted); border-color:var(--line-strong); }
.int[open] .int-act::before { content:"Cancel"; }
/* The row already says what it connects; the body should not repeat the same
   sentence directly underneath it. */
.int[open] .int-blurb { display:none; }

@media (max-width: 620px) {
  .int > summary { flex-wrap:wrap; }
  .int-main { flex:1 1 100%; order:2; }
  .int-act { order:1; margin-left:auto; }
  .int-blurb { white-space:normal; }
}

/* And a word at the end of the row, so it is said once rather than guessed
   at. At rest it is 0 wide, and that is the whole point: it used to sit there
   invisible but still 96px wide, so every strip in the app carried a stretch
   of empty pill after its last tab. Stefan, 09-25-2026: "to the right of
   Files, there's a lot of blank space in that first box." It opens on hover
   instead, with the grips, and the strip hugs its tabs the rest of the time.
   In flow rather than absolute on purpose: an absolute hint hanging off
   left:100% still counts as overflow, and on a narrow laptop the seven-tab
   Clients strip would have given the whole page a sideways scrollbar. */
.subnav { position: relative; }
.subnav-hint { align-self: center; white-space: nowrap; overflow: hidden;
  max-width: 0; margin-left: 0;
  font-size: 11px; color: var(--subtle); opacity: 0; pointer-events: none;
  transition: opacity .14s ease, max-width .18s ease, margin-left .18s ease; }
.subnav:hover .subnav-hint, .subnav:focus-within .subnav-hint {
  opacity: .85; max-width: 120px; margin-left: 4px; }
.subnav.tab-rearranging .subnav-hint { opacity: 0; }
@media (max-width: 720px) { .subnav-hint { display: none; } }
@media (pointer: coarse) { .subnav-hint { display: none; } }

/* ------------------------------------------------------- rearrangeable chips
   A row of chips somebody can drag into their own order. The handle follows
   the tab strip's bargain exactly: invisible at rest, every one fades in when
   the pointer is anywhere on the row, and the whole chip is the drag target.
   A row that can be rearranged has to say so, and one chip quietly growing a
   handle reads as a fault rather than an invitation. */
.chip-row .chip { position: relative; padding-left: 20px; }
.chip-grip { position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; color: currentColor; fill: currentColor;
  opacity: 0; cursor: grab; transition: opacity .14s ease; }
.chip-row:hover .chip-grip, .chip-row:focus-within .chip-grip { opacity: .4; }
.chip-row .chip:hover .chip-grip, .chip-row .chip:focus-visible .chip-grip { opacity: .75; }
.chip-row.chip-rearranging .chip-grip { opacity: .55; }
.chip-grip:active { cursor: grabbing; }
.chip.chip-dragging { opacity: .45; }
.chip-row.chip-rearranging .chip { transition: transform .12s ease; }
/* No hover and no drag on a touch screen, so the handle only takes room. */
@media (pointer: coarse) {
  .chip-row .chip { padding-left: 12px; }
  .chip-grip { display: none; }
}
/* The rule, said at the end of the row rather than left to be discovered. */
.chip-hint { align-self: center; margin-left: 6px; white-space: nowrap;
  font-size: 11px; color: var(--subtle); opacity: 0; pointer-events: none;
  transition: opacity .14s ease; }
.chip-row:hover .chip-hint, .chip-row:focus-within .chip-hint { opacity: .85; }
.chip-row.chip-rearranging .chip-hint { opacity: 0; }
@media (max-width: 720px), (pointer: coarse) { .chip-hint { display: none; } }

/* ------------------------------------------------------ the Calls tab
   Not the shelf. These are four links you SEND somebody, several times a
   week, so the row is the name, what it is for, and a button that puts it on
   the clipboard -- no folders, no drag handles, no stars, none of which help
   you paste a link into a text message. */
.calllist { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; }
.callrow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 13px 15px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--panel); box-shadow: var(--shadow-sm); }
.callmain { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.calltitle { font-size: 14px; font-weight: 750; letter-spacing: -.01em; }
.callnote { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.callurl { font-size: 12px; color: var(--subtle); text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.callurl:hover { color: var(--blue); text-decoration: underline; }
.callacts { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
.libempty { margin: 18px 0; color: var(--muted); }
@media (max-width: 560px) {
  .callrow { align-items: stretch; }
  .callacts { width: 100%; }
  .callacts .button, .callacts .tb { flex: 1; text-align: center; }
}

/* Whose clock the booking panel is on. The badge sits in the Time label and
   the line under the row spells the moment out -- "06:00 PM" in a box with no
   zone on it is what sent MJ asking which 6pm she was typing. */
.bk-tz { font-size: 10px; font-weight: 700; letter-spacing: .06em; color: var(--subtle);
  background: var(--lift-2); border-radius: 3px; padding: 1px 5px; margin-left: 4px;
  vertical-align: 1px; }
.bk-opt { font-size: 10px; font-weight: 600; color: var(--subtle); margin-left: 4px; }
.bk-when { font-size: 12px; color: var(--text); font-weight: 600; margin: -2px 0 8px;
  min-height: 17px; }
.bk-theirs { margin-bottom: 4px; }
.bk-tztheirs { font: inherit; font-size: 12.5px; max-width: 220px; }

/* Whose card a form shows on. Under the contact strip, quiet until there is
   something to say -- the same person can be two GoHighLevel contacts, and a
   form lands on the one carrying the email it was filled in with. */
.fv-whose { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin-top: 8px; font-size: 11.5px; color: var(--muted); }
.fv-whose:empty { display: none; }
.fv-link { display: inline-flex; align-items: center; gap: 5px; }
.fv-sug { color: var(--subtle); }
.fv-whose .link-btn { font-size: 11.5px; }
.fv-attach { margin-left: auto; }

/* Contracts: a template that already knows its price, and the dates it can
   work out. What is in front of somebody on a call should only be the things
   that change per client. */
.ct-preset { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 8px 14px; margin-top: 8px; }
.ct-preset label > span { display: block; font-size: 11.5px; font-weight: 650;
  color: var(--subtle); margin-bottom: 3px; }
.ct-preset input { width: 100%; font: inherit; font-size: 12.5px; }
.ct-preset-done { grid-column: 1 / -1; margin-top: 4px; font-size: 12px; }
.ct-preset-done summary { color: var(--subtle); cursor: pointer; padding: 5px 0; }
.ct-derived { font-style: normal; font-weight: 600; color: var(--ink-good); font-size: 10.5px; }

/* Two records, one person. Under the details table, quiet -- merging is rare
   and irreversible-looking, so it should not sit next to things you click all
   day. */
.ws-merge { margin-top: 10px; font-size: 11.5px; color: var(--muted); }
.ws-merged { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 4px 0; }
.ws-merge .link-btn { font-size: 11.5px; }

/* "You are writing to the team, not to them."

   Every notes thread is internal, but on a prospect there is a real
   conversation with that person one column over and the two looked alike.
   Stefan, 09-27-2026: "I would hate to think that I'm accidentally responding
   in somebody's inbox." Amber, because it is a caution and not an error, and
   deliberately unlike anything an outbound message wears. */
.team-only { display: flex; align-items: flex-start; gap: 8px;
  background: var(--tint-amber, rgba(180,130,10,.10));
  border: 1px solid color-mix(in srgb, var(--ink-warn, #7a5f00) 26%, transparent);
  border-radius: 8px; padding: 8px 10px; margin: 0 0 10px;
  font-size: 11.5px; line-height: 1.45; color: var(--ink-warn, #7a5f00); }
.team-only b { font-weight: 750; }
/* The composer under an internal thread is tinted to match, so the box you
   type into never looks like the box that reaches the prospect. */
.team-only ~ .cmt-box, .team-only ~ .msg-box.cmt-box {
  border-color: color-mix(in srgb, var(--ink-warn, #7a5f00) 30%, transparent); }

/* Quick picks under a date box: today, tomorrow, the coming Monday. Typing a
   date mid-sentence on a sales call is exactly the pause this page exists to
   remove. */
.ct-quick { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.ct-quick .chip { font-size: 11px; padding: 3px 9px; }

/* Straight to their agreement from the call in front of you. Beside the
   contact strip, because that is where you already are when the answer is
   yes. */
.fv-agree { margin-left: 8px; text-decoration: none; }
.ct-quick .chip.on { background: var(--blue); border-color: var(--blue); color: #fff; }

/* Contracts: the form and the list, side by side.

   The form was stretched to the page's full width, which made a box for "6
   months" nine inches long and pushed the list below the fold. Stefan,
   09-27-2026: "I could just hover over the right box and scroll up and down
   while making an agreement still." So two columns that each keep their own
   scroll, and one under the other only when there is not room for two. */
.ct-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px; align-items: start; }
.ct-two > .card { min-width: 0; }
.ct-two .ct-sent { position: sticky; top: 12px; max-height: calc(100vh - 150px);
  display: flex; flex-direction: column; }
.ct-two .ct-sent > .table-wrap, .ct-two .ct-sent > table { overflow: auto; }
.ct-two .ct-sent #ctTable { overflow-y: auto; overflow-x: hidden; min-height: 0; }
/* Everything sent, as a list that fits its column: two lines a row, each
   wrapping rather than scrolling sideways. */
.ct-list { display: flex; flex-direction: column; }
.ct-row { padding: 11px 2px; border-top: 1px solid var(--line); display: grid; gap: 6px; }
.ct-row:first-child { border-top: 0; }
.ct-row-top, .ct-row-sub { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 6px 12px; min-width: 0; }
.ct-who, .ct-what { min-width: 0; overflow-wrap: anywhere; }
.ct-what { font-size: 12.5px; }
.ct-badges, .ct-acts { display: flex; flex-wrap: wrap; gap: 5px; }
.ct-acts { margin-left: auto; }
@media (max-width: 1100px) {
  .ct-two { grid-template-columns: 1fr; }
  .ct-two .ct-sent { position: static; max-height: none; }
}

/* The currency, in front of the box rather than inside the value. */
.ct-money { display: flex; align-items: center; gap: 0; }
/* A price is four or five characters. Full width -- which is what a stacked
   form gives everything -- made it look like a message box. Stefan,
   10-10-2026: "The contract price box is very large ... it probably needs to
   be the same size as the end date." So both are set from the same number
   here rather than left to come out alike by accident. */
.ct-fields .ct-money, .ct-fields .dp { width: 200px; max-width: 100%; }
.ct-money i { font-style: normal; font-weight: 700; color: var(--subtle);
  padding: 0 2px 0 9px; border: 1px solid var(--line); border-right: 0;
  border-radius: 8px 0 0 8px; align-self: stretch; display: grid; place-items: center;
  background: var(--lift-1); }
.ct-money input { border-radius: 0 8px 8px 0; flex: 1 1 auto; min-width: 0; }

/* The agreement panel: the Contracts page itself, over whatever you were
   doing. Same shape as the client card's panel, because it is the same idea
   -- Stefan, 09-27-2026: "it should open up the card right where I'm at."
   The page inside is rendered with ?embed=1, so it arrives without a sidebar
   of its own. */
/* OVER the card, not under it.

   .cp-panel sits at z-index 40, which is right when it opens over a list. The
   agreement panel opens over an open CLIENT CARD, and that card is a
   .brain-overlay at 60 with a blur on it -- so the panel appeared behind the
   card, dimmed, and had to be dismissed before it could be read. Stefan,
   10-01-2026: "it pulled it up behind his card, and it was kind of blurred
   out ... it should be deploying over the card, not behind it."

   71 clears the card (60), the push-left client panel (65) and that panel's
   raised state (66), and stays under the toasts and menus above it. */
.ct-panel { display: flex; flex-direction: column; z-index: 71; }
.ct-panel iframe { flex: 1 1 auto; width: 100%; border: 0; background: var(--bg); }
.ct-panel .cp-panel-head { flex: 0 0 auto; }

/* The tag list in the rail: pick one to narrow the list, × to retire it,
   + to adopt one somebody typed. Kept here rather than in Settings --
   Stefan, 09-28-2026: a tag is a thing you reach for while reading somebody,
   not a preference you set once a quarter. */
.ws-tag-row { display: flex; align-items: center; gap: 4px; width: 100%; }
.ws-tag-pick { flex: 1 1 auto; min-width: 0; display: flex; border: 0; padding: 0;
  background: none; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.ws-tag-pick span { display: flex; flex-direction: column; min-width: 0; }
.ws-tag-pick small { color: var(--subtle); font-size: 10px; line-height: 1.2; font-weight: 500; }
.ws-tag-x { flex: 0 0 auto; border: 0; background: none; color: var(--subtle);
  font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 5px; cursor: pointer;
  opacity: 0; transition: opacity .12s ease; }
.ws-tag-row:hover .ws-tag-x, .ws-tag-x:focus-visible { opacity: 1; }
.ws-tag-x:hover { background: var(--lift-2); color: var(--text); }
.ws-tag-new { padding: 4px 6px 2px; }
.ws-tag-add { width: 100%; font: inherit; font-size: 11.5px; padding: 4px 7px;
  border: 1px dashed var(--line); border-radius: 7px; background: transparent; }
.ws-tag-add:focus { border-style: solid; }
/* A number we read out of the conversation rather than one they gave a form. */
.ws-caught { font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-good); background: var(--tint-green, rgba(28,122,68,.12));
  padding: 1px 5px; border-radius: 999px; vertical-align: 1px; }

/* Their clock, under their number. The zone name is the fact; the time is why
   anyone reads it, so the time is not quieter than the name. Where it came
   from sits beside both, because "Mountain" off an area code and "Mountain"
   because they typed it are worth different amounts at 8am. */
.ws-tz { white-space: nowrap; }
.ws-tz-now { color: var(--muted); font-variant-numeric: tabular-nums; }
.ws-tz-src { font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); background: var(--lift-2);
  padding: 1px 5px; border-radius: 999px; vertical-align: 1px; white-space: nowrap; }
/* Said by them outranks anything worked out, so it does not look worked out. */
.ws-tz-src[title^="Read from"], .ws-tz-src[title^="Somebody here"] {
  color: var(--ink-good); background: var(--tint-green, rgba(28,122,68,.12)); }
.ws-tz-pick { width: 100%; max-width: 190px; }

/* What they paid last time, under the tier on a renewal row. Quiet, and
   absent entirely for a first-termer -- a column of dashes would bury the
   handful of rows that actually carry a figure. */
.renew-last { margin-top: 2px; font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.renew-last b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* The schedule, grouped by term. A header you can shut, and the term in
   force left open -- newest at the top, because the live term is what the
   card was opened for. Quiet: these are dividers in a list, not cards in a
   grid, so they get a rule and a row of type rather than a border and a
   radius each. Four bordered boxes stacked would read as four things rather
   than one history. */
.pay-term + .pay-term { border-top: 1px solid var(--line); }
.pay-term-head { display: flex; width: 100%; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 10px 2px; background: none; border: 0; cursor: pointer; text-align: left;
  font: inherit; font-size: 12.5px; color: var(--muted); }
.pay-term-head:hover { color: var(--text); }
.pay-term-head[disabled] { cursor: default; }
.pay-term-when { min-width: 0; }
.pay-term-sum { white-space: nowrap; color: var(--text); font-variant-numeric: tabular-nums; }
.pay-term.on > .pay-term-head { color: var(--text); font-weight: 600; }
/* The one in force is the only one whose rows are worth the eye by default. */
.pay-term:not(.on) .pay-term-sum { font-weight: 400; color: var(--muted); }
.pay-term-body { padding-bottom: 6px; }
.pay-term-body[hidden] { display: none; }

/* Three sections, because one list ran them together. Stefan, 10-03-2026:
   "Maybe there need to be three sections: their current term, past terms,
   early resign terms. That way, we know what's going to hit next." The
   headings are labels on a ledger, not cards: a rule and small caps, the same
   weight as the schedule headings above them. A section with nothing in it is
   never drawn, so "Paid ahead" appears on the one client who has one. */
.pay-group + .pay-group { margin-top: 26px; }
/* Stefan, 10-03-2026: "can we make those sections pop a little more for
   separation? Maybe we use the gradient line in between them because right now
   it just looks like a bunch of blended text." So the rule under each heading
   is the app's own blue-to-violet, 2px and fading out to the right rather than
   running the full width -- three hard rules stacked down a pane would shout
   as loudly as no rule at all. The heading sits on the page's text colour, not
   the muted grey it had, which is most of what was making them blend. */
.pay-group-head { margin: 0 0 6px; padding-bottom: 8px; font-size: 11.5px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text);
  border-bottom: 0; position: relative; }
.pay-group-head::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; border-radius: 3px;
  background: linear-gradient(100deg, var(--blue) 0%, var(--violet) 72%,
                              color-mix(in srgb, var(--violet) 20%, transparent) 100%); }
.pay-group-head span { font-weight: 600; letter-spacing: 0; color: var(--muted); }
.pay-group .pay-term:first-of-type { border-top: 0; }
/* The current term is the one being read; the history is reference, so its rule
   keeps the gradient but carries less of it. Still a line you can see -- the
   complaint was that the sections blended, and a rule nobody notices is the
   same problem with extra CSS. */
.pay-group:not(:first-of-type) .pay-group-head { color: var(--muted); }
.pay-group:not(:first-of-type) .pay-group-head::after { height: 2px;
  background: linear-gradient(100deg, color-mix(in srgb, var(--blue) 70%, transparent) 0%,
                              color-mix(in srgb, var(--violet) 40%, transparent) 72%,
                              transparent 100%); }

/* The sentence the Runway is built on. It is the only thing on this pane that
   states a liability rather than a fact about money received, so it gets the
   one accent on the pane and nothing else does. */
.pay-owed { margin: 8px 0 2px; font-size: 12.5px; color: var(--muted);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.pay-owed b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.pay-owed span { font-size: 11.5px; }
.pay-owed.owing b { color: var(--warn); }

/* What a lifetime figure is made of: one row per term, under the headline.
   Quiet on purpose -- the total is the fact being read, and these are its
   working. The `how` chip is the important part and is never decorative: a
   term read off a signed agreement and one inferred from a flag have to look
   different, because one of them is a record and the other is a reconstruction. */
.ltv-terms { width: 100%; margin-top: 8px; border-collapse: collapse; font-size: 12.5px; }
.ltv-terms td { padding: 4px 8px 4px 0; border-top: 1px solid var(--line); color: var(--muted);
  vertical-align: baseline; }
.ltv-terms tr:first-child td { border-top: 0; }
.ltv-seq { width: 18px; color: var(--subtle); font-variant-numeric: tabular-nums; }
.ltv-n { text-align: right; white-space: nowrap; }
.ltv-amt { text-align: right; white-space: nowrap; color: var(--text);
  font-variant-numeric: tabular-nums; }
.ltv-how { font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--subtle); background: var(--lift-2); padding: 1px 5px; border-radius: 999px;
  white-space: nowrap; margin-left: 4px; cursor: help; }

/* Pricing: the one address for a whole program, and the flag on a price whose
   Stripe side has not caught up with a change made here. */
.pr-buy {
  margin-top: 6px;
  font: 600 11.5px/1 var(--mono, ui-monospace, monospace);
  letter-spacing: .02em;
  color: var(--muted);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 4px 7px;
  cursor: pointer;
}
.pr-buy:hover { color: var(--ink); border-color: var(--line-strong); }
/* Under Tier 1: the private Elite payment pages a closer sends (pricing.js
   elitePages). One row per term: the page, then a chip per payment plan. */
.pr-elite { margin-top: 14px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--lift-2); }
.pr-elite-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-bottom: 8px; }
.pr-elite-head b { font-size: 13.5px; }
.pr-elite-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; padding: 7px 0; border-top: 1px solid var(--line); }
.pr-elite-row:first-of-type { border-top: 0; }
.pr-elite-term { min-width: 78px; font-weight: 700; font-size: 13px; }
.pr-elite-open { margin-left: auto; font-size: 12.5px; color: var(--muted); text-decoration: none; }
.pr-elite-open:hover { color: var(--ink); }
.pr-stale {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; margin-left: 4px;
  border-radius: 50%;
  background: var(--red); color: #fff;
  font: 700 10px/1 var(--sans, system-ui);
  cursor: help;
}

/* Sales floor: the line of "why" that replaces the outcome buttons. Not a
   dialog -- the answer is one short sentence and it belongs where the button
   you just pressed was. */
.pr-pend-note { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.pitch-answer .pitch-why {
  flex: 1 1 200px;
  min-width: 160px;
  font: inherit;
  padding: 5px 9px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel-2);
  color: var(--ink);
}
.pitch-answer .pitch-why:focus { outline: 2px solid var(--blue); outline-offset: 1px; }

/* Pricing: the switch that decides whether a program has a public page at
   all. Worded rather than labelled, because the consequence is not obvious
   from two words. */
.pr-public { display: flex; gap: 9px; align-items: flex-start; font-size: 12.5px;
             color: var(--muted); line-height: 1.45; max-width: 68ch; cursor: pointer; }
.pr-public input { margin-top: 2px; flex: 0 0 auto; }
.pr-public b { color: var(--ink); font-weight: 600; }

/* Do-not-contact: TWO boxes, because they are two different facts.

   One is a person withdrawing consent -- binding, per channel, usually
   recorded by the system when they text STOP, and not ours to reverse. The
   other is us deciding somebody is not worth contacting, which is a
   judgement we can change. They were sharing a box and reading as one thing.

   The colours carry the difference: an opt-out goes red, because ignoring it
   is a compliance problem. Our own call goes amber, because it is a note to
   ourselves. */
.dnd-box, .dnd-row {
  display: flex; flex-direction: column; gap: 8px; margin-top: 10px;
  font-size: 12.5px; color: var(--muted); line-height: 1.45;
}
.dnd-box:empty, .dnd-row:empty { display: none; }
/* Quiet: one line, folded. Six switches is a lot of card to spend on a
   question whose usual answer is "no". */
/* Folded, it says the answer: a green tick and "Okay to contact", with the
   switches as an action on the right -- not "DO NOT CONTACT · nothing set",
   which read as an instruction (10-02-2026). */
.dnd-box.is-quiet { gap: 0; border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--ink-good) 26%, var(--line));
  background: color-mix(in srgb, var(--ink-good) 5%, var(--panel)); }
/* Its own look wherever it is mounted: margin 0 against the global summary
   rule that pulls every summary 6px outward (which put the chevron over the
   border), and no inherited capitals. In a narrow column the action drops
   to its own line, on the right, instead of running past the edge. */
.dnd-sum { display: flex; align-items: center; flex-wrap: wrap; gap: 3px 8px; cursor: pointer;
  list-style: none; margin: 0; padding: 7px 10px; font-size: 12px; font-weight: 400;
  letter-spacing: 0; text-transform: none; color: var(--muted); }
.dnd-sum::-webkit-details-marker { display: none; }
.dnd-ok { display: grid; place-items: center; flex: 0 0 auto; width: 17px; height: 17px;
  border-radius: 50%; font-size: 10px; font-weight: 800; color: var(--ink-good);
  background: color-mix(in srgb, var(--ink-good) 16%, transparent); }
.dnd-sum-t { flex: 1 1 170px; min-width: 0; }
.dnd-sum-t b { color: var(--ink-good); font-weight: 700; }
.dnd-sum-act { flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; color: var(--subtle); white-space: nowrap; }
.dnd-sum-act::after { content: ""; width: 6px; height: 6px; margin-top: -3px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform .15s ease; }
.dnd-box[open] .dnd-sum-act::after { transform: rotate(-135deg); margin-top: 3px; }
/* Open, a hairline keeps the answer apart from the switches under it. */
.dnd-box[open] .dnd-sum { margin: 0 0 8px;
  border-bottom: 1px solid color-mix(in srgb, var(--ink-good) 20%, var(--line)); }
.dnd-sum:hover .dnd-sum-act { color: var(--text); }
.dnd-box.is-quiet .dnd-inner { padding: 0 10px 10px; display: flex;
  flex-direction: column; gap: 8px; }
.dnd-box.is-quiet .dnd-part { border: 0; background: transparent; padding: 0; }
.dnd-box.is-on .dnd-inner { display: flex; flex-direction: column; gap: 8px; }
.dnd-part {
  display: flex; flex-direction: column; gap: 4px;
  padding: 9px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2);
}
.dnd-theirs.is-set { border-color: var(--red);
  background: color-mix(in srgb, var(--red) 9%, transparent); }
.dnd-theirs.is-set .dnd-head { color: var(--red); }
.dnd-theirs.is-set input { accent-color: var(--red); }
.dnd-theirs.is-set .dnd-line.is-set .dnd-when { color: var(--red); }
.dnd-ours.is-set { border-color: var(--amber, #d99a2b);
  background: color-mix(in srgb, var(--amber, #d99a2b) 10%, transparent); }
.dnd-ours.is-set .dnd-head { color: var(--ink-warn, #b7791f); }
.dnd-ours.is-set input { accent-color: var(--ink-warn, #b7791f); }
.dnd-ours.is-set .dnd-line.is-set .dnd-when { color: var(--ink-warn, #b7791f); }
/* Kept for the blank state, which is still one box. */
.dnd-row.is-blank { padding: 9px 10px; border: 1px dashed var(--line);
  border-radius: 8px; background: var(--panel-2); }
/* flex-basis 200px meant 200px of WIDTH when this row was a row. The row is
   a column now, so the same declaration became 200px of empty height under
   every card that had nothing set. */
.dnd-note { flex: 0 0 auto; min-width: 0; margin-top: 2px; }
.dnd-note b { color: var(--ink); }
.dnd-why { display: block; font-style: italic; }

/* A phone call on a person's card: the audio, then what it was about, then
   the transcript folded away. The brief is the thing people read; the
   transcript is the evidence behind it, and forty turns of it unfolded would
   push everything else off the card. */
.cb-list { display:flex; flex-direction:column; gap:10px; }
.cb-call { display:flex; flex-direction:column; gap:8px; padding:10px 11px;
  border:1px solid var(--line); border-radius:10px; background:var(--panel-2); }
.cb-top { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; font-size:12.5px; }
.cb-top b { font-size:12.5px; }
.cb-when { color:var(--muted); font-size:11.5px; }
.cb-len { margin-left:auto; color:var(--muted); font-size:11.5px;
  font-variant-numeric:tabular-nums; }
.cb-flag { font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.05em;
  color:var(--ink-warn, #b7791f); }
.cb-call audio { width:100%; height:34px; }
.cb-brief { display:flex; flex-direction:column; gap:6px; }
.cb-brief > b { font-size:13px; line-height:1.45; letter-spacing:-.01em; }
.cb-row { display:flex; gap:9px; align-items:baseline; font-size:12px; }
.cb-row > span { flex:0 0 94px; color:var(--muted); font-size:10.5px; font-weight:700;
  text-transform:uppercase; letter-spacing:.05em; }
.cb-row p { margin:0; flex:1 1 auto; min-width:0; line-height:1.5; }
.cb-none { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.cb-none .helper { flex:1 1 auto; }
.cb-script summary { cursor:pointer; font-size:11.5px; color:var(--muted);
  font-weight:650; padding:2px 0; }
.cb-script summary:hover { color:var(--text); }
.cb-turns { display:flex; flex-direction:column; gap:5px; margin-top:6px;
  max-height:320px; overflow-y:auto; scrollbar-width:thin; }
.cb-turn { margin:0; font-size:12px; line-height:1.5; display:flex; gap:8px; }
.cb-turn > span { flex:0 0 38px; font-size:10.5px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.04em; padding-top:2px; }
.cb-turn.them > span { color:var(--blue); }

/* Two ads on one keyword. Amber rather than red: the number above it is not
   wrong, it just cannot be split any further than the keyword. */
.kw-clash { margin:0 0 10px; padding:9px 12px; border-radius:9px; font-size:12.5px;
  line-height:1.5; border:1px solid color-mix(in srgb, var(--amber) 55%, transparent);
  background:color-mix(in srgb, var(--amber) 12%, transparent); color:var(--ink-warn, #b7791f); }
.kw-clash b { color:var(--text); }
.kw-clash[hidden] { display:none; }

/* The weekly scorecard's controls: two jobs, said apart.

   WHICH DAYS you are reading, and WHERE THE DATA CAME FROM. They were six
   controls in one row with the meaning only in hover text, which made Sync
   from Meta look like it respected the dates beside it. It does not -- it
   refetches the whole account. */
.sc-controls { display:flex; align-items:center; gap:18px; flex-wrap:wrap; }
.sc-group { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.sc-group-label { font-size:10px; font-weight:700; letter-spacing:.07em;
  text-transform:uppercase; color:var(--subtle); }
.sc-group-data { padding-left:18px; border-left:1px solid var(--line); }
.sc-window { margin:8px 0 0; font-size:12px; color:var(--muted); }
.sc-window b { color:var(--text); font-weight:650; }
/* A creative that spent and produced nothing: one line, not nine tiles of
   em-dashes. The grid is a click away because every tile is editable. */
.sc-cre.is-quiet > summary { display:flex; align-items:center; gap:10px;
  flex-wrap:wrap; cursor:pointer; list-style:none; padding:2px 0; }
.sc-cre.is-quiet > summary::-webkit-details-marker { display:none; }
.sc-cre.is-quiet > summary .sc-cre-top { flex:1 1 auto; min-width:0; margin:0; }
.sc-cre.is-quiet > summary:hover { background:var(--lift-1); border-radius:8px; }
.sc-quiet-note { flex:0 0 auto; font-size:11.5px; color:var(--subtle);
  font-variant-numeric:tabular-nums; }
.sc-cre.is-quiet[open] .sc-quiet-note { display:none; }
.sc-cre.is-quiet > summary::after { content:"▸"; font-size:9px; color:var(--subtle);
  flex:0 0 auto; }
.sc-cre.is-quiet[open] > summary::after { content:"▾"; }

/* The creative's header line. It had NO styling at all, which is why the
   launch date and the on/off badge ran off the right edge: nothing told the
   name to give way. The name is the part that can shrink. */
.sc-cre-top { display:flex; align-items:baseline; gap:9px; flex-wrap:wrap;
  min-width:0; }
.sc-kw { flex:0 0 auto; font-size:10.5px; font-weight:800; letter-spacing:.05em;
  text-transform:uppercase; color:var(--blue); }
.sc-cre-name { flex:1 1 180px; min-width:0; font-size:12.5px; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Launch and off dates: the thing Stefan called out as worth keeping, so it
   wraps to its own line rather than being the thing that gets cut. */
.sc-age { flex:0 0 auto; margin-left:auto; font-size:11px; color:var(--subtle);
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.sc-on, .sc-off { flex:0 0 auto; font-size:9.5px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; padding:2px 7px; border-radius:20px; white-space:nowrap; }
.sc-on { color:var(--ink-good, #1c6b4a); background:color-mix(in srgb, var(--green) 16%, transparent); }
.sc-off { color:var(--muted); background:var(--lift-2); }
@media (max-width: 900px) {
  .sc-age { margin-left:0; flex:1 1 100%; }
  .sc-cre-name { flex:1 1 100%; white-space:normal; }
}

/* Only what is on. */
/* Pairs with .switch; only what is particular to this one is kept, or the
   later rule would quietly take the layout back off the component. */
.sc-liveonly { white-space:nowrap; font-size:12px; }
.sc-liveonly em { font-style:normal; color:var(--subtle); font-size:11px;
  font-variant-numeric:tabular-nums; }
.sc-liveonly:hover { color:var(--text); }

/* ---- two ads on one keyword -------------------------------------------
   A finding about a DAY, not about now. It used to be one flat line per day
   -- "SWAT ran on 2 ads at once on 10-11-2026" -- which reads as a live
   fault, so Stefan switched the duplicate off, resynced and saw it again.
   The state line is the whole point: amber while it is still running, quiet
   grey once it is over, because an over clash has nothing to action and
   cannot be cleared. A day that spent is a day that spent. */
.sc-clash { padding:12px 14px; border:1px solid var(--line); border-radius:10px;
  background:var(--lift-1); }
.sc-clash + .sc-clash { margin-top:8px; }
.sc-clash.is-now { border-color:color-mix(in srgb, var(--amber) 55%, transparent);
  background:color-mix(in srgb, var(--amber) 7%, transparent); }
.sc-clash-top { font-size:13px; color:var(--text); }
/* The keyword is the only thing in this line that is a keyword. The date used
   to be bold too and inherited the uppercase blue, so "OCT 11, 2026" read as
   another trigger word. */
.sc-clash-kw { font-weight:800; letter-spacing:.04em; text-transform:uppercase;
  color:var(--blue); }
.sc-clash-top em { font-style:normal; font-weight:650; color:var(--text);
  font-variant-numeric:tabular-nums; }
.sc-clash-state { margin:5px 0 0; font-size:12px; line-height:1.5; color:var(--muted); }
.sc-clash-now { color:var(--ink-warn); }
.sc-clash-done { color:var(--text); }
/* One line per ad, so the claim names what it is about. The two sit in a
   column rather than a row: the names are long and the on/off badge beside
   each one is the thing being compared. */
.sc-clash-who { display:flex; flex-direction:column; gap:4px; margin:9px 0 10px; }
.sc-clash-ad { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  min-width:0; font-size:12px; }
.sc-clash-ad > b { flex:0 1 auto; min-width:0; font-weight:650; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sc-clash-spend { flex:0 0 auto; color:var(--subtle); font-size:11.5px;
  font-variant-numeric:tabular-nums; }
/* The two rows the warning is about, once Show me both has put them on
   screen. A ring, not a fill: the row's own numbers are what you came to
   read. */
.sc-cre.is-clash { position:relative; border-radius:10px;
  box-shadow:0 0 0 2px color-mix(in srgb, var(--amber) 60%, transparent);
  background:color-mix(in srgb, var(--amber) 6%, transparent); }
.sc-cre.is-clash > summary { padding-left:8px; padding-right:8px; }

/* A keyword's number, opened. The count is the door to the people behind it;
   underlined on hover only, so a table of figures does not read as a table of
   links. */
.kw-people { color:inherit; text-decoration:none; border-bottom:1px dashed var(--line-strong); }
.kw-people:hover { color:var(--blue); border-bottom-color:currentColor; }
/* A badge that filters the board. Only the ones that actually can: a creative
   with no keyword behind it keeps the plain style and no handler. */
.badge.is-filter { cursor:pointer; }

/* The client card, reordered around what it is opened for: the notes, the
   contract, the dates, the money. Stefan, 09-30-2026: "all the other stuff
   we're never really looking at." */
/* The thread brings its own styling (comments.js); this is just the room
   it sits in. */
.cp-notes { display:block; }

/* The agreement at a glance, which is the other reason it gets opened. The
   form it used to be buried in is one click down. */
.cp-read { display:flex; flex-direction:column; gap:1px; }
.cp-read-row { display:flex; align-items:baseline; gap:10px; padding:6px 0;
  border-bottom:1px solid var(--line); font-size:12.5px; }
.cp-read-row:last-child { border-bottom:0; }
.cp-read-row > span { flex:0 0 110px; color:var(--muted); font-size:11px;
  text-transform:uppercase; letter-spacing:.05em; font-weight:700; }
.cp-read-row > b { flex:1 1 auto; min-width:0; font-weight:600; }
.cp-missing { color:var(--red); font-weight:600; }
@media (max-width: 760px) {
  .sc-group-data { padding-left:0; border-left:0; border-top:1px solid var(--line);
    padding-top:10px; width:100%; }
}
@media (max-width: 560px) {
  .cb-row { flex-direction:column; gap:1px; }
  .cb-row > span { flex:0 0 auto; }
}

/* ---- when the card is open and there is not room for four columns ------
   The card -- the person, the stage, the tags, the calls, whichever of its
   sections an icon opened -- is a grid child whether or not the grid has a
   column spare for it. Left to itself it lands in an implicit column that was
   never sized and the conversation collapses to one word a line, which is
   what the Messages panel has been doing.

   So the columns to its left give way, in the order you would give them up:
   the bucket rail first, the name list second.

   Both numbers are where a layout stops being WORTH it, not where it stops
   fitting -- each is the width at which the conversation would fall under
   430px, which is roughly where it becomes narrower than the card sitting
   next to it. A conversation narrower than the panel beside it is the wrong
   way round on any screen. Held to that, the conversation never drops below
   about 430px at any width, which is the thing worth keeping true. The conversation is the thing
   being read, so it is the one that grows. Shutting the card brings both
   straight back. Keeping the list through the middle band was tried and is
   worse -- it holds 260px while the conversation gets 226, a list you are not
   reading beside a message you cannot.

   THIS BLOCK LIVES AT THE END OF THE FILE ON PURPOSE. It was written above
   the @media rules that size the grid and lost to every one of them, even
   though three :not()s outrank a bare .ws4 -- the descendant rule inside it
   applied and the one on .ws4 itself did not, which reads exactly like a
   specificity problem and is not one. Anything moved above those rules stops
   working silently. */
@container ws (max-width: 1200px) {
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) {
    grid-template-columns:240px minmax(0,1fr) minmax(250px,300px); }
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) .ws-rail { display:none; }
}
@container ws (max-width: 970px) {
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) {
    grid-template-columns:minmax(0,1fr) minmax(250px,300px); }
  .ws4:not(.cardless):not(.guide-fold):not(.nobody) .ws-list { display:none; }
}

/* And the same again with the notes open -- the one state that never got it.

   Stefan, 10-01-2026, with a mention opened on Deepak inside the Messages
   panel: "prospect notes are messed up". The notes column was one pixel wide
   and its heading was coming down the screen a letter at a time.

   Every rule that sizes .noting is an @media rule, so all of them still
   believed they were on a 1120px desk -- while the grid itself was in a
   panel, inside the app shell, with about 890px to work with. They laid out
   four tracks for it: 262 for the conversation, 320 for a list that was
   hidden, 330 for a card that was hidden, and minmax(0,1fr) -- the notes --
   for what was left, which was nothing. A grid does not drop a track when its
   item is display:none; the remaining items simply move up into the tracks in
   front of them, so the conversation sat in the list's column and the notes
   were squeezed out of the end. The page did it too, in any window between
   about 1100 and 1350.

   So .noting is sized from the container like everything else here, and each
   band states BOTH the template and which columns are in it. Those two
   drifting apart is the whole bug, and keeping them in one place is the only
   thing that stops it coming back. The widths are this file's existing rule:
   the conversation never falls under about 430px, and the columns are given
   up in the order you would give them up -- the list before the card.

   Inside @media (min-width:701px) on purpose. Below that a phone already
   makes the notes a sheet over the thread, and these selectors would outrank
   it and take that away. */
@media (min-width: 701px) {
  /* Room for all four: the list and the card come back, whatever the
     window-width rules above decided about a window that was never the
     measurement that mattered. */
  .ws4.noting:not(.nobody) .ws-list { display:flex; }
  .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) .ws-card { display:flex; }

  /* 1450 = 320 list + 340 notes + 360 card + 430 of conversation. */
  @container ws (max-width: 1450px) {
    .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) {
      grid-template-columns:minmax(0,1fr) minmax(280px,320px) 330px; }
    .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) .ws-list { display:none; }
  }
  /* 1080 = 320 notes + 330 card + 430. The card goes; it is the thing you are
     writing about, not the thing you are writing in. */
  @container ws (max-width: 1080px) {
    .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) {
      grid-template-columns:minmax(0,1fr) minmax(280px,320px); }
    .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) .ws-card { display:none; }
  }
  /* Folded to the dock, or hidden behind the guide: one column narrower to
     begin with, so the list survives further down. */
  @container ws (max-width: 1140px) {
    .ws4.noting.cardless:not(.guide-fold):not(.nobody) {
      grid-template-columns:minmax(0,1fr) minmax(280px,320px) 48px; }
    .ws4.noting.cardless:not(.nobody) .ws-list { display:none; }
  }
  @container ws (max-width: 1090px) {
    .ws4.noting.guide-fold:not(.nobody) {
      grid-template-columns:minmax(0,1fr) minmax(280px,320px); }
    .ws4.noting.guide-fold:not(.nobody) .ws-list { display:none; }
  }
}
/* Nothing to key a do-not-contact on: still a row, so the absence is stated
   rather than looking like a clean record. */
.dnd-row.is-blank { border-style:dashed; }
/* The unattributed-call rows on the weekly scorecard: the name opens their
   conversation, and Delete sits apart from the three answers so it is never
   the one you hit reaching for Organic. */
.sc-gap-open { font-weight:700; font-size:inherit; color:var(--ink); text-align:left; }
.sc-gap-open:hover { color:var(--blue); text-decoration:underline; }
.sc-gap-del { margin-left:10px; color:var(--red); border-color:transparent; }
.sc-gap-del:hover { border-color:var(--red); }
/* The Prospects Inbox inside a panel. body.embed has already taken the
   topbar and the subnav away, so the workspace's 118px allowance for them is
   118px of nothing at the bottom of the frame. It gets the whole frame. */
body.embed .ws4 { height:100vh; min-height:0; }

/* ---- the Prospects Inbox inside a panel --------------------------------
   Opened on one person, deliberately, from somewhere else. The bucket rail
   and the name list are how you FIND somebody and you have already found
   them, so the frame is their conversation and their card side by side --
   which is what the page looks like anywhere else, and what Stefan means by
   "it should be the same layout as any contact card". Stefan, 09-29-2026,
   on the scorecard panel: "the messaging box was very small, his card
   underneath was kind of big. We could probably put that to the right."

   After the container rules above and one class heavier than them, because
   in a panel this is true at every width and their widths are not. */
body.embed .ws4:not(.cardless):not(.guide-fold):not(.nobody) {
  grid-template-columns:minmax(0,1fr) minmax(300px,360px); }
body.embed .ws4:not(.cardless):not(.guide-fold):not(.nobody) :is(.ws-rail, .ws-list) {
  display:none; }
body.embed .ws4.cardless:not(.nobody) { grid-template-columns:minmax(0,1fr); }
body.embed .ws4.cardless:not(.nobody) :is(.ws-rail, .ws-list) { display:none; }

/* And with the notes open in there: the conversation and the notes, and the
   card stands down. Three columns have never fitted in a frame this wide, and
   the card is the thing you are writing ABOUT -- the notes are the thing you
   came here to write. Without this the card had nowhere to go and dropped
   onto a second row underneath, which is the same mistake in the other
   direction: a layout that assumes a column it does not have the room for. */
body.embed .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody),
body.embed .ws4.noting.cardless:not(.nobody),
body.embed .ws4.noting.guide-fold:not(.nobody) {
  grid-template-columns:minmax(0,1fr) minmax(300px,360px); }
body.embed .ws4.noting:not(.cardless):not(.guide-fold):not(.nobody) :is(.ws-rail, .ws-list, .ws-card),
body.embed .ws4.noting.cardless:not(.nobody) :is(.ws-rail, .ws-list, .ws-card),
body.embed .ws4.noting.guide-fold:not(.nobody) :is(.ws-rail, .ws-list, .ws-card) { display:none; }
/* What the website recorded about somebody who applied on it, on the row
   asking where they came from. Quieter than the row's own line: it narrows
   the answer, it does not give it. */
.sc-gap-web { display:block; margin-top:2px; font-size:11.5px; color:var(--blue); }

/* ------------------------------------------------------------- automations
   A board, not a spreadsheet. GoHighLevel lists folders and workflows in one
   table, so a folder row carries four empty columns and reads like a workflow
   until you spot the icon; here a folder is a section and an automation is a
   card that says what it does and whether it is working. */
.au-root { max-width: 1180px; }
.au-folder { margin: 0 0 22px; border-radius: var(--radius); }
.au-folder.dragging { opacity: .45; }
.au-folder.over { box-shadow: 0 -2px 0 0 var(--accent, #4c5bd4); }
/* A closed folder is a single line. Open, the cards sit under its header --
   so the header reads as the thing the cards are in, which is the whole point
   of calling it a folder. */
.au-fhead { display: flex; align-items: center; gap: 9px; margin: 0 0 10px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line); cursor: grab; }
.au-folder.shut .au-fhead { margin-bottom: 0; }
.au-fhead:active { cursor: grabbing; }
.au-folder.unfiled .au-fhead { cursor: default; }
.au-fhead h2 { margin: 0; font-size: 15px; min-width: 0; }
.au-fname { padding: 0; border: 0; background: none; font: inherit; color: var(--text);
  cursor: pointer; text-align: left; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.au-fname:hover { text-decoration: underline; }
.au-twist { display: grid; place-items: center; width: 20px; height: 20px; flex: none;
  padding: 0; border: 0; border-radius: 5px; background: none; color: var(--muted);
  cursor: pointer; }
.au-twist svg { transition: transform .15s ease; transform: rotate(90deg); }
.au-folder.shut .au-twist svg { transform: rotate(0deg); }
.au-twist:hover { background: var(--lift-2); color: var(--text); }
.au-folder-icon { display: grid; place-items: center; flex: none;
  color: var(--subtle); }
.au-folder.unfiled .au-folder-icon { opacity: .5; }
/* The star fills when it is on, so "starred" is legible without reading a
   tooltip; an outline star beside a filled one is the clearest pair there is. */
.au-star { display: grid; place-items: center; width: 24px; height: 24px; flex: none;
  padding: 0; border: 0; border-radius: 6px; background: none; color: var(--subtle);
  cursor: pointer; }
.au-star:hover { background: var(--lift-2); color: var(--text); }
.au-star.on { color: #d9a106; }
.au-fmeta { font-size: 12px; color: var(--muted); }
@media (max-width: 620px) { .au-fmeta { display: none; } }

.au-menu-wrap { position: relative; flex: none; }
.au-dots { width: 26px; height: 26px; padding: 0; border: 1px solid transparent;
  border-radius: 7px; background: none; color: var(--muted); cursor: pointer;
  line-height: 1; letter-spacing: .5px; }
.au-dots:hover, .au-dots[aria-expanded="true"] { background: var(--lift-2);
  border-color: var(--line); color: var(--text); }
.au-menu { position: absolute; right: 0; top: 30px; z-index: 30; min-width: 170px;
  padding: 4px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); box-shadow: var(--shadow-lg); }
.au-menu button { display: block; width: 100%; padding: 7px 10px; border: 0;
  border-radius: 7px; background: none; font: inherit; font-size: 12.5px;
  color: var(--text); text-align: left; cursor: pointer; }
.au-menu button:hover { background: var(--lift-2); }
.au-menu button.danger { color: var(--bad, #b91c1c); }
.au-bad { color: var(--bad, #b91c1c); }
.au-spacer { flex: 1 1 auto; }
.chip.sm { font-size: 11.5px; padding: 4px 9px; }
.au-empty { margin: 2px 0 0; }

/* Cards wrap into as many columns as fit. A fixed grid leaves a lone card
   stretched across a row it does not fill. */
.au-cards { display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); }
.au-card { display: flex; flex-direction: column; gap: 8px; min-width: 0;
  padding: 13px 14px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); }
/* Paused is dimmed, never hidden: a reminder that is off is still a fact
   about what prospects are hearing. */
.au-card.off { opacity: .58; }
/* A card is picked up to move it into another folder. Grab over the card's
   body; the name and the switch keep their own pointer. */
.au-card[draggable="true"] { cursor: grab; }
.au-card[draggable="true"]:active { cursor: grabbing; }
.au-card.dragging { opacity: .4; }
/* While a card is in the air every folder it could land in says so, and the
   one under it lights up -- a drop target you have to guess at is a drag
   people stop trying. */
#board.filing .au-folder:not(:has(.au-card.dragging)) { outline: 1px dashed var(--line); outline-offset: 6px; }
.au-folder.take { outline: 2px solid var(--accent, #4c5bd4) !important; outline-offset: 6px;
  background: color-mix(in srgb, var(--accent, #4c5bd4) 6%, transparent); }
.au-ctop { display: flex; align-items: flex-start; gap: 10px; }
.au-cname { flex: 1 1 auto; min-width: 0; padding: 0; border: 0; background: none;
  font: inherit; font-weight: 650; color: var(--text); text-align: left;
  cursor: pointer; line-height: 1.35; }
.au-cname:hover, .au-cname:focus-visible { text-decoration: underline; }
.au-cnote { margin: 0; font-size: 12px; color: var(--muted); line-height: 1.45; }
.au-steps { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.au-pill { font-size: 11px; padding: 3px 8px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.au-pill.wait { color: var(--muted); }
.au-pill.mail { background: var(--lift-2); }
.au-pill.empty { color: var(--subtle); font-style: italic; }
.au-arrow { color: var(--subtle); font-size: 11px; }
.au-cfoot { font-size: 11.5px; color: var(--muted); margin-top: auto;
  padding-top: 6px; border-top: 1px solid var(--line); }
.au-quiet { color: var(--subtle); }
.au-warn { border-left: 3px solid var(--warn, #b45309); }
.au-warn b { display: block; margin-bottom: 2px; }

/* The switch. A state reads better as something that slides than as something
   that gets ticked; the track and knob are real elements because built from
   ::before and ::after the knob has nothing to anchor to. */
/* .au-sw was the app's FIRST switch, built here for this board, and it was
   the thing a second one should have been folded into rather than written
   beside. It is gone; both switches here are .switch now. Only the label
   width and its weight-on-live were particular to this page. */
.au-lab { min-width: 40px; }
.switch > input:checked ~ .au-lab { font-weight: 600; }

/* The detail. Body scrolls, head and foot stay put -- left to grow it pushed
   Save off the bottom of a laptop screen with nothing that would scroll to it. */
/* A fixed height, not a maximum: the canvas wants all the room it can get,
   and a panel that changes height as you move between Builder and Settings
   makes the tabs jump under the cursor. */
.bkd.au-edit { max-width: 760px; display: flex; flex-direction: column;
  height: calc(100dvh - 96px); max-height: 820px; }
.au-edit > .bkd-head, .au-edit > .bkd-foot, .au-edit > .au-tabs { flex: none; }
.au-edit > .au-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: 14px; }
@media (max-width: 560px) { .bkd.au-edit { height: calc(100dvh - 40px); } }
.au-head { gap: 12px; }
.au-title { flex: 1 1 auto; min-width: 0; font: inherit; font-weight: 650;
  border: 1px solid transparent; background: none; color: var(--text);
  padding: 4px 6px; border-radius: 7px; }
.au-title:hover { border-color: var(--line); }
.au-title:focus { border-color: var(--accent, #4c5bd4); outline: none; background: var(--panel-2); }
.au-tabs { display: flex; gap: 2px; padding: 0 14px; border-bottom: 1px solid var(--line); }
.au-tab { padding: 9px 12px; border: 0; border-bottom: 2px solid transparent;
  background: none; font: inherit; font-size: 12.5px; color: var(--muted); cursor: pointer; }
.au-tab:hover { color: var(--text); }
.au-tab.is-on { color: var(--text); font-weight: 650;
  border-bottom-color: var(--accent, #4c5bd4); }

/* The builder: a board you can pan and zoom, with the flow laid out down the
   middle of it. Stefan, 10-06-2026: "I do like the Miro board design look,
   especially if we need to zoom in and out and make a really long automation."

   The nodes still lay themselves out -- free placement earns its keep when a
   flow branches, and until then it only means a node can get lost. The dotted
   ground is what makes panning legible; without it the board appears not to
   move at all. */
.au-pane-build { margin: -14px; height: calc(100% + 28px); min-height: 280px; }
.au-canvas { position: relative; width: 100%; height: 100%; overflow: hidden;
  cursor: grab; background-color: var(--panel-2);
  background-image: radial-gradient(var(--line) 1px, transparent 1px);
  background-size: 22px 22px; }
.au-canvas.grabbing { cursor: grabbing; }
.au-stage { position: absolute; top: 0; left: 0; width: 460px;
  transform-origin: 0 0; will-change: transform; }
.au-canvas-hint { position: absolute; right: 10px; bottom: 10px; margin: 0;
  font-size: 11px; color: var(--subtle); pointer-events: none; }
.au-zoom { position: absolute; left: 10px; bottom: 10px; display: flex; gap: 2px;
  padding: 3px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--panel); box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.08)); }
.au-zbtn, .au-zpct { min-width: 28px; height: 28px; padding: 0 7px; border: 0;
  border-radius: 6px; background: none; color: var(--muted); font: inherit;
  font-size: 12px; cursor: pointer; line-height: 1; }
.au-zbtn.wide { min-width: 38px; }
.au-zpct { font-variant-numeric: tabular-nums; min-width: 46px; }
.au-zbtn:hover, .au-zpct:hover { background: var(--lift-2); color: var(--text); }

.au-flow { display: flex; flex-direction: column; align-items: stretch; }
.au-node { position: relative; display: flex; flex-direction: column; gap: 2px;
  padding: 11px 13px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); }
.au-node[data-step] { cursor: pointer; }
.au-node[data-step]:hover { border-color: var(--accent, #4c5bd4); background: var(--lift-2); }
.au-node.trigger { background: var(--panel-2); border-style: dashed; }
.au-node.wait { background: var(--panel-2); }
.au-nkind { font-size: 10px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--subtle); }
.au-node b { font-weight: 600; line-height: 1.4; overflow-wrap: anywhere; }
.au-nsub { font-size: 11.5px; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.au-nmove { position: absolute; top: 8px; right: 8px; display: flex; gap: 3px;
  opacity: 0; transition: opacity .12s ease; }
.au-node:hover .au-nmove, .au-node:focus-within .au-nmove { opacity: 1; }
.au-mv { width: 24px; height: 24px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); color: var(--muted); cursor: pointer; font-size: 12px;
  line-height: 1; }
.au-mv:disabled { opacity: .3; cursor: default; }
.au-link { width: 2px; height: 16px; margin: 0 auto; background: var(--line); }
.au-end { align-self: center; font-size: 11px; color: var(--subtle);
  padding: 4px 12px; border: 1px solid var(--line); border-radius: 999px; }
.au-add { margin-top: 14px; width: 100%; padding: 11px; border: 1px dashed var(--line);
  border-radius: 10px; background: var(--panel); color: var(--muted); font: inherit;
  cursor: pointer; }
.au-add:hover { border-color: var(--accent, #4c5bd4); color: var(--text); }

.au-row { margin-bottom: 14px; }
.au-row > label { display: block; margin-bottom: 4px; font-size: 12px; }
.au-row > input, .au-row > textarea, .au-row > select { width: 100%; }
.au-row > textarea { font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  resize: vertical; }
.au-when { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.au-when select { flex: 0 1 auto; width: auto; min-width: 0; }
.au-when input { width: 84px; }
.au-danger { padding-top: 12px; border-top: 1px solid var(--line); display: flex;
  flex-wrap: wrap; gap: 8px; align-items: center; }
.au-danger > label { width: 100%; }
.au-danger .bk-hint { width: 100%; }
.chip.danger { color: var(--bad, #b91c1c); }

/* Merge fields as buttons: one you have to retype is one somebody gets wrong,
   and {{frist}} does not fill in. */
.au-fields { display: flex; flex-wrap: wrap; gap: 6px; }
.au-field { font: 11.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel-2); color: var(--text); cursor: pointer; }
.au-field:hover, .au-field:focus-visible { background: var(--lift-2); }
/* The real email on a white card whatever the app's theme, because that is the
   ground it will be read on in a mail client. */
.au-preview { border: 1px solid var(--line); border-radius: 9px; padding: 14px;
  background: #fff; color: #141413; max-height: 280px; overflow: auto; }
.au-preview a { color: inherit; }
.au-pv-subj { font-weight: 700; font-size: 13.5px; margin-bottom: 10px;
  padding-bottom: 8px; border-bottom: 1px solid #e8e6e1; }
.bkd.au-step-edit { max-width: 640px; display: flex; flex-direction: column;
  max-height: calc(100dvh - 96px); }
.au-step-edit > .bkd-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; }

.bkd-foot { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border-top: 1px solid var(--line); flex-wrap: wrap; }
.au-msg { font-size: 12px; color: var(--muted); }
.au-msg.bad { color: var(--bad, #b91c1c); }
.au-msg.good { color: var(--good, #15803d); }

.au-qwrap { overflow-x: auto; }
.au-q { min-width: 560px; }
.au-q td { font-variant-numeric: tabular-nums; }
.au-tag { display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--muted); }
.au-tag.on { color: var(--good, #15803d); }
.au-tag.bad { color: var(--bad, #b91c1c); }
.au-tag.off { color: var(--subtle); }

/* The email log, under Settings -> Email. A plain table: the question is
   always "did it leave, and if not why", and the answer is one column. */
.mail-log { margin-top: 12px; overflow-x: auto; }
.mail-log-t { min-width: 480px; font-size: 12.5px; }
.mail-log-t td:last-child { min-width: 170px; }
.ml-why { display: block; line-height: 1.4; white-space: normal; }
.mail-log-t td { vertical-align: top; }
.ml-ok { color: var(--good, #15803d); font-weight: 600; }
.ml-bad { color: var(--bad, #b91c1c); font-weight: 600; }

/* A call that is not ours to score. Dimmed and still there, because "why is
   that one not scored" needs an answer on the page -- hiding it just moves
   the question somewhere nobody can ask it. */
tr.cr-off td { opacity: .5; }
tr.cr-off td.cr-act { opacity: 1; }
.cr-act { text-align: right; white-space: nowrap; }
.chip.sm.ghost { color: var(--muted); }

/* The step picker. Two columns of what-it-does, grouped by what you are
   trying to achieve rather than by which system does it -- a person adding a
   step is thinking "tell them something", not "invoke the mail module". */
.bkd.au-pick { max-width: 580px; }
.au-pick-grid { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 520px) { .au-pick-grid { grid-template-columns: minmax(0, 1fr); } }
.au-pick-one { display: flex; flex-direction: column; gap: 3px; padding: 11px 12px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  text-align: left; font: inherit; cursor: pointer; }
.au-pick-one:hover, .au-pick-one:focus-visible { border-color: var(--accent, #4c5bd4);
  background: var(--lift-2); }
.au-pick-one b { font-weight: 650; }
.au-pick-one span { font-size: 11.5px; color: var(--muted); line-height: 1.45; }
.au-pick .bk-group:first-child { margin-top: 0; }

/* Node colours by kind, so a flow reads at a glance without being read. */
.au-node.sms { background: var(--panel-2); }
.au-node.notify, .au-node.task { background: var(--panel-2); }

/* Live, and whether anything is actually moving through it.
   A steady dot means switched on. A beating one means somebody is in it right
   now. The distinction is the point: an automation can be live and idle, and
   a board where every live card blinks the same way tells you nothing. */
.au-dot-live { flex: none; width: 8px; height: 8px; margin-top: 5px; border-radius: 50%;
  background: var(--good, #15803d); position: relative; }
.au-dot-live.beating::after { content: ""; position: absolute; inset: 0;
  border-radius: 50%; background: inherit; animation: au-beat 2.4s ease-out infinite; }
@keyframes au-beat {
  0%   { transform: scale(1);   opacity: .55; }
  70%  { transform: scale(3.2); opacity: 0; }
  100% { transform: scale(3.2); opacity: 0; }
}
/* Slow and soft on purpose, and gone entirely for anybody who has asked the
   system for less movement. A page that twitches is a page you stop looking at. */
@media (prefers-reduced-motion: reduce) {
  .au-dot-live.beating::after { animation: none; }
  .au-dot-live.beating { box-shadow: 0 0 0 3px color-mix(in srgb, var(--good, #15803d) 28%, transparent); }
}
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* A branch: indented under its "if", with a rail and a Yes/No label.
   Not side by side the way GoHighLevel does it — nesting past one level runs
   out of width fast, and a vertical canvas you pan is already a column. */
.au-branch { position: relative; margin: 10px 0 0 18px; padding-left: 18px; }
.au-branch::before { content: ""; position: absolute; left: 0; top: 0; bottom: 14px;
  width: 2px; border-radius: 2px; background: var(--line); }
.au-branch.yes::before { background: color-mix(in srgb, var(--good, #15803d) 55%, var(--line)); }
.au-branch.no::before  { background: color-mix(in srgb, var(--bad, #b91c1c) 40%, var(--line)); }
.au-bside { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line); color: var(--muted); }
.au-branch.yes .au-bside { color: var(--good, #15803d); }
.au-branch.no  .au-bside { color: var(--bad, #b91c1c); }
.au-bsteps { margin-top: 2px; }
.au-bsteps > .au-link:first-child { height: 10px; }
.au-bempty { margin: 8px 0 0; font-size: 11.5px; color: var(--subtle); font-style: italic; }
.au-add.sm { margin-top: 10px; padding: 7px; font-size: 12px; }
.au-node.if { background: var(--panel-2); border-style: dashed; }

/* The trigger node is a control, so it has to look like one. */
.au-node.trigger { cursor: pointer; }
.au-node.trigger:hover { border-color: var(--accent, #4c5bd4); background: var(--lift-2); }
.au-nchange { position: absolute; top: 10px; right: 12px; font-size: 11px;
  color: var(--muted); opacity: 0; transition: opacity .12s ease; }
.au-node.trigger:hover .au-nchange, .au-node.trigger:focus-within .au-nchange { opacity: 1; }
.au-pick-one.on { border-color: var(--accent, #4c5bd4); background: var(--lift-2); }

/* CONTACT DETAILS ON THE CLIENT CARD.

   The same key/value table the prospect card uses, given room to breathe --
   this one is the whole pane rather than a column in a narrow card, and every
   row here is editable, so each needs space for a control beside the value.
   Stefan, 10-08-2026: he had to open the prospect inbox to find a client's
   own phone number. */
.cc-contact { font-size:13px; }
.cc-contact td { padding:7px 10px 7px 0; }
.cc-contact td:first-child { width:108px; color:var(--subtle); font-size:12px; }
.cc-contact tr + tr td { border-top:1px solid var(--line); }
.cc-c-val { margin-right:8px; }
/* The edit affordance stays quiet until the row is hovered: nine of these
   shouting at once would be the loudest thing on the card. */
.cc-contact .cc-edit { opacity:0; font-size:11px; transition:opacity .12s; }
.cc-contact tr:hover .cc-edit, .cc-contact .cc-edit:focus-visible { opacity:1; }
@media (hover:none) { .cc-contact .cc-edit { opacity:1; } }
.cc-use { font-size:12px; }
.cc-edit-row { display:flex; gap:6px; align-items:flex-start; }
.cc-edit-in {
  flex:1 1 auto; min-width:0; font:inherit; padding:4px 7px;
  border:1px solid var(--line); border-radius:6px;
  background:var(--card); color:var(--text);
}
.cc-rec-warn {
  margin-top:12px; padding:8px 11px; border-radius:8px;
  background:color-mix(in srgb, var(--bad) 9%, transparent);
  border:1px solid color-mix(in srgb, var(--bad) 26%, transparent);
  color:var(--text);
}

/* Where a worked-out value came from, set apart from the value itself.
   Stefan, 10-08-2026, on "Pacific their area code" reading as one phrase:
   "we need to put a dash between that and then italicize 'their area code'
   or 'got this from their area code'." */
.cc-why { font-style:italic; color:var(--subtle); font-size:12px; }

/* THE CONTACTS LIST, beside the Prospects Inbox.

   Stefan, 10-09-2026: "in GHL, you obviously have your contacts where you can
   actually look up the contact, or you can look up the messaging." GHL carries
   both; this page only ever had the messaging. Same destination, same cards --
   a toggle in the rail head, and the table takes the full width while it is up
   because a directory is wide and the four-pane workspace is for reading one
   person. */
/* CONTACTS TAKES THE WHOLE SCREEN, rail included.

   BY ID, AND THAT IS NOT FUSSINESS. The responsive rules further up are
   written `.ws4:not(.cardless):not(.guide-fold):not(.nobody)` -- four classes
   of specificity, which beat a two-class `.ws4.on-dir` wherever they both
   match, whatever the source order. With two classes the grid collapsed only
   while nobody was selected, so opening a contact and coming back put the
   table in the narrow list column. An id outranks any number of classes, and
   the element has one. */
#ws4.on-dir { grid-template-columns:minmax(0,1fr); }
/* Named panes, not `> aside` -- the notes panel and the client panel are
   asides too, and hiding those would break them in this mode only. */
#ws4.on-dir > .ws-rail,
#ws4.on-dir > .ws-rail-grip,
#ws4.on-dir > .ws-list,
#ws4.on-dir > .ws-thread,
#ws4.on-dir > .ws-card { display:none; }
@media (max-width: 900px) {
  #ws4.on-dir { height:auto; }
  .ws-dir { min-height:70vh; }
}

.ws-dir { display:flex; flex-direction:column; min-height:0; min-width:0;
  border:1px solid var(--line); border-radius:14px; background:var(--panel);
  overflow:hidden; }
.ws-dir[hidden] { display:none; }
.dir-head {
  display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:11px 14px; border-bottom:1px solid var(--line); background:var(--panel);
}
.dir-head input[type="search"] {
  flex:1 1 300px; min-width:0; font:inherit; font-size:13px; padding:7px 11px;
  border:1px solid var(--line); border-radius:8px; background:var(--card); color:var(--text);
}
.dir-head .helper { margin-left:auto; font-variant-numeric:tabular-nums; }
.dir-wrap { flex:1 1 auto; min-height:0; overflow:auto; }
.dir-table { width:100%; border-collapse:collapse; font-size:13px; }
.dir-table thead th {
  position:sticky; top:0; z-index:1; text-align:left; font-size:10.5px; font-weight:700;
  letter-spacing:.05em; text-transform:uppercase; color:var(--subtle);
  padding:8px 14px; background:var(--panel); border-bottom:1px solid var(--line);
}
.dir-table td { padding:9px 14px; border-bottom:1px solid var(--line); vertical-align:top; }
.dir-table tbody tr { cursor:pointer; }
.dir-table tbody tr:hover { background:var(--lift-1); }
.dir-table tbody tr:focus-visible { outline:2px solid var(--blue); outline-offset:-2px; }
.dir-nm { width:21%; }
.dir-nm b { display:inline-block; margin-right:6px; }
.dir-client { font-size:9.5px; }
.dir-tel { width:14%; white-space:nowrap; font-variant-numeric:tabular-nums; }
/* The number and the button that rings it, on one line. The button only
   appears for an account that can place a call, so the cell is just the
   number for everybody else and needs no second layout. */
.dc-phone { white-space:nowrap; }
.dir-num { font-variant-numeric:tabular-nums; }
.dc-phone .ws-dial { margin-left:8px; vertical-align:middle; }
.dir-em { width:24%; overflow-wrap:anywhere; }
.dir-tags { width:28%; line-height:1.9; }
.dir-last { width:13%; white-space:nowrap; color:var(--subtle); }
.dir-none { color:var(--muted); }
.dir-more { font-size:10.5px; color:var(--subtle); }
.dir-empty { padding:28px 14px !important; text-align:center; color:var(--subtle); }
.dir-foot {
  display:flex; align-items:center; gap:12px; justify-content:center;
  padding:10px 14px; border-top:1px solid var(--line); background:var(--panel);
}
.dir-foot .helper { font-variant-numeric:tabular-nums; }
@media (max-width:760px) {
  .dir-tags, .dir-last { display:none; }
  .dir-nm { width:38%; } .dir-tel { width:28%; } .dir-em { width:34%; }
}

/* SMART LISTS, AS A TAB BAR. Stefan, 10-09-2026, with a GoHighLevel
   screenshot: "the smart list need to be 'quick clickable' from the contact
   list view". So they sit where his do -- across the top, All on the left,
   + Add on the right -- rather than as chips under a toolbar.

   And they are objects, not filters: "we should be able to edit the smart
   list. It's not something that is a filter tag that we just exit out of."
   Hence the ⋯ on each tab rather than a bare ×. */
.dir-tabs { display:flex; flex-wrap:wrap; align-items:center; gap:2px;
  padding:4px 12px 0; border-bottom:1px solid var(--line); background:var(--panel); }
.dir-tab-wrap { display:inline-flex; align-items:stretch; position:relative; }
.dir-tab { font:inherit; font-size:12.5px; font-weight:600; border:0; background:transparent;
  color:var(--subtle); cursor:pointer; padding:9px 11px; border-bottom:2px solid transparent;
  margin-bottom:-1px; white-space:nowrap; max-width:200px; overflow:hidden;
  text-overflow:ellipsis; }
.dir-tab:hover { color:var(--text); }
.dir-tab.on { color:var(--blue); border-bottom-color:var(--blue); }
.dir-tab-add { color:var(--muted); font-weight:650; }
.dir-tab-add:hover { color:var(--blue); }
/* The menu is quiet until the tab is the one you are on, or hovered -- a row
   of eight tabs each shouting an ellipsis is a row of sixteen controls. */
.dir-tab-menu { font:inherit; font-size:13px; line-height:1; border:0; background:transparent;
  color:var(--muted); cursor:pointer; padding:0 7px 0 0; margin-bottom:-1px;
  opacity:0; transition:opacity .12s; }
.dir-tab-wrap:hover .dir-tab-menu, .dir-tab-wrap.on .dir-tab-menu,
.dir-tab-menu:focus-visible { opacity:1; }
.dir-tab-menu:hover { color:var(--text); }
@media (hover:none) { .dir-tab-menu { opacity:1; } }
/* Fixed, because the tab bar scrolls and a menu clipped by its own container
   is a menu you cannot reach the bottom of. */
.dir-menu { position:fixed; z-index:80; min-width:260px; max-width:300px;
  background:var(--panel); border:1px solid var(--line-strong); border-radius:11px;
  box-shadow:0 12px 32px rgba(0,0,0,.26); padding:5px; }
.dir-menu-what { font-size:11px; color:var(--subtle); font-style:italic;
  padding:6px 9px 7px; border-bottom:1px solid var(--line); margin-bottom:4px;
  overflow-wrap:anywhere; }
.dir-menu button { display:block; width:100%; text-align:left; font:inherit; font-size:13px;
  border:0; background:transparent; color:var(--text); cursor:pointer;
  padding:7px 9px; border-radius:7px; }
.dir-menu button small { display:block; font-size:11px; color:var(--subtle); margin-top:2px;
  overflow-wrap:anywhere; }
.dir-menu button:hover:not(:disabled) { background:var(--lift-2); }
.dir-menu button:disabled { color:var(--muted); cursor:default; }
.dir-menu button:disabled small { color:var(--muted); }
.dir-menu-bad { color:var(--ink-bad) !important; }
.dir-menu-bad:hover { background:color-mix(in srgb, var(--bad) 12%, transparent) !important; }

/* A COLOUR PER SMART LIST. Stefan, 10-09-2026: "each smart list needs to have
   its own gradient color or definitive pill. That way, we know it's a separate
   item/list." A dot carries the identity and the underline says which one you
   are in -- two signals for two different jobs. Eight filled pills would be
   louder and would stop the ACTIVE list standing out at all, which is the one
   thing the bar has to say. */
.lc-violet { --lc:#8b5cf6; }
.lc-green  { --lc:#35c98f; }
.lc-coral  { --lc:#f0997b; }
.lc-blue   { --lc:#4387f5; }
.lc-amber  { --lc:#f5ae4e; }
.lc-pink   { --lc:#ed93b1; }
.lc-teal   { --lc:#5dcaa5; }
.lc-red    { --lc:#fb6472; }
.dir-tab-dot { display:inline-block; width:7px; height:7px; border-radius:50%;
  background:var(--lc, var(--muted)); margin-right:6px; vertical-align:1px; flex:0 0 auto; }
.dir-tab-wrap .dir-tab.on { color:var(--text); border-bottom-color:var(--lc, var(--blue)); }
.dir-swatch { width:17px; height:17px; border-radius:50%; border:2px solid transparent;
  background:var(--lc); cursor:pointer; padding:0; }
.dir-swatch.on { border-color:var(--text); }
.dir-menu-colors { display:flex; gap:6px; padding:8px 9px 4px; margin-top:4px;
  border-top:1px solid var(--line); }

/* THE COLUMN PICKER. Stefan, 10-09-2026: "can we add a column picker like GHL
   has?" Eighteen real columns, grouped by what you are asking, with Name
   locked because a row without it is unusable. */
.dir-cols { position:fixed; z-index:80; width:min(400px, calc(100vw - 20px));
  background:var(--panel); border:1px solid var(--line-strong); border-radius:12px;
  box-shadow:0 14px 36px rgba(0,0,0,.28); padding:11px 12px 9px;
  max-height:min(70vh, 560px); overflow:auto; }
.dir-cols-head { display:flex; align-items:center; justify-content:space-between;
  gap:10px; margin-bottom:9px; }
.dir-cols-grp { margin:9px 0 4px; font-size:10px; letter-spacing:.06em;
  text-transform:uppercase; color:var(--subtle); }
.dir-cols-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr));
  gap:1px 10px; }
.dir-col { display:flex; align-items:center; gap:7px; font-size:12.5px; color:var(--text);
  padding:4px 5px; border-radius:6px; cursor:pointer; }
.dir-col:hover { background:var(--lift-2); }
.dir-col.locked { color:var(--muted); cursor:default; }
.dir-col.locked:hover { background:none; }
.dir-col input { flex:0 0 auto; margin:0; }
.dir-cols-foot { margin:10px 0 0; padding-top:8px; border-top:1px solid var(--line);
  font-size:11.5px; color:var(--subtle); }
#dirCols { display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }

/* HOW FAR THROUGH THEIR PROGRAM. Stefan, 10-09-2026: "show week and month in
   live roster view and status completion bar for their program." The bar is
   read first and the words confirm it, so the bar sits on top. Four tones, by
   how close the term is to its end -- not a judgement on the client, just how
   much runway is left before a re-sign conversation has to happen. */
.ros-prog { white-space:nowrap; min-width:104px; }
/* The track is a GROOVE, not a grey box: an inset shadow gives the fill
   something to sit in, which is most of why a flat pair of rectangles reads
   as unfinished. Stefan, 10-09-2026: "I just feel right now they're very
   basic and elementary." */
.ros-bar {
  display:block; width:92px; height:7px; border-radius:999px; overflow:hidden;
  background:var(--bar-track, rgba(255,255,255,.07));
  box-shadow:inset 0 1px 2px var(--bar-groove, rgba(0,0,0,.45));
}
/* The fill runs the house gradient ALONG the bar -- blue into violet, the
   same pair the brand buttons use -- and carries a soft bloom of its own
   colour, so it reads as lit rather than painted on. */
.ros-bar > span {
  display:block; height:100%; border-radius:999px;
  background:linear-gradient(90deg, var(--bar-a), var(--bar-b));
  box-shadow:0 0 9px var(--bar-glow);
  transition:width .35s cubic-bezier(.22,.61,.36,1);
  --bar-a:#4387f5; --bar-b:#8b5cf6; --bar-glow:rgba(99,120,245,.55);
}
.ros-bar.late > span { --bar-a:#f0b24e; --bar-b:#ef7f52; --bar-glow:rgba(240,160,70,.5); }
.ros-bar.over > span { --bar-a:#f0556b; --bar-b:#d8427f; --bar-glow:rgba(230,70,110,.5); }
/* A bar that has barely begun still has to be SEEN -- a 2% sliver is a
   hairline otherwise. Zero is deliberately excluded: a term that has not
   started yet shows an empty groove, not a nub claiming a day of coaching. */
.ros-bar > span[style*="width:1%"], .ros-bar > span[style*="width:2%"],
.ros-bar > span[style*="width:3%"] { min-width:7px; }
.ros-bar.sm { width:76px; height:5px; display:inline-block; margin-left:7px;
  vertical-align:middle; }
.ros-bar.wide { width:150px; height:9px; flex:0 0 auto;
  box-shadow:inset 0 1px 3px var(--bar-groove, rgba(0,0,0,.5)); }
.ros-bar.wide > span { box-shadow:0 0 12px var(--bar-glow); }
.ros-prog-t { display:block; margin-top:4px; font-size:10.5px; color:var(--subtle);
  font-variant-numeric:tabular-nums; letter-spacing:.01em; }
@media (prefers-reduced-motion: reduce) { .ros-bar > span { transition:none; } }
/* Light mode: a groove in a pale surface is a soft grey dent, not a black
   one, and the bloom has to come right down or it reads as a smudge. */
:root[data-theme="light"] .ros-bar { --bar-track:rgba(24,24,20,.08);
  --bar-groove:rgba(24,24,20,.14); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ros-bar { --bar-track:rgba(24,24,20,.08);
    --bar-groove:rgba(24,24,20,.14); }
  :root:not([data-theme="dark"]) .ros-bar > span { box-shadow:0 0 6px var(--bar-glow); }
}
:root[data-theme="light"] .ros-bar > span { box-shadow:0 0 6px var(--bar-glow); }

/* The term's progress on the client card, which has the room the roster cell
   has not. Its own line under the dates -- it spent a few minutes nested
   inside them, which pushed the GHL link onto a line of its own. */
.cc-term { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:3px; }
.cc-term b { color:var(--text); font-weight:650; }
.cc-term-left { color:var(--subtle); }
@media (max-width:560px) { .ros-bar.wide { width:100%; } }

/* SORTING BY A COLUMN. Stefan, 10-09-2026: "maybe an up and down arrow
   filter ... by clicking an up and/or down arrow over the columns (such as
   roster status, coach, program)." The arrow is quiet until the header is
   hovered or the column is the one in force -- eighteen live arrows across a
   header is a row of noise. */
.dir-table th.dir-sortable { padding:0; }
.dir-table th.dir-sortable button {
  display:flex; align-items:center; gap:5px; width:100%; font:inherit;
  font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  color:var(--subtle); background:transparent; border:0; cursor:pointer;
  padding:8px 14px; text-align:left;
}
.dir-table th.dir-sortable button:hover { color:var(--text); background:var(--lift-1); }
.dir-table th.dir-sortable.on button { color:var(--blue); }
.dir-arrow { opacity:0; font-size:11px; line-height:1; transition:opacity .12s; }
.dir-table th.dir-sortable:hover .dir-arrow,
.dir-table th.dir-sortable.on .dir-arrow,
.dir-table th.dir-sortable button:focus-visible .dir-arrow { opacity:1; }
.dir-table th.dir-sortable.on .dir-arrow { opacity:1; }
@media (hover:none) { .dir-arrow { opacity:.6; } }

/* WHO IS ON A WORKFLOW, opened under its own row. Stefan, 10-09-2026: "when
   I click on tier 1, tier 2, or tier 3 ... it should open up and show all the
   people who are subscribed to that." Beside the rule rather than in a
   dialog, because the question is "is the right set of people on this" and
   that is answered by reading the two together. */
.ci-who { font-variant-numeric:tabular-nums; font-weight:650; }
.ci-people-row > td { background:var(--lift-1); padding:0 !important; }
.ci-who-wrap { padding:11px 14px 13px; }
.ci-who-head { margin:0 0 9px; }
.ci-who-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));
  gap:7px; }
.ci-who-one { display:block; padding:8px 11px; border:1px solid var(--line);
  border-radius:9px; background:var(--panel); color:inherit; text-decoration:none; }
.ci-who-one:hover { border-color:var(--line-strong); background:var(--lift-2); }
.ci-who-one b { display:block; font-size:13px; }
.ci-who-one .helper { display:block; font-size:11.5px; margin-top:1px; }
.ci-who-state { color:var(--subtle); }
.ci-who-one.off { opacity:.6; }

/* A row for somebody enrolled whose first check-in has not been generated.
   Quieter than a real one, because nothing is owed on it -- but present, so
   the page can answer "who is on check-ins" before the first Sunday rather
   than looking broken. Stefan, 10-09-2026. */
.ci-row.ci-waiting td { opacity:.74; }
.ci-row.ci-waiting td:first-child { opacity:1; }

/* THE FORM EDITOR. Stefan, 10-09-2026: "I just think we need the UI, UX, and
   overall use of the form editing to be way cleaner than what it is right
   now." A question was a cramped one-line row with no way to set the answers
   it offered. It is a card now: numbered, with whatever that TYPE needs
   underneath it. */
.ci-q { border:1px solid var(--line); border-radius:12px; background:var(--panel);
  margin-bottom:10px; overflow:hidden; }
.ci-q:focus-within { border-color:var(--line-strong); }
.ci-q-head { display:flex; align-items:center; gap:9px; padding:10px 12px;
  border-bottom:1px solid var(--line); }
/* The number, which the old row never had. A heading gets a mark instead:
   numbering a signpost makes the question after it look like a sub-point. */
.ci-q-n { flex:0 0 auto; min-width:24px; height:24px; padding:0 6px; border-radius:7px;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:11.5px; font-weight:750; font-variant-numeric:tabular-nums;
  background:var(--lift-3); color:var(--subtle); }
.ci-q-content .ci-q-n { background:color-mix(in srgb, var(--blue) 20%, transparent);
  color:var(--blue); }
.ci-q-content .ci-q-head { background:var(--lift-1); }
.ci-qlabel { flex:1 1 auto; min-width:0; font:inherit; font-size:13.5px; font-weight:600;
  border:0; background:transparent; color:var(--text); padding:5px 2px; }
.ci-qlabel:focus { outline:0; box-shadow:0 1px 0 var(--blue); }
.ci-q-kind { flex:0 0 auto; width:auto; min-width:142px; font-size:12px; padding:5px 26px 5px 8px; }
.ci-q-tools { display:flex; gap:1px; flex:0 0 auto; }
.ci-q-tools .ci-arrow { opacity:.55; }
.ci-q:hover .ci-q-tools .ci-arrow, .ci-q-tools .ci-arrow:focus-visible { opacity:1; }
.ci-arrow.ci-x:hover { color:var(--ink-bad); }
.ci-q-body { padding:10px 12px 12px; display:flex; flex-direction:column; gap:9px; }
.ci-qhelp { font:inherit; font-size:12.5px; padding:6px 9px; border:1px solid var(--line);
  border-radius:8px; background:var(--card); color:var(--text); }
.ci-q-req { font-size:12.5px; }

/* The panel that belongs to the TYPE. This is the part that did not exist:
   a Single choice offered no way to say what the answers were. */
.ci-sub { border:1px dashed var(--line-strong); border-radius:9px; padding:10px 11px;
  background:var(--lift-1); }
.ci-sub-h { display:block; font-size:11px; font-weight:700; letter-spacing:.04em;
  text-transform:uppercase; color:var(--subtle); margin-bottom:7px; }
.ci-opts { display:flex; flex-direction:column; gap:5px; }
.ci-opt { display:flex; align-items:center; gap:6px; }
.ci-opt-n { flex:0 0 auto; width:19px; text-align:center; font-size:11px;
  color:var(--muted); font-variant-numeric:tabular-nums; }
.ci-opt input { flex:1 1 auto; min-width:0; font:inherit; font-size:12.5px;
  padding:5px 9px; border:1px solid var(--line); border-radius:7px;
  background:var(--card); color:var(--text); }
.ci-scale { display:grid; grid-template-columns:repeat(auto-fit, minmax(118px, 1fr));
  gap:8px; }
.ci-scale label { display:flex; flex-direction:column; gap:3px; font-size:11.5px;
  color:var(--subtle); }
.ci-scale input { font:inherit; font-size:12.5px; padding:5px 8px;
  border:1px solid var(--line); border-radius:7px; background:var(--card);
  color:var(--text); }
@media (max-width:620px) {
  .ci-q-head { flex-wrap:wrap; }
  .ci-q-kind { width:100%; min-width:0; order:3; }
}

/* The builder's preview is the REAL client page in a frame, not a drawing of
   it. Stefan, 10-10-2026: "when I'm previewing it as the client on mobile and
   desktop, I want to see it exactly how they see it: colors, layouts,
   everything." The frame carries the client ground behind it so there is no
   white flash before the page paints. */
.fb-pv-frame { display:block; width:100%; height:min(78vh, 900px); border:0;
  background:#07090a; border-radius:10px; }
.fb-pv-frame.mobile { width:390px; max-width:100%; margin:0 auto;
  height:min(78vh, 820px); border:1px solid rgba(255,255,255,.18);
  border-radius:22px; }
.fb-preview-body { background:#07090a; padding:14px; border-radius:12px; }

/* SORTED BY, in order, each one removable. Stefan, 10-10-2026, wanting what
   ClickUp does: sort by program AND progress, and take either off without
   disturbing the other. The number is the only place the ORDER shows, and the
   order is what decides what the roster looks like -- so it appears as soon
   as there is a second one and not before. */
.sort-chips { display:flex; flex-wrap:wrap; align-items:center; gap:6px;
  margin:10px 0 0; }
.sort-chips[hidden] { display:none; }
.sort-chip { display:inline-flex; align-items:center; gap:6px; font-size:12px;
  padding:4px 4px 4px 10px; border-radius:999px; color:var(--subtle);
  background:var(--lift-1); border:1px solid var(--line); }
.sort-chip i { font-style:normal; font-size:9.5px; font-weight:800;
  width:15px; height:15px; border-radius:50%; display:inline-grid;
  place-items:center; background:var(--blue); color:#fff; }
.sort-chip b { color:var(--text); font-size:12px; }
.sort-chip button { font:inherit; font-size:12px; line-height:1; border:0;
  background:transparent; color:var(--muted); cursor:pointer;
  padding:3px 6px; border-radius:999px; }
.sort-chip button:hover { color:var(--ink-bad);
  background:color-mix(in srgb, var(--bad) 14%, transparent); }
/* The rank on the header, so the table says the same thing as the chips. */
th.sortable[data-rank]:not([data-rank=""])::after {
  content:attr(data-rank); display:inline-grid; place-items:center;
  width:14px; height:14px; margin-left:5px; border-radius:50%;
  background:var(--blue); color:#fff; font-size:9px; font-weight:800;
  vertical-align:1px;
}

/* Which sort a column is, when more than one is on. Same idea as the
   roster's chips: the order decides what the table looks like, so it has to
   be visible. One sort needs no number -- there is nothing for it to be
   first of. */
.dir-rank { font-style:normal; display:inline-grid; place-items:center;
  width:14px; height:14px; margin-left:4px; border-radius:50%;
  background:var(--blue); color:#fff; font-size:9px; font-weight:800; }


/* A CONTROL THAT HAS JUST COPIED SOMETHING. hubCopy (common.js) sets this for
   about a second and a half alongside changing the word, because the toast
   lands at the foot of the page and a client card or a panel is usually over
   it -- Stefan, 10-10-2026: "it's in the background, and it's hard to see."
   This lived only under .libscope, so every Copy outside Resources changed
   its label and showed nothing. Last in the file on purpose: it has to beat
   .button.secondary and friends on equal specificity, without !important. */
.copied, .button.copied, .chip.copied, .link-btn.copied {
  background: color-mix(in srgb, var(--green) 16%, transparent);
  border-color: color-mix(in srgb, var(--green) 55%, transparent);
  color: var(--green);
}

/* Check-ins → Forms: the version pill over the date it was made. Inline, the
   pill's padding hung over the line below and sat on the date. */
.ci-ver .badge { display: inline-block; }
.ci-ver .helper { margin-top: 6px; white-space: nowrap; }

/* Pricing → Landing page videos. */
.pr-vid + .pr-vid { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.pr-vid-row { display: flex; gap: 8px; margin-top: 4px; }
.pr-vid-row input { flex: 1 1 auto; min-width: 0; }
.pr-vid-said { margin: 6px 0 0; }

/* Pricing → Landing pages: a card per self-checkout page, the live page
   framed as a phone or a desktop, scaled down. */
.pr-lp-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: 16px; }
.pr-lp { display: flex; flex-direction: column; gap: 12px; }
.pr-lp-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.pr-lp-head h3 { margin: 0; }
/* The address opens the live page; Copy beside it copies. Stefan, 10-16-2026:
   clicking the address only copied it, and he opens these more than he copies. */
.pr-lp-addr { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.pr-lp-url { color: var(--ink-blue, var(--blue)); font-size: 12.5px; text-decoration: none; }
.pr-lp-url:hover { text-decoration: underline; }
.pr-lp-copy { padding: 2px 9px; cursor: pointer; }
.pr-lp-copy:hover { color: var(--text); background: var(--lift-2); }
/* The funnel, step by step. A row of chips above the frame: pressing one
   swaps what the frame is showing, so the whole journey is one card rather
   than nine pages to remember. Scrolls sideways on a narrow screen rather
   than wrapping into a block that pushes the frame off the bottom. */
.pr-steps { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 3px;
  scrollbar-width: thin; }
.pr-step { display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  padding: 6px 12px 6px 7px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--muted); font: inherit; font-size: 12.5px;
  font-weight: 600; cursor: pointer; white-space: nowrap; }
.pr-step:hover { border-color: var(--line-strong); color: var(--text); }
.pr-step.on { background: var(--brand, var(--ink-info)); border-color: transparent; color: #fff; }
.pr-step.on .pr-step-n { background: rgba(255,255,255,.26); color: #fff; }
/* Greyed, not hidden: a step one programme does not get is information. */
.pr-step.off { opacity: .55; }
.pr-step.off .pr-step-n { background: var(--line); }
/* A circle for one number, a pill for a range: the Client Hub chip covers two
   of the client's steps (set a password, then the form) and says "5-6". */
.pr-step-n { display: grid; place-items: center; min-width: 19px; height: 19px; flex: 0 0 auto;
  padding: 0 5px; border-radius: 10px; background: var(--lift-2); font-size: 11px;
  font-weight: 800; font-variant-numeric: tabular-nums; }
.pr-step-note { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
/* A step that belongs to Stripe or Trainerize, said rather than drawn. */
.pr-lp-outside { padding: 26px 22px; text-align: center; max-width: 46ch; margin: 0 auto;
  display: grid; gap: 10px; place-items: center; color: var(--muted); font-size: 13.5px;
  line-height: 1.55; }
.pr-out-tag { margin: 0; font-size: 11px; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--subtle); }
.pr-lp-outside p { margin: 0; }

.pr-lp-stage { display: flex; justify-content: center; align-items: flex-start; background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 12px; padding: 16px; overflow: hidden; }
.pr-lp-view { position: relative; overflow: hidden; background: #07090a; flex: none; }
.pr-lp-view iframe { position: absolute; top: 0; left: 0; border: 0; transform-origin: 0 0; pointer-events: auto; }
/* Phone: a 390 x 844 page at half size, in a phone's outline. */
.pr-lp-view.phone { width: 195px; height: 422px; border-radius: 22px; box-shadow: 0 0 0 6px #1d2326, 0 12px 28px rgba(0,0,0,.35); }
.pr-lp-view.phone iframe { width: 390px; height: 844px; transform: scale(.5); }
/* Desktop: a 1280 x 800 page at 0.34, in a screen's outline. */
.pr-lp-view.desktop { width: 435px; height: 272px; border-radius: 8px; box-shadow: 0 0 0 5px #1d2326, 0 12px 28px rgba(0,0,0,.35); }
.pr-lp-view.desktop iframe { width: 1280px; height: 800px; transform: scale(.34); }
@media (max-width: 560px) { .pr-lp-view.desktop { width: 290px; height: 181px; } .pr-lp-view.desktop iframe { transform: scale(.2266); } }
.pr-lp-acts { flex-wrap: wrap; }
.pr-lp-vid { border-top: 1px solid var(--line); padding-top: 12px; }
.pr-flash { box-shadow: 0 0 0 2px var(--blue) !important; transition: box-shadow .3s; }

/* Landing cards fold to a slim row; Phone or Desktop opens them. */
.pr-lp-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.pr-lp.folded { gap: 10px; }
.pr-lp.folded .pr-lp-stage { display: none; }

/* ============================================================
   Check-in answers — one card per question
   ============================================================
   The cards are deliberately not uniform: a 1-10 scale, a bucket question, a
   yes/no and a paragraph are four different shapes of truth, and forcing them
   into one tile would flatten the only thing worth reading. Stefan,
   10-13-2026: "cards showing what people are saying." */
.ca-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(330px, 1fr));
  gap:14px; align-items:start; }
.ca-card { display:flex; flex-direction:column; gap:12px; }
.ca-head { display:flex; align-items:flex-start; justify-content:space-between; gap:14px; }
.ca-head h3 { line-height:1.35; }
.ca-skip { color:var(--ink-warn); font-weight:650; border-bottom:1px dotted currentColor; cursor:help; }
/* The one number the card exists to say. */
.ca-big { flex:0 0 auto; text-align:right; font-size:30px; font-weight:800; letter-spacing:-.03em;
  line-height:1; color:var(--text); white-space:nowrap; }
.ca-big span { display:block; margin-top:5px; font-size:10.5px; font-weight:650; letter-spacing:.04em;
  text-transform:uppercase; color:var(--subtle); }
.ca-big.ca-text { font-size:17px; letter-spacing:-.01em; max-width:190px; white-space:normal; }
.ca-big.ca-warn { color:var(--ink-warn); }
.ca-thin { margin:0; font-size:11.5px; color:var(--ink-warn); background:var(--tint-amber);
  border-radius:8px; padding:7px 10px; }
.ca-note { margin:0; font-size:12px; color:var(--muted); }

.ca-bars { display:flex; flex-direction:column; gap:4px; }
.ca-bar { display:grid; grid-template-columns:minmax(58px, auto) 1fr auto; gap:9px;
  align-items:center; font-size:11.5px; color:var(--muted); }
.ca-bar-l { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ca-bar-t { height:9px; border-radius:999px; background:var(--lift-2); overflow:hidden; }
.ca-bar-t i { display:block; height:100%; border-radius:999px; background:var(--btn-grad);
  min-width:2px; transition:width .25s ease; }
.ca-bar-n { font-variant-numeric:tabular-nums; font-weight:700; color:var(--text); }
.ca-bar-n em { font-style:normal; font-weight:600; color:var(--subtle); font-size:10.5px; }
.ca-bar.top .ca-bar-l { color:var(--text); font-weight:650; }
/* An option nobody picked still has to be there, or the scale lies. */
.ca-bar.none { opacity:.45; }
.ca-bar.none .ca-bar-t i { background:var(--lift-3); }

/* One colour whichever way it goes: Energy up is good and Stress up is not,
   and the page cannot tell which question it is drawing. */
.ca-spark { display:flex; align-items:center; gap:10px; color:var(--blue); }
.ca-spark svg { width:150px; height:34px; flex:0 0 auto; }

/* The words, sized by how often they were said. A tag cloud is the wrong tool
   for most things and the right one here: the question is literally "what are
   people saying", and the answer is a handful of words said a lot. */
.ca-words { display:flex; flex-wrap:wrap; gap:6px; align-items:baseline; }
.ca-word { display:inline-flex; align-items:baseline; gap:5px; padding:3px 9px; border-radius:999px;
  background:var(--lift-2); color:var(--text); font-weight:650; line-height:1.3; }
.ca-word.phrase { background:var(--tint-blue); color:var(--blue-on-tint); }
.ca-word b { font-size:10px; font-weight:800; color:var(--subtle); }
.ca-word.phrase b { color:inherit; opacity:.75; }

/* And the sentences themselves, because a word count is a signpost and the
   answer is the thing. */
.ca-says { display:flex; flex-direction:column; gap:8px; }
.ca-say { margin:0; padding:9px 11px; border-left:2px solid var(--line-strong);
  background:var(--lift-1); border-radius:0 9px 9px 0; }
.ca-say p { margin:0; font-size:12.5px; line-height:1.5; color:var(--text); }
.ca-say footer { margin-top:5px; display:flex; gap:8px; align-items:baseline; font-size:11px; }
.ca-say footer a { color:var(--blue); font-weight:650; }
@media (max-width:680px) {
  .ca-grid { grid-template-columns:1fr; }
  .ca-head { flex-direction:column; gap:8px; }
  .ca-big { text-align:left; }
}

/* The two halves of the answers page. Numbers are scanned for a figure that
   moved; words are read. Mixing them made you change gear at every card. */
.ca-band { margin:26px 2px 12px; }
.ca-band-split { border-top:1px solid var(--line); padding-top:24px; margin-top:34px; }
.ca-band-t { margin:0; font-size:16px; letter-spacing:-.02em; }
.ca-band-s { margin:3px 0 0; color:var(--subtle); font-size:12px; }
/* The written cards carry sentences, so they get room to be sentences. */
.ca-grid-wide { grid-template-columns:repeat(auto-fill, minmax(420px, 1fr)); }
@media (max-width:680px) { .ca-grid-wide { grid-template-columns:1fr; } }

/* The two views of the check-in data, as a tab row on the page rather than a
   sixth entry in the Clients strip. Anchors, not buttons, so each is a real
   address — and anchors need the link colour taken back off them. */
.ci-subtabs { margin-bottom:2px; }
.ci-subtabs .tb-btn { text-decoration:none; }
.ci-subtabs .tb-btn:hover { color:var(--text); }
.ci-subtabs .tb-btn.active:hover { color:#fff; }

/* ============================================================
   Walkthrough videos
   ============================================================
   The guide panel gains a film above the words. Both, not either: text is
   scannable and a video is not -- "what does the amber mean" is a three-second
   read and a forty-second scrub -- but "show me how" is the other way round.
   One ? opens both; there is no second icon, because two controls for one job
   is the thing we keep agreeing not to ship. */
.sop-film { margin:0 0 14px; border:1px solid var(--line); border-radius:12px;
  overflow:hidden; background:var(--sink-1); }
.sop-film video { display:block; width:100%; height:auto; background:#07090a; }
.sop-film-foot { display:flex; align-items:center; gap:8px; padding:8px 11px;
  font-size:11.5px; color:var(--muted); }
.sop-film-foot b { color:var(--text); font-weight:700; }
.sop-film-foot .gap { flex:1; }
.sop-chapters { display:flex; flex-wrap:wrap; gap:5px; padding:0 11px 10px; }
.sop-chap { border:1px solid var(--line); border-radius:999px; background:transparent;
  color:var(--muted); font-size:10.5px; font-weight:650; padding:3px 9px; cursor:pointer; }
.sop-chap:hover { background:var(--lift-2); color:var(--text); }
.sop-chap b { font-variant-numeric:tabular-nums; color:var(--blue); font-weight:700; }
/* The play glyph in the search list, so a walkthrough does not read as a page. */
.gs-kind-howto svg { width:13px; height:13px; }
.sop-film-page { max-width:860px; margin:0 0 22px; }

/* ---- the Prospects Inbox as a pipeline board ------------------------------
   Stefan, 10-14-2026: the inbox "from a pipeline view", beside the list rather
   than instead of it. The rail stays; the list, thread and card give way to a
   column per stage. By id for the same reason as Contacts above: the
   responsive .ws4 rules carry four classes and would win otherwise. */
.ws-layout { display:grid; grid-template-columns:1fr 1fr; gap:2px; margin:0 6px 10px; padding:2px;
  border:1px solid var(--line); border-radius:9px; background:var(--lift-1); }
.ws-layout button { display:inline-flex; align-items:center; justify-content:center; gap:5px; font:inherit; font-size:11.5px; font-weight:700;
  padding:5px 6px; border:0; border-radius:7px; background:transparent; color:var(--muted); cursor:pointer; }
.ws-layout button:hover { color:var(--text); }
.ws-layout button[aria-pressed="true"] { background:var(--panel); color:var(--text); box-shadow:0 1px 2px rgba(0,0,0,.12), 0 0 0 1px var(--line); }
@media (max-width: 900px) { .ws-layout { display:none; } }

#ws4.on-board { grid-template-columns:var(--ws-rail-w, 176px) minmax(0,1fr); height:calc(100vh - 118px); }
#ws4.on-board > .ws-list,
#ws4.on-board > .ws-thread,
#ws4.on-board > .ws-card,
#ws4.on-board > .ws-npanel { display:none; }
#ws4.on-board > .ws-rail { display:block; }

.ws-board { display:flex; flex-direction:column; min-width:0; min-height:0; background:var(--panel); }
.ws-board[hidden] { display:none; }
.pb-head { display:flex; flex-direction:column; gap:6px; padding:10px 14px 6px; border-bottom:1px solid var(--line); }
.pb-tools { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.pb-tools #wsSearch { flex:0 1 320px; min-width:180px; }
.pb-tools #wsDays { flex:0 0 auto; width:auto; padding:6px 26px 6px 8px; font-size:12px; }
.pb-tools .ws-chanbar { display:flex; gap:4px; padding:0; border:0; margin-left:auto; }
/* One line each up here: the list's tall two-line buttons would make the head
   taller than the cards under it. */
.pb-tools .ws-chanbtn { display:inline-flex; flex-direction:row; align-items:center; gap:6px; padding:6px 10px; min-height:0; font-size:12px; }
.pb-tools .ws-chanbtn b { font-size:11px; color:var(--muted); }
.pb-sub { font-size:12px; }
.pb-sub b { color:var(--text); }
.pb-team .ws-team { padding:6px 14px; }

/* The columns and, over them, the card. */
.pb-stage { position:relative; flex:1 1 auto; min-height:0; display:flex; }
.pb-cols { flex:1 1 auto; min-width:0; min-height:0; display:flex; gap:10px; padding:12px 14px 14px; overflow-x:auto; overflow-y:hidden;
  align-items:stretch; scrollbar-width:thin; }
.pb-col { --col-accent:var(--blue); flex:0 0 272px; display:flex; flex-direction:column; min-height:0; min-width:0;
  border:1px solid var(--line); border-top:3px solid var(--col-accent); border-radius:12px; background:var(--lift-1);
  transition:border-color .12s, background .12s; }
.pb-col.g-pitch { --col-accent:var(--ink-violet, #8b5cf6); }
.pb-col.g-booked { --col-accent:var(--green); }
.pb-col.g-held, .pb-col.g-follow_up { --col-accent:var(--amber, #f5ae4e); }
.pb-col.g-no_close { --col-accent:var(--red, #e5484d); }
.pb-col.over { border-color:var(--blue); border-top-color:var(--col-accent); background:color-mix(in srgb, var(--blue) 8%, var(--lift-1)); }
.pb-col.no-drop { opacity:.55; }
.pb-cols.dragging .pb-col.fact:not(.over) { opacity:.55; }
.pb-col.flash { animation:pb-flash 1s ease-out; }
@keyframes pb-flash { 0% { box-shadow:0 0 0 3px var(--blue-line); } 100% { box-shadow:0 0 0 0 transparent; } }

.pb-col-head { display:flex; align-items:center; gap:7px; width:100%; padding:9px 10px 8px; border:0; background:none;
  font:inherit; color:var(--text); text-align:left; cursor:pointer; border-radius:10px 10px 0 0; }
.pb-col-head:hover { background:var(--lift-2); }
.pb-col-n { flex:0 0 auto; display:inline-grid; place-items:center; min-width:20px; height:20px; padding:0 5px; border-radius:6px;
  background:color-mix(in srgb, var(--col-accent) 16%, transparent); color:var(--col-accent); font-size:11px; font-weight:800; }
.pb-col-n i.em { font-style:normal; font-size:12px; }
.pb-col-name { flex:1 1 auto; min-width:0; font-size:12.5px; font-weight:750; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pb-col-counts { display:inline-flex; gap:4px; align-items:center; }
.pb-col-counts b { font-size:11px; font-weight:750; padding:1px 7px; border-radius:999px; background:var(--lift-2); color:var(--muted); font-variant-numeric:tabular-nums; }
.pb-col-counts b.fresh { background:#e5484d; color:#fff; }

.pb-list { flex:1 1 auto; min-height:0; overflow-y:auto; padding:2px 8px 10px; display:flex; flex-direction:column; gap:7px; scrollbar-width:thin; }
.pb-empty { margin:6px 2px; padding:16px 8px; text-align:center; font-size:12px; color:var(--subtle);
  border:1px dashed var(--line); border-radius:10px; }
.pb-more { font:inherit; font-size:12px; font-weight:650; padding:7px; border:1px dashed var(--line); border-radius:9px;
  background:transparent; color:var(--muted); cursor:pointer; }
.pb-more:hover { color:var(--text); border-color:var(--line-strong); }

/* Folded: a slim strip with the name running down it, still a drop target. */
.pb-col.folded { flex:0 0 44px; }
.pb-col.folded .pb-col-head { flex-direction:column; height:100%; padding:10px 0; gap:10px; border-radius:10px; }
.pb-col.folded .pb-col-name { writing-mode:vertical-rl; flex:0 1 auto; color:var(--muted); font-weight:700; }
.pb-col.folded .pb-col-counts { flex-direction:column; }

.pb-card { position:relative; flex:0 0 auto; min-width:0; padding:9px 10px 8px; border:1px solid var(--line); border-radius:10px; background:var(--panel);
  box-shadow:0 1px 1.5px rgba(0,0,0,.06); cursor:pointer; transition:border-color .12s, box-shadow .12s; }
.pb-card:hover { border-color:var(--blue-line); box-shadow:0 2px 8px rgba(0,0,0,.10); }
.pb-card[draggable="true"]:active { cursor:grabbing; }
.pb-card.dragging { opacity:.4; }
.pb-card.due { box-shadow:inset 3px 0 0 var(--amber, #f5ae4e), 0 1px 1.5px rgba(0,0,0,.06); }
.pb-card-top { display:flex; align-items:center; gap:7px; min-width:0; }
.pb-card .ws-avatar { width:24px; height:24px; flex:0 0 24px; font-size:9.5px; }
.pb-name { flex:0 1 auto; min-width:0; font-size:12.5px; font-weight:650; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:left; }
.pb-card.unread .pb-name { font-weight:800; }
.pb-card.waiting .pb-name::before { content:""; display:inline-block; width:6px; height:6px; border-radius:50%; background:var(--amber, #f5ae4e); margin-right:5px; vertical-align:middle; }
.pb-card .ws-new { margin-left:0; margin-right:0; }
.pb-when { margin-left:auto; flex:0 0 auto; font-size:11px; color:var(--subtle); }
.pb-dot { flex:0 0 auto; width:8px; height:8px; border-radius:50%; background:#e5484d; }
.pb-prev { margin-top:5px; font-size:12px; line-height:1.4; color:var(--muted); display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; overflow-wrap:anywhere; }
.pb-meta { display:flex; align-items:center; gap:5px; flex-wrap:wrap; margin-top:6px; }
.pb-meta:empty { display:none; }
.pb-meta .heat-stars.ro .heat-star { font-size:11px; }
.pb-meta .ws-tag { max-width:96px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pb-chan { margin-left:auto; display:inline-flex; align-items:center; gap:4px; color:var(--subtle); }
.pb-star { display:inline-flex; color:var(--star-gold); }

/* THE CARD ON THE BOARD. The inbox's own conversation, notes and details,
   moved in here by prospects.js while the board is up.

   In the MIDDLE of the board, not against an edge. Stefan, 10-14-2026, on a
   screenshot of the first version (the side panel, against the right of the
   screen): "I want the card to pop up in the middle of the screen not
   bordering the right side ... I should be able to click on another card ...
   When I click on it, it stays in the open card view."

   So it is centred on the board with columns showing either side, a shadow
   rather than a shade behind it, and those columns stay live: click a card
   beside it and this one switches to them. The columns scroll sideways under
   it, so any of them can be brought out to one side or the other.

   Its width follows what is open inside it -- the conversation and the dock;
   plus the details when they are unfolded; plus the notes -- but always
   leaves a margin of board on both sides, and never shrinks below what a
   conversation can be read in.

   Later the same day: "a little less wide and taller". So the widths came
   down by about a tenth, and the card is measured against the WHOLE board
   rather than the columns -- it stands up over the search row while it is
   open, which is where the height came from. Close it and the filters are
   all there.

   Then, having lived with it: "can we make the card about 50% smaller? It
   covers a lot of the rest of the board, and ideally I would want it to not
   cover very much of it." At full height it was not a peek, it was a page
   with a board behind it. So both dimensions came down by about three
   tenths, which is roughly half the area it was covering, and the height is
   the bigger half of that -- a shorter card is what puts columns back above
   and below it, and inset:0 with margin:auto keeps it centred between them.

   The floor still holds: it never shrinks below what a conversation can be
   read in. When the notes AND the details are both up the card is under the
   container query below, so the details step down to their dock rather than
   squeezing the thread.

   FOLDING THE DETAILS DOES NOT SHRINK THE CARD. Stefan, 10-14-2026: "I like
   the size of it when we're viewing the details, but I collapse the details
   and the card got substantially smaller. I don't want that to happen." It
   used to follow every pane that was open, which meant putting the details
   away took 300px off the whole card and moved the conversation out from
   under the pointer. So the width answers one question now -- are the notes
   up -- and folding the details simply hands those 300px to the thread
   inside a card that has not moved. */
.ws-board { --peek-w:820px; --peek-keep:150px; position:relative; }
#ws4.noting .ws-board { --peek-w:1000px; }
.pb-peek { position:absolute; z-index:20; inset:0; margin:auto;
  width:max(min(var(--peek-w), calc(100% - 2 * var(--peek-keep))), min(460px, calc(100% - 28px)));
  height:min(620px, calc(100% - 24px));
  display:flex; overflow:hidden;
  border:1px solid var(--line-strong); border-radius:16px; background:var(--panel);
  box-shadow:0 30px 80px rgba(0,0,0,.24), 0 4px 14px rgba(0,0,0,.10);
  animation:pb-peek-pop .2s cubic-bezier(.2,.8,.3,1); container:peek / inline-size; }
.pb-peek[hidden] { display:none; }
@keyframes pb-peek-pop { from { opacity:0; transform:translateY(8px) scale(.975); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion: reduce) { .pb-peek { animation:none; } }
/* Inside it the panes are a row, not grid tracks. */
.pb-peek > .ws-thread { flex:1 1 auto; min-width:0; }
.pb-peek .ws-thread-inner { border:0; border-radius:0; }
.pb-peek > .ws-npanel { flex:0 0 320px; }
.pb-peek > .ws-card { flex:0 0 auto; }
.pb-peek .ws-card-main { flex:0 0 312px; width:312px; }
/* Too narrow for the conversation, the notes AND the details: the details
   step down to their dock while the notes are up, the way the inbox gives
   way -- they are what you are writing about, not what you are writing in. */
@container peek (max-width: 1100px) {
  #ws4.noting .pb-peek .ws-card-main { display:none; }
}
/* And below that, the details fold whether the notes are up or not. Once the
   card came down in size a laptop could leave the thread about 170px wide
   with the details still open beside it -- a column of one word, and a
   composer stacked six lines deep. The details fold to their own icon dock,
   which is how the person card behaves everywhere else; the icons are still
   there to bring them back. */
@container peek (max-width: 700px) {
  .pb-peek .ws-card-main { display:none; }
}
/* The person who is open, marked on the board. */
.pb-card.active { border-color:var(--blue); box-shadow:0 0 0 2px var(--blue-line), 0 2px 8px rgba(0,0,0,.10); }

/* ============================================================== TEAM
   Redone 10-15-2026. Stefan, with the old page on screen: "The access is
   running off the screen to the right ... the buttons to the right are
   missing." A list whose lines never overflow, and a person opening beside it
   in the client card's panel with their access grouped and folded the way
   GoHighLevel's is. */

/* The two views of the page: the people, and what each role starts with. */
.tm-tabs { display:flex; gap:4px; border-bottom:1px solid var(--line); margin:4px 0 2px; }
.tm-tabs [role="tab"], .tm-ptabs [role="tab"] {
  font:inherit; font-size:13px; font-weight:650; color:var(--subtle); background:none; border:0;
  padding:9px 12px 10px; cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-1px;
  display:inline-flex; align-items:center; gap:6px; border-radius:6px 6px 0 0; }
.tm-tabs [role="tab"]:hover, .tm-ptabs [role="tab"]:hover { color:var(--text); }
.tm-tabs [aria-selected="true"], .tm-ptabs [aria-selected="true"] {
  color:var(--text); border-bottom-color:var(--accent-solid, var(--blue)); }
.tm-n { font-size:10.5px; font-weight:750; color:var(--subtle); background:var(--lift-2);
  border-radius:999px; padding:1px 7px; min-width:18px; text-align:center; }
.tm-n:empty { display:none; }

/* ---- the list. One line per person; five columns that share the width and
   a sixth that is only as wide as its buttons, so the actions can never be
   pushed off the edge again. The middle column gives way, not the buttons. */
.tm-list { display:grid; gap:8px; }
.tm-row {
  display:grid; align-items:center; gap:14px;
  /* Fixed widths for everything but the name and the access, because each row
     is its own grid: an `auto` column sizes to that row's pill, and the status
     dots wandered left and right down the list. */
  grid-template-columns: minmax(200px, 1.15fr) 118px minmax(0, 2fr) 150px 178px 14px;
  padding:12px 14px; border:1px solid var(--line); border-radius:12px;
  background:linear-gradient(160deg, var(--panel-2), var(--panel)); cursor:pointer;
  transition:border-color .15s ease, box-shadow .15s ease, background .15s ease; }
.tm-row:hover { border-color:color-mix(in srgb, var(--accent-solid, var(--blue)) 30%, var(--line)); }
.tm-row:focus-visible { outline:2px solid var(--accent-solid, var(--blue)); outline-offset:2px; }
.tm-row.is-open { border-color:color-mix(in srgb, var(--accent-solid, var(--blue)) 55%, var(--line));
  box-shadow:0 0 0 3px color-mix(in srgb, var(--accent-solid, var(--blue)) 12%, transparent); }

.tm-id { display:flex; align-items:center; gap:11px; min-width:0; }
.tm-av { --h:220; flex:none; width:36px; height:36px; border-radius:50%; display:grid; place-items:center;
  font-size:12.5px; font-weight:800; letter-spacing:.02em;
  background:hsl(var(--h) 70% 55% / .16); color:hsl(var(--h) 75% 72%);
  border:1px solid hsl(var(--h) 60% 50% / .25); }
/* This stylesheet is dark-first, so the light theme is the override, through
   the same two doors the palette uses. */
:root[data-theme="light"] .tm-av { color:hsl(var(--h) 60% 40%); }
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .tm-av { color:hsl(var(--h) 60% 40%); } }
.tm-id-txt { min-width:0; display:flex; flex-direction:column; gap:1px; }
.tm-id-txt b { font-size:13.5px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-mail { font-size:11.5px; color:var(--subtle); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-you { font-size:9.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  color:var(--subtle); border:1px solid var(--line); border-radius:999px; padding:0 6px; margin-left:4px;
  vertical-align:1px; }

.tm-role { display:flex; gap:4px; flex-wrap:wrap; }
.tm-rolepill { font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; white-space:nowrap;
  color:var(--ink-violet); background:rgba(139,92,246,.10); border:1px solid rgba(139,92,246,.30); }

/* What they can reach, in two lines that never wrap: the areas by name, and
   how many. The first line ellipses rather than pushing anything. */
.tm-reach { min-width:0; display:flex; flex-direction:column; gap:2px; }
.tm-reach-main { font-size:12.5px; font-weight:600; color:var(--text);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-reach-main.tm-muted { color:var(--subtle); font-weight:500; }
.tm-reach-sub { font-size:11px; color:var(--subtle); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

.tm-status { display:flex; align-items:flex-start; gap:8px; min-width:0; }
.tm-status > div { display:flex; flex-direction:column; gap:1px; min-width:0; }
.tm-status b { font-size:12px; color:var(--text); }
.tm-status span:not(.tm-dot) { font-size:11px; color:var(--subtle); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-dot { flex:none; width:8px; height:8px; border-radius:50%; margin-top:5px; background:var(--subtle); }
.tm-status.ok .tm-dot { background:var(--sw-green, #35c98f); box-shadow:0 0 0 3px rgba(53,201,143,.16); }
.tm-status.warn .tm-dot { background:var(--sw-amber, #f5ae4e); box-shadow:0 0 0 3px rgba(245,174,78,.16); }
.tm-status.off b { color:var(--subtle); }

.tm-acts { display:flex; gap:6px; justify-content:flex-end; white-space:nowrap; }
.tm-chev { color:var(--subtle); font-size:20px; line-height:1; justify-self:end; transition:transform .15s ease; }
.tm-row:hover .tm-chev, .tm-row.is-open .tm-chev { color:var(--text); transform:translateX(2px); }

/* Narrower: the panel is open beside the list, or the window is small. The
   line becomes two -- who and what, then status and the buttons -- rather
   than squeezing five columns into a strip. */
@media (max-width:1180px) {
  .tm-row { grid-template-columns: minmax(0,1fr) auto 14px;
    grid-template-areas: "id role chev" "reach reach chev" "status acts chev"; row-gap:10px; }
  .tm-id { grid-area:id; } .tm-role { grid-area:role; } .tm-reach { grid-area:reach; }
  .tm-status { grid-area:status; } .tm-acts { grid-area:acts; } .tm-chev { grid-area:chev; align-self:center; }
}
@media (min-width:1100px) {
  body.cp-open .tm-row { grid-template-columns: minmax(0,1fr) auto 14px;
    grid-template-areas: "id role chev" "reach reach chev" "status acts chev"; row-gap:10px; }
  body.cp-open .tm-id { grid-area:id; } body.cp-open .tm-role { grid-area:role; }
  body.cp-open .tm-reach { grid-area:reach; } body.cp-open .tm-status { grid-area:status; }
  body.cp-open .tm-acts { grid-area:acts; } body.cp-open .tm-chev { grid-area:chev; align-self:center; }
}
@media (max-width:560px) {
  /* The role sits beside the name rather than on a line of its own -- a card
     per person was five lines tall on a phone and the list was mostly air. */
  .tm-row { grid-template-columns: minmax(0,1fr) auto 14px;
    grid-template-areas: "id role chev" "reach reach chev" "status status chev" "acts acts chev"; }
  .tm-acts { justify-content:flex-start; flex-wrap:wrap; }
}

/* A sign-in link, shown once, at the top of the list. */
.tm-linkcard { margin-bottom:10px; border-color:color-mix(in srgb, var(--accent-solid, var(--blue)) 40%, var(--line)); }
.tm-link-head { display:flex; align-items:center; justify-content:space-between; gap:8px; }
.tm-link-row { display:flex; align-items:center; gap:8px; margin:8px 0 4px; flex-wrap:wrap; }
code.tm-link { flex:1 1 260px; min-width:0; background:var(--lift-2); padding:7px 10px; border-radius:8px;
  font:12px/1.4 ui-monospace, Menlo, monospace; color:var(--text); overflow-wrap:anywhere; }

/* ---- one person, in the client card's panel */
.tm-panel { width:var(--cp-w); }
.tm-head { align-items:center; }
.tm-who { display:flex; align-items:center; gap:12px; min-width:0; }
.tm-who .tm-av { width:44px; height:44px; font-size:15px; }
.tm-who-txt { min-width:0; }
.tm-who-txt h3 { font-size:16px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-who-sub { margin:2px 0 0; font-size:12px; color:var(--subtle); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-ptabs { display:flex; gap:4px; padding:0 14px; border-bottom:1px solid var(--line); flex:none; }
.tm-body { padding:16px 18px 24px; }

.tm-fields { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.tm-field { display:flex; flex-direction:column; gap:5px; min-width:0; }
.tm-field > span, .tm-label { font-size:10.5px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--subtle); margin:0; }
.tm-field input { width:100%; }
.tm-block { margin-top:18px; padding-top:16px; border-top:1px solid var(--line); }
.tm-block .chips { margin-top:8px; }
.tm-toggle-row { display:flex; align-items:center; justify-content:space-between; gap:14px; }
.tm-toggle-row .helper { margin:3px 0 0; }
.tm-adv { margin-top:18px; padding-top:14px; border-top:1px solid var(--line); }
.tm-adv summary { cursor:pointer; font-size:12.5px; font-weight:700; color:var(--subtle); list-style:none;
  display:flex; align-items:center; gap:6px; }
.tm-adv summary::-webkit-details-marker { display:none; }
.tm-adv summary::before { content:"›"; font-size:16px; line-height:1; transition:transform .15s ease; }
.tm-adv[open] summary::before { transform:rotate(90deg); }
.tm-adv summary:hover { color:var(--text); }
@media (max-width:560px) { .tm-fields { grid-template-columns:1fr; } }

.tm-note { margin:0 0 12px; padding:10px 12px; border-radius:10px; font-size:12.5px; line-height:1.5;
  background:color-mix(in srgb, var(--accent-solid, var(--blue)) 9%, transparent);
  border:1px solid color-mix(in srgb, var(--accent-solid, var(--blue)) 28%, var(--line)); color:var(--text); }
.tm-acc-tools { display:flex; flex-direction:column; gap:8px; }
.tm-acc-tools input[type="search"] { width:100%; }
.tm-acc-actions { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.tm-acc-actions select { width:auto; min-width:0; flex:1 1 180px; max-width:260px; }
.tm-acc-sum { margin:12px 0 4px; font-size:12px; font-weight:650; color:var(--subtle); }

/* The footer never scrolls away. */
.tm-foot { flex:none; display:flex; align-items:center; gap:10px; padding:12px 18px;
  border-top:1px solid var(--line); background:var(--panel); }
.tm-state { font-size:12px; font-weight:650; color:var(--subtle); flex:1 1 auto; min-width:0; }
.tm-state.warn { color:var(--ink-warn); }
.tm-state.ok { color:var(--ink-good); }
.tm-err { font-size:12px; color:var(--ink-bad); }
.tm-err:empty { display:none; }
.tm-foot .button[disabled] { opacity:.5; cursor:default; }

/* ---- the access list: sections of folded rows */
.pm-list { margin-top:4px; display:grid; gap:4px; }
.pm-sec { display:grid; gap:6px; margin-top:10px; }
.pm-sec-h { margin:4px 0 0; font-size:10.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--subtle); }
.pm-row { border:1px solid var(--line); border-radius:10px; background:var(--lift-1); }
.pm-row.is-open { border-color:color-mix(in srgb, var(--accent-solid, var(--blue)) 40%, var(--line)); }
.pm-head { display:flex; align-items:center; gap:12px; padding:10px 12px; }
.pm-open { flex:1; display:flex; align-items:center; gap:10px; background:none;
  border:0; padding:0; text-align:left; cursor:pointer; color:inherit; min-width:0; font:inherit; }
.pm-name { flex:1; min-width:0; }
.pm-name b { font-size:13px; color:var(--text); }
.pm-why { display:block; font-size:11px; color:var(--subtle); margin-top:2px; line-height:1.4; }
.pm-count { flex:none; font-size:11px; font-weight:750; color:var(--subtle);
  border:1px solid var(--line); border-radius:999px; padding:2px 8px; font-variant-numeric:tabular-nums; }
.pm-count.on { color:var(--ink-good); border-color:rgba(53,201,143,.35); }
.pm-count.part { color:var(--ink-warn); border-color:rgba(245,174,78,.4); }
.pm-chev { flex:none; color:var(--subtle); transition:transform .16s ease; }
.pm-row.is-open .pm-chev { transform:rotate(180deg); }

/* The switch. Three states, because "some of it" has to be visible on the
   folded row -- a half-granted area that looked like an empty one is how you
   hand somebody export without meaning to. */
.pm-sw { flex:none; width:36px; height:20px; border-radius:999px; cursor:pointer;
  border:1px solid var(--line); background:var(--lift-2); position:relative; padding:0;
  transition:background .16s ease, border-color .16s ease; }
.pm-sw span { position:absolute; top:2px; left:2px; width:14px; height:14px; border-radius:50%;
  background:var(--subtle); transition:transform .16s ease, background .16s ease;
  box-shadow:0 1px 2px rgba(0,0,0,.18); }
.pm-sw.on { background:var(--sw-green, #35c98f); border-color:transparent; }
.pm-sw.on span { transform:translateX(16px); background:#fff; }
.pm-sw.part { background:color-mix(in srgb, var(--sw-amber, #f5ae4e) 60%, transparent);
  border-color:rgba(245,174,78,.5); }
.pm-sw.part span { transform:translateX(8px); background:#fff; }
.pm-sw[disabled] { cursor:not-allowed; opacity:.55; }

/* A single page, given without its area. Same row as an area, no fold. */
.tm-accview { align-self:flex-start; }
.tm-accview .tm-n:empty { display:none; }
.pm-row.pm-page .pm-head { padding:8px 12px; }
.pm-page-path { font-size:10.5px; color:var(--subtle); font-variant-numeric:tabular-nums; margin-left:6px; font-weight:600; }
.pm-pages-note { margin:6px 0 0; }
/* What the Brain reads. A source their access does not open is drawn quieter
   than one switched off by hand: one is a choice, the other is not on offer. */
.pm-row.is-locked { background:transparent; }
.pm-row.is-locked .pm-name b { color:var(--subtle); }
.pm-brain-why { font-weight:600; }

.pm-acts { border-top:1px solid var(--line); padding:6px 12px 10px 60px; display:grid; gap:2px; }
.pm-act { display:flex; gap:10px; align-items:flex-start; padding:7px 0; cursor:pointer; }
.pm-act input { width:auto; margin-top:2px; flex:none; }
.pm-act b { font-size:12.5px; color:var(--text); }
@media (max-width:560px) { .pm-acts { padding-left:16px; } }

/* ---- the roles matrix */
.tm-legend { display:flex; flex-wrap:wrap; gap:6px 14px; margin:12px 0 4px; font-size:11.5px; color:var(--subtle); }
.tm-leg { display:inline-flex; align-items:center; gap:6px; }
.tm-pip { display:inline-grid; place-items:center; width:18px; height:18px; border-radius:5px; margin-right:3px;
  font-size:9.5px; font-weight:800; color:var(--subtle); border:1px dashed var(--line); background:transparent; }
.tm-pip.on { color:#fff; border:1px solid transparent; background:var(--sw-green, #35c98f); }
.tm-matrix-wrap { overflow-x:auto; margin-top:10px; border:1px solid var(--line); border-radius:10px; }
.tm-matrix { width:100%; border-collapse:collapse; font-size:12px; }
.tm-matrix th, .tm-matrix td { padding:8px 10px; border-bottom:1px solid var(--line); text-align:left;
  white-space:nowrap; }
.tm-matrix thead th { font-size:10.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  color:var(--subtle); background:var(--lift-1); }
.tm-matrix .tm-mx-area { position:sticky; left:0; background:var(--panel); z-index:1; min-width:150px; }
.tm-matrix thead .tm-mx-area { background:var(--lift-1); }
.tm-matrix .tm-mx-area b { font-weight:650; color:var(--text); }
/* Row headers are <th> for screen readers, and the table rule upper-cases
   every th -- an area name is a name, not a column heading. */
.tm-matrix tbody th.tm-mx-area { text-transform:none; letter-spacing:normal; font-size:12.5px; }
.tm-matrix tr.tm-mx-sec th { font-size:10.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--subtle); background:var(--lift-1); padding-top:10px; }
.tm-mx-none { color:var(--subtle); opacity:.6; }
.tm-matrix tbody tr:not(.tm-mx-sec):hover td, .tm-matrix tbody tr:not(.tm-mx-sec):hover th { background:var(--lift-1); }

/* Their Instagram under their name on the prospect card: the glyph says what
   it opens before anybody reads the handle (prospects.js, 10-15-2026). */
.ws-ig-handle { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; white-space: nowrap; }
.ws-ig-handle svg { flex: none; }
.ws-sub-line { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.ws-sub-line .ws-ig-handle { font-size: 12px; align-self: center; }
.ws-sub-line #wsSub { min-width: 0; }

/* Phone calls in the conversation thread (prospects.js, callRow). A row, not
   a bubble: nobody typed it. Bordered on the panel colour so it reads as the
   same family as the Calls rail's card, and sits on the side its message
   would -- ours right, theirs left. Missed and voicemail carry the warning
   ink because those are the ones that want a call back. */
.msg-turn.th-call-turn { max-width:min(380px, 78%); }
.th-call { display:flex; flex-direction:column; gap:7px; width:100%; min-width:240px;
  padding:9px 12px; border:1px solid var(--line); border-radius:12px; background:var(--panel-2); }
.th-call.o-missed, .th-call.o-voicemail { border-color:color-mix(in srgb, var(--amber) 45%, var(--line)); }
.th-call.o-live { border-color:color-mix(in srgb, var(--blue) 55%, var(--line)); }
.th-call-top { display:flex; align-items:center; gap:7px; font-size:12.5px; flex-wrap:wrap; }
.th-call-top .ch-ico { color:var(--blue); }
.th-call.o-missed .th-call-top .ch-ico, .th-call.o-voicemail .th-call-top .ch-ico { color:var(--ink-warn); }
.th-call-top b { font-weight:650; }
.th-call-len { color:var(--muted); font-variant-numeric:tabular-nums; }
.th-call-tag { font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.05em;
  color:var(--muted); }
.th-call-tag.o-missed, .th-call-tag.o-voicemail, .th-call-tag.o-failed { color:var(--ink-warn); }
.th-call-tag.o-live { color:var(--blue); }
.th-call-when { margin-left:auto; color:var(--muted); font-size:11px; }
.th-call audio { width:100%; height:32px; }
.th-call-more > summary { cursor:pointer; font-size:12px; font-weight:600; line-height:1.45;
  color:var(--text); padding:1px 0; }
.th-call-more > summary:hover { color:var(--blue); }
.th-call-more[open] > summary { margin-bottom:6px; }
.th-call-more .cb-script { margin-top:6px; }
/* The list's last line, when the last thing was a call. */
.ws-prev-call { display:inline-flex; align-items:center; gap:5px; }
.ws-prev-call .ch-ico { color:var(--blue); }
.ws-prev-call.bad { color:var(--ink-warn); }
.ws-prev-call.bad .ch-ico { color:var(--ink-warn); }
/* A Zoom call Fathom recorded, in the same thread (prospects.js, meetRow). */
.th-meet .th-call-top .ch-ico { color:var(--violet, #6d45c9); }
.th-meet-sub { font-size:11.5px; color:var(--muted); margin-top:-3px; }
.th-meet-sum { font-size:12px; line-height:1.5; max-height:260px; overflow-y:auto; scrollbar-width:thin; }
.th-meet-links { display:flex; gap:12px; flex-wrap:wrap; }
.th-meet-link { font-size:12px; font-weight:650; color:var(--blue); text-decoration:none; }
.th-meet-link:hover { text-decoration:underline; }
.th-call-tag.o-closed { color:var(--ink-good); }
.th-call-tag.o-no_close, .th-call-tag.o-no_show { color:var(--ink-warn); }
.th-call-tag.o-unlogged { color:var(--ink-warn); }
.th-meet[data-meet^="appt-"] .th-call-top .ch-ico { color:var(--muted); }

/* ---------- Pricing: the programmes, side by side ----------
   The deliverables grid further down the page, drawn to be looked at rather
   than filled in. Stefan, 10-16-2026: "I want it to look nice because if I'm
   on a Zoom call and somebody is genuinely curious, I could always present it
   to them ... It should have kind of the vibe of a landing page."

   IT WEARS THE PURSUIT BRAND, NOT OFFEROPS'. Stefan, same day: "I want it to
   be the same brand kit as we use for the PHP Hub/client hub, since I might
   be presenting it." So the chart carries the hub's own kit -- near-black
   ground, charcoal panels, off-white text, ONE red, heavy condensed uppercase
   headlines, small tracked labels with a short red rule, sharp corners and
   hairlines -- as its own scoped tokens, copied from clienthub/hub.css rather
   than reached for across stylesheets (that file is standalone on purpose).
   It stays the brand in either OfferOps theme, the same way the landing-page
   previews above it show the real dark site inside this page: what a client
   would see does not change because an admin prefers light.

   The folding and the triangle are borrowed off .mr-fold rather than drawn
   again. Present (the button in the summary) lays the chart over the window. */
.pr-cmp-card {
  --pc-bg: #07090a;
  --pc-panel: #101517;
  --pc-panel-2: #151b1e;
  --pc-line: rgba(255,255,255,.08);
  --pc-line-strong: rgba(255,255,255,.18);
  --pc-text: #f4f3ee;
  --pc-muted: #aab7b4;
  --pc-subtle: #7d8a87;
  --pc-red: #e31b23;
  --pc-red-ink: #ff3d44;
  --pc-good: #3ecf8e;
  --pc-head: "Barlow Condensed", Impact, "Arial Narrow", sans-serif;
  --pc-body: "DM Sans", ui-sans-serif, system-ui, -apple-system, sans-serif;
}
.pr-cmp-card > summary { align-items: flex-start; }
.pr-cmp-card > summary::before { margin-top: 6px; }
.pr-cmp-card > summary .card-sub { margin: 3px 0 0; max-width: 62ch; }
.pr-cmp-acts { display: inline-flex; align-items: center; gap: 10px;
  margin-left: auto; flex: 0 0 auto; }
.pr-cmp-body { margin-top: 14px; }

/* Sharp corners and a hairline: the brand has no rounded cards. */
.pr-cmp { border: 1px solid var(--pc-line); background: var(--pc-bg);
  font-family: var(--pc-body); color: var(--pc-text); overflow: hidden; }
/* One template, used by the head and every row, so the columns line up
   without a table -- a table cannot give a column its own heading block. */
.pr-cmp-head, .pr-cmp-row { display: grid;
  grid-template-columns: minmax(118px, 1.15fr) repeat(var(--pr-cmp-cols, 5), minmax(0, 1fr)); }
.pr-cmp-head { background: var(--pc-panel); border-bottom: 1px solid var(--pc-line-strong); }
.pr-cmp-col { position: relative; padding: 18px 12px 16px; text-align: center;
  border-left: 1px solid var(--pc-line); }
/* The top of the ladder carries the red and nothing else does: it is the one
   being compared against. The brand has a single red and spends it here. */
.pr-cmp-col.first::before { content: ""; position: absolute; inset: 0 0 auto 0;
  height: 3px; background: var(--pc-red); }
/* The hub's label: small, tracked, red, over a short red rule. */
.pr-cmp-eyebrow { display: flex; flex-direction: column; align-items: center; gap: 7px;
  margin: 0 0 9px; font-size: 10px; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--pc-red-ink); }
.pr-cmp-eyebrow::before { content: ""; width: 22px; height: 2px; background: var(--pc-red); }
.pr-cmp-name { display: flex; align-items: baseline; justify-content: center; gap: 8px;
  margin: 0; font-family: var(--pc-head); font-weight: 800; font-size: 27px;
  line-height: .96; letter-spacing: .01em; text-transform: uppercase; color: var(--pc-text); }
/* The tier number is outlined rather than red: the red is Elite's. */
.pr-cmp-tier { padding: 2px 6px; border: 1px solid var(--pc-line-strong);
  font-family: var(--pc-body); font-size: 9.5px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--pc-muted); white-space: nowrap; }
.pr-cmp-tag { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--pc-muted); }
.pr-cmp-row { border-top: 1px solid var(--pc-line); }
.pr-cmp-row:nth-child(even) { background: var(--pc-panel-2); }
.pr-cmp-rname { display: flex; align-items: center; padding: 13px 14px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  line-height: 1.4; color: var(--pc-muted); }
.pr-cmp-cell { display: flex; gap: 7px; align-items: baseline; justify-content: center;
  padding: 13px 12px; border-left: 1px solid var(--pc-line); text-align: center;
  font-size: 13px; line-height: 1.45; color: var(--pc-text); }
.pr-cmp-cell.off { color: var(--pc-subtle); }
.pr-cmp-tick { flex: 0 0 auto; color: var(--pc-good); font-weight: 700; }

/* Presented: the chart over the whole window, centred, and sized to be read
   through a video call. A fixed overlay rather than the Fullscreen API, which
   is refused inside an embedded view and fails by doing nothing. */
.pr-cmp-card.presenting { position: fixed; inset: 0; z-index: 200; margin: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 30px clamp(20px, 4vw, 56px); overflow: auto; border: 0; border-radius: 0;
  background: var(--pc-bg); }
body.pr-presenting { overflow: hidden; }
.pr-cmp-card.presenting > summary::before { display: none; }
/* Presenting is the one time somebody outside the business reads this card,
   so the line underneath goes: "deliverables only, no prices" is a note to
   ourselves about why it is safe to share, and reading it is the client
   noticing there are prices somewhere. The heading becomes the brand's own
   headline -- condensed, uppercase -- rather than the page's small capitals. */
.pr-cmp-card.presenting > summary .card-sub { display: none; }
.pr-cmp-card.presenting > summary h2 { font-family: var(--pc-head); font-weight: 800;
  font-size: clamp(22px, 2.2vw, 34px); line-height: .96; letter-spacing: .01em;
  text-transform: uppercase; color: var(--pc-text); }
.pr-cmp-card.presenting .pr-cmp-body { margin-top: 20px; }
.pr-cmp-card.presenting .pr-cmp-name { font-size: clamp(22px, 2.1vw, 34px); }
.pr-cmp-card.presenting .pr-cmp-eyebrow { font-size: 11px; }
.pr-cmp-card.presenting .pr-cmp-tag { font-size: clamp(12px, .95vw, 14.5px); }
.pr-cmp-card.presenting .pr-cmp-col { padding: 24px 16px 22px; }
.pr-cmp-card.presenting .pr-cmp-rname { font-size: clamp(11px, .85vw, 13px); padding: 17px 16px; }
.pr-cmp-card.presenting .pr-cmp-cell { font-size: clamp(13px, 1.05vw, 16.5px); padding: 17px 16px; }

/* Narrow: the columns stop shrinking and the chart scrolls instead, because
   five columns squeezed into a phone is five columns nobody can read. */
@media (max-width: 900px) {
  .pr-cmp-body { overflow-x: auto; scrollbar-width: thin; }
  .pr-cmp { min-width: 680px; }
}

/* The tagline row in the grid's own editor. */
.pd-lab { font-size: 10.5px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--subtle); }
.pd-lab small { font-weight: 600; letter-spacing: 0; text-transform: none; }

/* ===================================================== POP-UP CHATS: THE FRAME
   With a chat open the window is a frame (templates/_shell.html, chat_dock.js):
   the page fills it as an iframe and the chat boxes dock along the bottom,
   outside the page, so moving around the app never reloads them. */
html.oo-frame, html.oo-frame body.oo-frame-body { margin:0; padding:0; height:100%; overflow:hidden; background:var(--bg); }
.oo-main { display:block; position:fixed; inset:0; width:100%; height:100%; border:0; background:var(--bg); }
.oo-dock { position:fixed; right:16px; bottom:0; z-index:9100; display:flex; flex-direction:row-reverse; align-items:flex-end; gap:12px; pointer-events:none; }
.oo-dock[hidden] { display:none; }
.oo-dock-row { display:flex; flex-direction:row-reverse; align-items:flex-end; gap:10px; }
.oo-chat { pointer-events:auto; width:340px; display:flex; flex-direction:column; background:var(--panel); border:1px solid var(--line-strong);
  border-bottom:0; border-radius:12px 12px 0 0; box-shadow:var(--shadow-lg); overflow:hidden; }
.oo-chat[hidden] { display:none; }
.oo-chat-head { display:flex; align-items:center; gap:2px; height:44px; padding:0 6px 0 4px; border-bottom:1px solid var(--line); background:var(--panel-2); flex:0 0 auto; }
.oo-chat-title { display:flex; align-items:center; gap:8px; flex:1 1 auto; min-width:0; height:36px; padding:0 6px; border:0; border-radius:8px;
  background:none; color:var(--text); font:inherit; font-size:13px; font-weight:700; cursor:pointer; text-align:left; }
.oo-chat-title:hover { background:var(--lift-1); }
.oo-chat-av { flex:0 0 auto; width:26px; height:26px; border-radius:50%; display:grid; place-items:center; color:#fff; font-size:10.5px; font-weight:800; letter-spacing:.02em; }
.oo-chat-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.oo-chat-btn { flex:0 0 auto; width:28px; height:28px; display:grid; place-items:center; border:0; border-radius:7px; background:none; color:var(--muted); cursor:pointer; }
.oo-chat-btn:hover { background:var(--lift-1); color:var(--text); }
.oo-chat-body { height:min(560px, calc(100vh - 96px)); }
.oo-chat-frame { display:block; width:100%; height:100%; border:0; background:var(--bg); }
/* Folded chats: round bubbles up the right edge, the way Messenger keeps
   them -- initials in the person's colour, a red dot when they have
   written, a small x on hover, and the last line beside it (.oo-peek). */
.oo-dock-heads { pointer-events:auto; display:flex; flex-direction:column; align-items:center; gap:10px; margin-bottom:18px; }
.oo-dock-heads[hidden] { display:none; }
.oo-head { position:relative; }
.oo-head-av { width:48px; height:48px; border-radius:50%; border:0; display:grid; place-items:center; color:#fff; font:inherit; font-size:15px; font-weight:800;
  letter-spacing:.02em; cursor:pointer; box-shadow:var(--shadow); transition:transform .12s ease; }
.oo-head-av:hover { transform:scale(1.06); }
.oo-head-more .oo-head-av { background:var(--panel); color:var(--text); border:1px solid var(--line-strong); font-size:13px; }
.oo-head-dot { display:none; position:absolute; top:0; right:0; width:13px; height:13px; border-radius:50%; background:var(--red); box-shadow:0 0 0 2px var(--bg); }
.oo-head.new .oo-head-dot { display:block; }
.oo-head-x { position:absolute; top:-4px; left:-4px; width:20px; height:20px; border-radius:50%; border:1px solid var(--line-strong); background:var(--panel);
  color:var(--muted); display:grid; place-items:center; cursor:pointer; opacity:0; transition:opacity .12s ease; box-shadow:var(--shadow-sm); }
.oo-head:hover .oo-head-x, .oo-head-x:focus-visible { opacity:1; }
.oo-head-x:hover { color:var(--text); }
.oo-peek { position:fixed; z-index:9200; width:max-content; max-width:260px; padding:10px 12px; background:var(--panel); border:1px solid var(--line-strong);
  border-radius:12px; box-shadow:var(--shadow-lg); pointer-events:none; font-size:13px; color:var(--text); }
.oo-peek[hidden] { display:none; }
.oo-peek b { font-weight:700; }
.oo-peek-when { margin-left:8px; color:var(--subtle); font-size:11px; }
.oo-peek p { margin:4px 0 0; color:var(--muted); line-height:1.4; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
.oo-peek p.new { color:var(--text); font-weight:600; }
.oo-peek p span { color:var(--subtle); font-weight:400; }
.oo-dock-menu { position:absolute; right:56px; top:0; min-width:220px; padding:6px; background:var(--panel); border:1px solid var(--line-strong);
  border-radius:12px; box-shadow:var(--shadow-lg); display:flex; flex-direction:column; gap:2px; }
.oo-dock-menu[hidden] { display:none; }
.oo-dock-menu button { display:flex; align-items:center; gap:8px; padding:6px 8px; border:0; border-radius:8px; background:none; color:var(--text);
  font:inherit; font-size:13px; text-align:left; cursor:pointer; }
.oo-dock-menu button:hover { background:var(--lift-1); }
.oo-dock-menu button.new { font-weight:800; }
@media (max-width: 700px) { .oo-dock { display:none; } }

/* A pop-up chat box's inside: the Inbox's own conversation (?chat=1), alone --
   no rail, list or person card, and no header, since the box's own title bar
   names them and "Open in the Inbox" is a click away. Never a second, smaller
   copy of the composer: this IS the composer. */
body.ws-chat { overflow:hidden; }
body.ws-chat .ws4 { display:flex !important; width:100%; height:100vh; height:100dvh; }
body.ws-chat .ws4 > :not(.ws-thread) { display:none !important; }
body.ws-chat .ws-thread { flex:1 1 auto; width:100%; min-width:0; height:100%; }
body.ws-chat .ws-thread-inner { height:100%; }
body.ws-chat .ws-thread-head { display:none !important; }
/* The team's notes need the room of the Inbox; in a box they are one click
   away through "Open in the Inbox", not a panel squeezed in beside 340px. */
body.ws-chat #wsNotesBtn { display:none !important; }
/* A box arrives the way Messenger's does: up from the edge in a blink. Every
   time it opens, from Pop out or from its bubble. */
@keyframes oo-chat-in { from { transform:translateY(14px); opacity:0; } to { transform:none; opacity:1; } }
.oo-chat:not([hidden]) { animation:oo-chat-in .16s ease-out; }
@media (prefers-reduced-motion: reduce) { .oo-chat:not([hidden]) { animation:none; } }


/* ---- trial copy (app/shadow.py) ----------------------------------------- */
.trialpill { position: fixed; left: 50%; bottom: 14px; transform: translateX(-50%); z-index: 70;
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px 6px 11px;
  font-size: 12px; font-weight: 650; line-height: 1.3; white-space: nowrap; color: #fff;
  background: rgba(13,18,26,.88); border: 1px solid rgba(255,255,255,.14); border-radius: 999px;
  box-shadow: 0 6px 18px rgba(0,0,0,.22); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.trialpill-dot { width: 8px; height: 8px; border-radius: 50%; background: #f5a524;
  box-shadow: 0 0 0 3px rgba(245,165,36,.25); }
@media (max-width:680px) { .trialpill { bottom: calc(env(safe-area-inset-bottom, 0px) + 72px); } }
/* Its tooltip opens upwards and wraps: the pill sits at the foot of the window. */
.trialpill[data-tip]::after { top:auto; bottom:calc(100% + 8px); width:300px; white-space:normal;
  line-height:1.45; transform:translateX(-50%) translateY(3px); }
.trialpill[data-tip]:hover::after { opacity:1; transform:translateX(-50%) translateY(0); }
.trialpill[data-tip]::before { display:none; }
