/* =============================================================================
   Erratumless Sales Portal

   Dark by default, and that is a decision rather than a fashion: the shifts are
   19:00, 20:00 and 21:00 Karachi time, so this interface is looked at through
   the night. Light is available and complete, for daytime managers.

   Every colour below is either sampled from the supplied artwork (see
   assets/brand/README.md) or derived from it. Nothing is invented.
   ========================================================================== */

:root {
  /* ---- brand, read out of the logo files ------------------------------- */
  --brand-cyan:     #29ADE6;
  --brand-grey:     #ADADAD;
  --brand-graphite: #424242;

  /* ---- type ------------------------------------------------------------- */
  --display: "Chakra Petch", "Segoe UI", system-ui, sans-serif;
  --ui:      "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, Consolas, monospace;

  --fs-xs:   11.5px;
  --fs-sm:   13px;
  --fs-base: 14.5px;
  --fs-lg:   17px;

  /* ---- shape ------------------------------------------------------------ */
  --r-sm: 6px;
  --r:    9px;
  --r-lg: 14px;
  --rail: 232px;
  --dock: 380px;
  --topbar: 56px;

  --ease: cubic-bezier(.22, .61, .36, 1);

  /* ---- dark, the default ------------------------------------------------ */
  --ground:      #090F13;
  --surface:     #0F171D;
  --surface-2:   #16212A;
  --surface-3:   #1D2B35;
  --line:        #1E2C36;
  --line-strong: #2B3D4A;

  --ink:   #E8F0F5;
  --ink-2: #A3B4C0;
  --ink-3: #748794;

  --accent:      var(--brand-cyan);
  --accent-text: #5BC4F0;
  --accent-wash: #0A2A39;
  --accent-line: #17506B;

  --good:   #3FD68B;
  --good-wash: #0B2A1E;
  --warn:   #F2B659;
  --warn-wash: #2C2210;
  --danger: #F7837A;
  --danger-wash: #2E1614;

  --focus: #5BC4F0;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-2: 0 12px 34px rgba(0, 0, 0, .55);
}

:root[data-theme="light"] {
  --ground:      #F1F5F8;
  --surface:     #FFFFFF;
  --surface-2:   #E9F0F4;
  --surface-3:   #DFE9EF;
  --line:        #DBE4EA;
  --line-strong: #BFCED8;

  --ink:   #0C161C;
  --ink-2: #46565F;
  --ink-3: #6B7F8C;

  /* The brand cyan is a fill colour, not a text colour: #29ADE6 on white is
     about 2.3:1, which fails for anything anyone has to read. Marks and fills
     keep the brand value; text and links use the darker step. */
  --accent:      var(--brand-cyan);
  --accent-text: #0A7099;
  --accent-wash: #E1F2FB;
  --accent-line: #A8DCF4;

  --good:   #10774B;
  --good-wash: #E1F5EC;
  --warn:   #8A5200;
  --warn-wash: #FBEFD9;
  --danger: #A8291D;
  --danger-wash: #FAE9E6;

  --focus: #0A7099;
  --shadow-1: 0 1px 2px rgba(16, 32, 42, .08);
  --shadow-2: 0 12px 30px rgba(16, 32, 42, .14);
}

/* ---- base ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

/* The browser hides [hidden] with display:none, but any class that sets
   display — .btn is inline-flex — wins on specificity and the element stays
   visible. Toggling `hidden` has to actually hide things. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 var(--fs-base)/1.55 var(--ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { font-family: var(--display); margin: 0; text-wrap: balance; letter-spacing: -.005em; }
h1 { font-size: 25px; font-weight: 600; line-height: 1.2; }
h2 { font-size: var(--fs-lg); font-weight: 600; }
h3 { font-size: var(--fs-base); font-weight: 600; }
p  { margin: 0; }

a { color: var(--accent-text); text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); text-underline-offset: 2px; }
a:hover { text-decoration-color: currentColor; }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }

img { max-width: 100%; height: auto; }

/* Every icon carries .ic, so one that lands somewhere without a size rule is
   16px rather than 246px. Context rules below (.btn, .empty, .nav-link) are
   more specific and still win. */
.ic { width: 16px; height: 16px; flex: none; }

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.num  { font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-3); }
.eyebrow {
  font: 500 var(--fs-xs)/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.ml-auto { margin-left: auto; }
.mt { margin-top: 16px; }
.stack { display: grid; gap: 12px; }
.row { display: flex; align-items: center; gap: 10px; }
.row-wrap { flex-wrap: wrap; }

.skip {
  position: fixed; left: 12px; top: -60px; z-index: 200;
  background: var(--accent); color: #041018; padding: 8px 14px;
  border-radius: 999px; font-weight: 600; text-decoration: none;
}
.skip:focus { top: 12px; }

/* ---- boot ---------------------------------------------------------------- */

.boot {
  min-height: 100dvh; display: grid; place-content: center; justify-items: center;
  gap: 22px; padding: 24px;
}
.boot-mark { width: 240px; opacity: .9; }
:root[data-theme="light"] .boot-mark { content: url("../brand/logo-light.png"); width: 200px; }
.boot-bar {
  width: 120px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent) var(--line);
  background-size: 50% 100%; background-repeat: no-repeat;
  animation: boot 1.1s var(--ease) infinite;
}
@keyframes boot { 0% { background-position: -60% 0; } 100% { background-position: 160% 0; } }

/* ---- sign in ------------------------------------------------------------- */

.auth {
  min-height: 100dvh; display: grid; place-items: center;
  padding: 24px 20px 48px;
  background:
    radial-gradient(900px 420px at 50% -10%, var(--accent-wash), transparent 70%),
    var(--ground);
}
.auth-card {
  width: min(420px, 100%); display: grid; gap: 20px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 30px 28px;
  box-shadow: var(--shadow-2);
}
.auth-logo { display: grid; justify-items: center; gap: 4px; }
.auth-logo img { width: 250px; }
.auth h1 { font-size: 21px; text-align: center; }
.auth-sub { text-align: center; color: var(--ink-3); font-size: var(--fs-sm); }
.auth-foot { text-align: center; font-size: var(--fs-sm); color: var(--ink-3); }

/* The mark animates once on the sign-in screen and then rests. Anyone who has
   asked their system for less motion gets the still version instead. */
/* On a phone the top bar has more in it than the screen is wide: a title, a
   search box with a 200px floor, and four controls. Left alone it simply runs
   off the right edge — the page scrolls sideways and the account button is
   unreachable. The search collapses to its icon (the shortcut hint goes with
   it, since there is no Ctrl key here anyway) and the title truncates; every
   control stays on screen and tappable. */
@media (max-width: 560px) {
  .topbar { gap: 8px; padding: 0 12px; }
  .topbar h1 {
    min-width: 0; flex: 0 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .search { min-width: 0; width: 34px; flex: none; padding: 0; justify-content: center; }
  .search span, .search kbd { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-logo img { content: url("../brand/logo-dark.png"); }
  :root[data-theme="light"] .auth-logo img { content: url("../brand/logo-light.png"); }
}

/* ---- controls ------------------------------------------------------------ */

.field { display: grid; gap: 6px; }
.field > span { font-size: var(--fs-sm); font-weight: 500; color: var(--ink-2); }
.hint { font-size: var(--fs-xs); color: var(--ink-3); }
.err  { font-size: var(--fs-xs); color: var(--danger); }

.input, .select, .textarea {
  width: 100%; height: 40px; padding: 0 12px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  font: 400 var(--fs-base)/1.4 var(--ui);
  transition: border-color .12s var(--ease), background .12s var(--ease);
}
.textarea { height: auto; padding: 10px 12px; resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-3); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--accent); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.input[aria-invalid="true"] { border-color: var(--danger); }
.input.mono { font-family: var(--mono); letter-spacing: .06em; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 15px; border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line-strong);
  font: 500 var(--fs-sm)/1 var(--ui); cursor: pointer; white-space: nowrap;
  transition: background .12s var(--ease), border-color .12s var(--ease), transform .06s var(--ease);
}
.btn:hover { background: var(--surface-3); border-color: var(--ink-3); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }
.btn-primary {
  background: var(--accent); border-color: var(--accent); color: #04161F; font-weight: 600;
}
.btn-primary:hover { background: color-mix(in srgb, var(--accent) 85%, #fff); border-color: transparent; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-ghost:hover { background: var(--surface-2); border-color: transparent; }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.btn-block { width: 100%; height: 42px; }
.btn-sm { height: 30px; padding: 0 10px; font-size: var(--fs-xs); }
.btn svg { width: 15px; height: 15px; }

.badge {
  display: inline-flex; align-items: center; gap: 6px; height: 21px; padding: 0 8px;
  border-radius: 999px; background: var(--surface-2); color: var(--ink-2);
  border: 1px solid var(--line); font: 500 var(--fs-xs)/1 var(--ui); white-space: nowrap;
}
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .75; }
.badge.plain::before { display: none; }
.b-accent { background: var(--accent-wash); color: var(--accent-text); border-color: var(--accent-line); }
.b-good   { background: var(--good-wash);   color: var(--good);   border-color: transparent; }
.b-warn   { background: var(--warn-wash);   color: var(--warn);   border-color: transparent; }
.b-danger { background: var(--danger-wash); color: var(--danger); border-color: transparent; }

/* ---- shell --------------------------------------------------------------- */

/* Rows are declared, not left implicit. `grid-row: 1 / -1` counts to the last
   line of the EXPLICIT grid, so without this the rail spans a single row, the
   main area is auto-placed into column one underneath it, and the whole screen
   renders where nobody can see it. */
.shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  grid-template-rows: var(--topbar) minmax(0, 1fr);
  min-height: 100dvh;
}
.shell.has-dock { grid-template-columns: var(--rail) minmax(0, 1fr) var(--dock); }

.rail  { grid-column: 1; grid-row: 1 / -1; }
.topbar { grid-column: 2; grid-row: 1; }
main   { grid-column: 2; grid-row: 2; }
.dock  { grid-column: 3; grid-row: 1 / -1; }

.rail {
  background: var(--surface); border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100dvh; overflow-y: auto; overscroll-behavior: contain;
}
.rail-head { padding: 14px 16px 12px; border-bottom: 1px solid var(--line); }
.rail-head img { width: 152px; display: block; }
.rail-nav { padding: 10px 10px 20px; display: grid; gap: 2px; align-content: start; }
.rail-group {
  padding: 16px 8px 6px; font: 500 10.5px/1 var(--mono);
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3);
}
.nav-link {
  display: flex; align-items: center; gap: 11px; height: 35px; padding: 0 10px;
  border-radius: var(--r-sm); color: var(--ink-2); text-decoration: none;
  font-size: var(--fs-sm); position: relative;
  transition: background .1s var(--ease), color .1s var(--ease);
}
.nav-link svg { width: 16px; height: 16px; flex: none; opacity: .8; }
.nav-link:hover { background: var(--surface-2); color: var(--ink); }
.nav-link[aria-current="page"] { background: var(--accent-wash); color: var(--accent-text); font-weight: 500; }
.nav-link[aria-current="page"] svg { opacity: 1; }
.nav-link[aria-current="page"]::before {
  content: ""; position: absolute; left: -10px; top: 8px; bottom: 8px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--accent);
}
.nav-count {
  margin-left: auto; font: 500 var(--fs-xs)/1 var(--mono);
  background: var(--surface-3); color: var(--ink-2); padding: 3px 6px; border-radius: 5px;
}
.nav-link[aria-current="page"] .nav-count { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent-text); }

/* ---- top bar ------------------------------------------------------------- */

.topbar {
  height: var(--topbar); position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 12px; padding: 0 18px;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.topbar h1 { font-size: var(--fs-lg); font-weight: 600; }

.search {
  display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px;
  min-width: 200px; max-width: 340px; flex: 1;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--ink-3); cursor: pointer; font-size: var(--fs-sm);
}
.search svg { width: 15px; height: 15px; flex: none; }
.search span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search:hover { border-color: var(--line-strong); }
.search kbd {
  margin-left: auto; font: 500 10.5px/1 var(--mono); color: var(--ink-3);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 4px; padding: 3px 5px;
}

/* Two clocks, because the team works Karachi hours for American prospects and
   getting that wrong wastes a dial. The prospect's zone leads; ours follows. */
.clocks {
  display: flex; align-items: center; gap: 0; font: 500 var(--fs-xs)/1 var(--mono);
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
}
.clock { display: flex; align-items: baseline; gap: 5px; padding: 6px 9px; background: var(--surface); }
.clock + .clock { border-left: 1px solid var(--line); }
.clock b { font-weight: 500; color: var(--ink); }
.clock span { color: var(--ink-3); font-size: 10px; letter-spacing: .08em; }
.clock.is-home b { color: var(--accent-text); }

.icon-btn {
  display: grid; place-items: center; width: 34px; height: 34px; flex: none;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--ink-2); cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn svg { width: 17px; height: 17px; }
.icon-btn .dot {
  position: absolute; margin: -14px 0 0 14px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--accent); border: 2px solid var(--ground);
}

.avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--accent-wash); color: var(--accent-text);
  display: grid; place-items: center; font: 600 11.5px/1 var(--ui);
  border: 1px solid var(--accent-line); cursor: pointer;
}

/* ---- main ---------------------------------------------------------------- */

main { min-width: 0; display: flex; flex-direction: column; }
.page { padding: 22px 22px 56px; flex: 1; min-width: 0; }
.page-head { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.page-head p { color: var(--ink-3); font-size: var(--fs-sm); margin-top: 3px; }
.page-head .actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.grid { display: grid; gap: 12px; }
.cols-2 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.cols-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.cols-4 { grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); }
/* Four tiles go two-by-two or four-across and never three-plus-an-orphan:
   auto-fit would drop one tile alone on a second row at middling widths. */
.cols-2-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 1040px) { .cols-2-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); min-width: 0;
}
.card-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 13px 16px; border-bottom: 1px solid var(--line);
}
.card-head .sub { font-size: var(--fs-sm); color: var(--ink-3); }
.card-body { padding: 16px; }

.stat { padding: 15px 16px; display: grid; gap: 5px; align-content: start; }
.stat-label { font-size: var(--fs-sm); color: var(--ink-3); }
.stat-value { font: 500 26px/1.1 var(--mono); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat-note { font-size: var(--fs-xs); color: var(--ink-3); }
.stat.is-alert .stat-value { color: var(--danger); }
.stat.is-accent .stat-value { color: var(--accent-text); }
.unknown { font: 400 var(--fs-sm)/1.3 var(--ui); color: var(--ink-3); letter-spacing: 0; }

.empty {
  display: grid; justify-items: center; text-align: center; gap: 7px;
  padding: 44px 20px; color: var(--ink-3);
}
.empty svg { width: 26px; height: 26px; color: var(--line-strong); margin-bottom: 2px; }
.empty h3 { color: var(--ink-2); }
.empty p { font-size: var(--fs-sm); max-width: 38ch; }

.list { list-style: none; margin: 0; padding: 0; }
.list li {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; border-bottom: 1px solid var(--line);
}
.list li:last-child { border-bottom: 0; }
.cell-main { font-weight: 500; color: var(--ink); text-decoration: none; }
.cell-sub { font-size: var(--fs-xs); color: var(--ink-3); }

.table-wrap { overflow-x: auto; }
table.t { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-base); }
.t th {
  text-align: left; font: 500 10.5px/1 var(--mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); padding: 11px 14px;
  border-bottom: 1px solid var(--line); white-space: nowrap; background: var(--surface);
}
.t td { padding: 11px 14px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.t tbody tr:last-child td { border-bottom: 0; }
.t tbody tr:hover { background: var(--surface-2); }

/* ---- list screens -------------------------------------------------------- */

.list-head { display: grid; gap: 10px; }
.filter-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.search-field { position: relative; display: flex; align-items: center; min-width: 220px; flex: 1 1 220px; max-width: 360px; }
.search-field .ic { position: absolute; left: 11px; color: var(--ink-3); pointer-events: none; }
.search-field .input { padding-left: 34px; height: 36px; }
.filter-row .select { height: 36px; width: auto; min-width: 0; padding-right: 30px; }

/* Status chips carry their own counts, and the counts come from the server —
   so the filter is correct before and after the pipeline stages change. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 11px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-2); font: 500 var(--fs-sm)/1 var(--ui); cursor: pointer; white-space: nowrap;
}
.chip:hover { border-color: var(--line-strong); color: var(--ink); }
.chip b { font: 500 var(--fs-xs)/1 var(--mono); color: var(--ink-3); }
.chip[aria-pressed="true"] {
  background: var(--accent-wash); border-color: var(--accent-line); color: var(--accent-text);
}
.chip[aria-pressed="true"] b { color: var(--accent-text); }

/* Segmented control — one choice from a short, stable set. */
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.seg-btn {
  appearance: none; border: 0; background: var(--surface); color: var(--ink-2);
  font: 500 var(--fs-sm)/1 var(--ui); padding: 0 13px; height: 34px; cursor: pointer;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--line); }
.seg-btn:hover { background: var(--surface-2); color: var(--ink); }
.seg-btn[aria-pressed="true"] { background: var(--accent-wash); color: var(--accent-text); }

.list-foot { display: flex; align-items: center; gap: 10px; padding: 12px 2px 0; }

.t tbody tr[data-href] { cursor: pointer; }
.t th button { all: unset; cursor: pointer; display: inline-flex; gap: 5px; align-items: center; }
.t th button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.t th .ic { width: 12px; height: 12px; }
.t .r { text-align: right; }

/* Loading placeholders: the shape of the answer while it is on its way. */
.skel {
  display: inline-block; height: 11px; border-radius: 3px;
  background: linear-gradient(90deg, var(--surface-2), var(--surface-3), var(--surface-2));
  background-size: 200% 100%; animation: shimmer 1.2s linear infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* A lead's score, shown with its number rather than as a mystery colour. */
.score {
  display: inline-flex; align-items: center; gap: 6px;
  font: 500 var(--fs-xs)/1 var(--mono); color: var(--ink-2);
}
.score i { width: 34px; height: 4px; border-radius: 2px; background: var(--surface-3); overflow: hidden; }
.score i span { display: block; height: 100%; background: var(--accent); }
.score.is-hot i span { background: var(--good); }
.score.is-cold i span { background: var(--line-strong); }

/* ---- detail screens ------------------------------------------------------
   The record on the left, the context beside it. One column on a phone, where
   the record itself is what the agent came for. */

.detail { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 14px; align-items: start; }
.detail-main, .detail-side { min-width: 0; }
@media (max-width: 1000px) { .detail { grid-template-columns: minmax(0, 1fr); } }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: flex; gap: 12px; padding: 13px 16px; border-bottom: 1px solid var(--line); }
.timeline li:last-child { border-bottom: 0; }
.t-icon {
  display: grid; place-items: center; width: 27px; height: 27px; flex: none;
  border-radius: 50%; background: var(--surface-2); color: var(--ink-3);
  border: 1px solid var(--line);
}
.t-icon .ic { width: 13px; height: 13px; }
/* The meta line wraps as a line, not mid-timestamp: "Sep 9, 7:09 AM" broken
   across two lines reads as two different times. */
.timeline .row { flex-wrap: wrap; row-gap: 2px; }
.timeline .cell-sub { white-space: nowrap; }

/* The score's reasoning: every signal with the points it contributed. */
.reasons { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.reasons li { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; }
.pts {
  font: 500 var(--fs-xs)/1 var(--mono); padding: 4px 6px; border-radius: 5px;
  flex: none; min-width: 34px; text-align: center;
}
.pts.up { background: var(--good-wash); color: var(--good); }
.pts.down { background: var(--danger-wash); color: var(--danger); }

/* ---- call workspace ------------------------------------------------------ */

/* A standing statement of fact, not an alert: recording is always on. */
.notice {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 14px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--ink-2); font-size: var(--fs-sm);
}
.notice .ic { color: var(--ink-3); margin-top: 1px; }

/* What a disposition will do, shown before it is committed to. */
.consequences {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--r-sm);
  background: var(--accent-wash); border: 1px solid var(--accent-line);
  font-size: var(--fs-sm);
}
.consequences .ic { color: var(--accent-text); flex: none; margin-top: 2px; }
.consequences ul { margin: 5px 0 0; padding-left: 17px; display: grid; gap: 3px; color: var(--ink-2); }
.consequences.is-serious { background: var(--danger-wash); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.consequences.is-serious .ic { color: var(--danger); }

.chip[role="radio"][aria-checked="true"] {
  background: var(--accent-wash); border-color: var(--accent-line); color: var(--accent-text);
}

.check { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--ink-2); cursor: pointer; }
.check input { width: 15px; height: 15px; accent-color: var(--accent); }

.btn-lg { height: 42px; padding: 0 18px; font-size: var(--fs-base); }

/* ---- the follow-up queue -------------------------------------------------
   A work list, not a table. Each row is one commitment an agent made on a
   call, and the stripe down its left edge says how late it is — colour first,
   because the queue is scanned, not read. */
.queue { list-style: none; margin: 0; padding: 0; }
.queue li {
  display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-start;
  padding: 13px 16px 13px 13px;
  border-bottom: 1px solid var(--line);
  border-left: 3px solid transparent;
}
.queue li:last-child { border-bottom: 0; }
.queue li.is-overdue { border-left-color: var(--danger); background: var(--danger-wash); }
.queue li.is-today   { border-left-color: var(--accent); }
.q-body { flex: 1; min-width: 0; display: grid; gap: 3px; }
.q-title { font-size: var(--fs-base); font-weight: 500; color: var(--ink); }
.q-meta { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.q-due { font-size: var(--fs-sm); color: var(--ink-2); }
.q-due.is-late { color: var(--danger); font-weight: 500; }
.q-note {
  font-size: var(--fs-sm); color: var(--ink-3); margin: 3px 0 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.q-actions { display: flex; gap: 6px; flex: none; align-items: center; }

/* The bucket heading carries its own count, so "Overdue" is never a surprise
   that only reveals its size once you have scrolled past it. */
.q-head {
  display: flex; align-items: baseline; gap: 8px;
  padding: 11px 16px; background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font: 500 var(--fs-sm)/1 var(--ui);
  letter-spacing: .02em; color: var(--ink-2);
}
.q-head .count { font: 400 var(--fs-xs)/1 var(--mono); color: var(--ink-3); }
.q-head.is-overdue { color: var(--danger); }

@media (max-width: 720px) {
  .q-actions { flex-basis: 100%; }
}

/* Snooze: a row of plain offsets plus an explicit date. Offsets are zone-free
   on purpose — "tomorrow morning" has no meaning until we know which morning
   the prospect is in, and we do not yet store that. */
/* Its own full-width row under the item it belongs to. Without the basis it
   becomes a third column and crushes the title into a vertical strip. */
.snooze {
  flex-basis: 100%; display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  padding-top: 10px; border-top: 1px dashed var(--line);
}
.snooze .input { width: auto; }

/* ---- the pipeline board --------------------------------------------------
   Columns scroll sideways as a unit and each scrolls its own cards, so the
   headers stay put and a long column never pushes the page around. */
.board {
  display: flex; gap: 12px; align-items: flex-start;
  overflow-x: auto; overflow-y: hidden;
  padding-bottom: 10px;
  /* Reaches the viewport bottom rather than growing the page: a board is a
     workspace, not a document. */
  height: calc(100dvh - var(--topbar) - 132px);
  min-height: 320px;
  scrollbar-gutter: stable;
}
.board-col {
  flex: 0 0 258px; display: flex; flex-direction: column;
  max-height: 100%;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); overflow: hidden;
}
.col-head {
  display: flex; align-items: center; gap: 8px; flex: none;
  padding: 10px 12px; border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  font: 500 var(--fs-sm)/1 var(--ui);
}
.col-head .count { font: 400 var(--fs-xs)/1 var(--mono); color: var(--ink-3); }
.col-head .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.col-body { overflow-y: auto; padding: 8px; display: grid; gap: 8px; align-content: start; }
.col-more { padding: 8px 12px 10px; font-size: var(--fs-xs); color: var(--ink-3); text-align: center; }
.col-empty { padding: 18px 12px; text-align: center; font-size: var(--fs-xs); color: var(--ink-3); }

/* A card is grabbable, so it says so before it is grabbed. */
.lead-card {
  display: block; padding: 10px 11px; border-radius: var(--r-sm);
  background: var(--ground); border: 1px solid var(--line);
  cursor: grab; position: relative;
}
.lead-card:hover { border-color: var(--line-strong); }
.lead-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.lead-card.is-dragging { opacity: .45; cursor: grabbing; }
.lc-title {
  font-size: var(--fs-sm); font-weight: 500; color: var(--ink);
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Clear of the move button, which is absolutely positioned over this corner. */
  padding-right: 20px;
}
.lc-meta { display: flex; flex-wrap: wrap; gap: 3px 8px; align-items: center; margin-top: 5px; }
.lc-meta .cell-sub { font-size: var(--fs-xs); }
.lc-score {
  font: 500 var(--fs-xs)/1 var(--mono); padding: 3px 5px; border-radius: 4px;
  background: var(--surface-2); color: var(--ink-2); flex: none;
}
.lc-score.is-hot { background: var(--good-wash); color: var(--good); }
/* Ageing is the thing a board is for: a card that has not moved in a fortnight
   should look different from one that arrived this morning. */
.lc-age.is-stale { color: var(--warn); }
.lc-age.is-rotting { color: var(--danger); font-weight: 500; }

/* While a card is in the air, only the columns it may legally land in are
   targets. The rest visibly step back rather than accepting a drop that the
   server would then refuse. */
.board.is-dragging .board-col { transition: opacity .12s var(--ease); }
.board.is-dragging .board-col:not(.is-target):not(.is-source) { opacity: .38; }
.board-col.is-target { border-color: var(--accent); }
.board-col.is-target .col-head { background: var(--accent-wash); color: var(--accent-text); }
.board-col.is-over .col-body { background: var(--accent-wash); }

/* The keyboard path to the same thing, and the one that is faster anyway. */
.lc-move {
  position: absolute; top: 7px; right: 7px;
  width: 22px; height: 22px; display: grid; place-items: center;
  border-radius: 4px; border: 1px solid transparent; background: transparent;
  color: var(--ink-3); cursor: pointer; opacity: 0;
}
.lead-card:hover .lc-move, .lc-move:focus-visible, .lc-move[aria-expanded="true"] { opacity: 1; }
.lc-move:hover { background: var(--surface-2); border-color: var(--line); color: var(--ink); }
.lc-moves {
  margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line);
  display: grid; gap: 4px;
}
.lc-moves button {
  text-align: left; font-size: var(--fs-xs); padding: 5px 7px; border-radius: 4px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-2); cursor: pointer;
}
.lc-moves button:hover { background: var(--accent-wash); border-color: var(--accent-line); color: var(--accent-text); }

@media (max-width: 720px) {
  .board { height: auto; overflow-x: visible; flex-direction: column; }
  .board-col { flex: none; width: 100%; max-height: none; }
  .col-body { max-height: 420px; }
}

/* ---- the quote builder ---------------------------------------------------
   A line is a row of small fields, not a form: an agent is adjusting numbers
   across several lines at once and should never lose their place. */
.q-lines { display: grid; }
/* Text on its own rows, numbers on theirs. One row of seven columns collapses
   the description to nothing as soon as the phone dock takes the width. */
.q-line {
  display: grid; grid-template-columns: minmax(0, 1fr) 30px; gap: 8px;
  align-items: start; padding: 11px 14px; border-bottom: 1px solid var(--line);
}
.q-line:last-child { border-bottom: 0; }
.q-line.is-problem { background: var(--danger-wash); }
.q-line-main { display: grid; gap: 6px; min-width: 0; }
.q-line-nums { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; }
.q-line .input, .q-line .select { height: 32px; font-size: var(--fs-sm); }
.q-line-nums .select { width: 102px; flex: none; }
.q-field { display: grid; gap: 3px; }
.q-field > span {
  font: 400 var(--fs-xs)/1 var(--ui); color: var(--ink-3);
  letter-spacing: .04em;
}
.q-num { width: 84px; text-align: right; font-family: var(--mono); }
.q-num.is-missing { border-color: var(--danger); }
.q-total {
  margin-left: auto; padding-bottom: 8px;
  font-size: var(--fs-sm); font-weight: 500; white-space: nowrap;
}
.q-remove { padding: 0; width: 30px; }
.warn-text { color: var(--danger); font-weight: 500; font-size: var(--fs-xs); }

.q-line.is-read { grid-template-columns: minmax(0, 1fr); padding: 12px 16px; }
.q-read-main { min-width: 0; }
.q-read-meta {
  display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: baseline; margin-top: 7px;
}
.q-read-total { margin-left: auto; font-weight: 500; font-size: var(--fs-sm); white-space: nowrap; }

/* The two totals, kept visually apart so nobody reads them as one figure. */
.q-sum {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 7px 0; border-bottom: 1px solid var(--line);
}
.q-sum:last-of-type { border-bottom: 0; }
.q-sum span { font-size: var(--fs-sm); color: var(--ink-2); }
.q-sum strong { font-size: var(--fs-lg); }
/* What the agent will actually say on the phone. */
.q-sentence {
  margin-top: 4px; padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--accent-wash); border: 1px solid var(--accent-line);
  color: var(--accent-text); font-size: var(--fs-sm); font-weight: 500;
}

/* The catalogue picker. */
.picker {
  display: grid; gap: 14px; max-height: 340px; overflow-y: auto;
  padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2);
}
.picker-group h4 {
  font: 500 var(--fs-xs)/1 var(--ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 6px;
}
.picker-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px; width: 100%;
  padding: 7px 9px; border-radius: 5px; border: 1px solid transparent;
  background: transparent; color: var(--ink); font-size: var(--fs-sm);
  cursor: pointer; text-align: left;
}
.picker-row:hover { background: var(--ground); border-color: var(--line); }
.picker-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

@media (max-width: 720px) {
  .q-total { padding-bottom: 0; }
}

/* ---- outcome bars --------------------------------------------------------
   The shape of a shift at a glance. Bars are proportional to the largest
   outcome, not to the total: the question is "what is happening most", and
   against a total every bar on a quiet shift is a sliver. */
.bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.bars li { display: grid; grid-template-columns: 116px minmax(0, 1fr) 34px; gap: 10px; align-items: center; }
.bar-label { font-size: var(--fs-sm); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 8px; border-radius: 4px; background: var(--surface-2); overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 4px; background: var(--ink-3); min-width: 3px; }
.bar-fill.b-good   { background: var(--good); }
.bar-fill.b-warn   { background: var(--warn); }
.bar-fill.b-danger { background: var(--danger); }
.bar-value { text-align: right; font-size: var(--fs-sm); color: var(--ink-2); }

/* ---- pace marker ----------------------------------------------------------
   A target bar answers two questions and the second is the one people get
   wrong: not "how much of this is done" but "is that enough YET". On Tuesday
   of a Monday-to-Sunday week, 29% of a weekly target is ahead of the clock and
   71% short of the total, and only one of those is worth worrying about.

   So the track carries a second mark at wherever the period has got to. The
   fill turns amber when it is behind that line — not red, because being behind
   the clock on Tuesday is a nudge, not a failure. */
.bar-track.has-pace { position: relative; overflow: visible; border-radius: 4px; }
.bar-track.has-pace > .bar-fill { position: relative; z-index: 1; }
.bar-pace {
  position: absolute; top: -3px; bottom: -3px; width: 2px; z-index: 2;
  background: var(--ink-2); border-radius: 1px;
  /* Never off the end of its own track, whichever way it is nudged. */
  transform: translateX(-1px);
}
.bar-fill.is-behind { background: var(--warn); }

/* A stat that is also a link should not look like body copy. */
a.stat { text-decoration: none; transition: border-color .12s var(--ease); }
a.stat:hover { border-color: var(--line-strong); }

@media (max-width: 560px) {
  .bars li { grid-template-columns: 96px minmax(0, 1fr) 30px; gap: 8px; }
}

/* ---- banners -------------------------------------------------------------
   A standing statement about the state of the screen, above the content it is
   about. The tone classes are shared with badges, which is why they only
   supply colour — the shape is here.

   These were being used before this existed, inheriting a background from the
   badge palette and nothing else. It looked deliberate and was not. */
.banner {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 12px 15px; border-radius: var(--r-sm);
  border: 1px solid transparent; font-size: var(--fs-sm); line-height: 1.5;
}
.banner .ic { flex: none; margin-top: 2px; }
.banner strong { font-weight: 600; }
.banner a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
/* The content beside the icon, so long text wraps under itself and not the icon. */
.banner > div { min-width: 0; }
/* Anything following a banner needs air; the banner itself sets none below it
   so it can sit flush against a card when that is wanted. */
.banner + * { margin-top: 14px; }

/* ---- message threads -----------------------------------------------------
   A conversation list and a conversation. Both are read far more than they are
   acted on, so the text gets the room. */
.threads { list-style: none; margin: 0; padding: 0; }
.threads li { border-bottom: 1px solid var(--line); }
.threads li:last-child { border-bottom: 0; }
.threads li.is-waiting { border-left: 3px solid var(--accent); }
.thread-row {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 12px 16px; text-decoration: none; color: inherit;
}
.thread-row:hover { background: var(--surface-2); }
.thread-main { flex: 1; min-width: 0; display: grid; gap: 3px; }
.thread-who { font-size: var(--fs-base); font-weight: 500; color: var(--ink); }
.thread-last {
  font-size: var(--fs-sm); color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.thread-side { flex: none; display: grid; gap: 3px; justify-items: end; text-align: right; }

.chat {
  display: grid; gap: 10px; padding: 16px;
  max-height: 52dvh; overflow-y: auto; align-content: start;
}
.msg { max-width: 76%; display: grid; gap: 4px; }
.msg.is-out { justify-self: end; }
.msg-body {
  padding: 9px 12px; border-radius: var(--r); font-size: var(--fs-sm);
  white-space: pre-wrap; overflow-wrap: anywhere;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink);
}
.msg.is-out .msg-body {
  background: var(--accent-wash); border-color: var(--accent-line); color: var(--ink);
}
.msg.is-optout .msg-body {
  background: var(--danger-wash); border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}
.msg-meta { font-size: var(--fs-xs); color: var(--ink-3); padding: 0 3px; }
.msg.is-out .msg-meta { text-align: right; }

.composer { border-top: 1px solid var(--line); padding: 12px 16px; display: grid; gap: 9px; }
.composer-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* Shut, and saying why — never a send button that fails on press. */
.composer.is-shut { background: var(--surface-2); }
.composer.is-shut p {
  display: flex; align-items: center; gap: 8px; margin: 0;
  font-size: var(--fs-sm); color: var(--ink-2);
}
.composer.is-shut .ic { color: var(--ink-3); }

@media (max-width: 560px) {
  .thread-row { flex-direction: column; gap: 6px; }
  .thread-side { justify-items: start; text-align: left; flex-direction: row; display: flex; gap: 10px; }
  .msg { max-width: 92%; }
}

/* ---- payslip lines -------------------------------------------------------
   A label and its working on the left, the amount on the right, and the
   amount never wraps or clips — a payslip is read for its numbers and a
   figure cut off mid-digit is worse than no figure at all. Tabular numerals
   so the column reads as a column rather than as ragged text. */
.pay-lines { display: grid; margin: 0; padding: 0; list-style: none; }
.pay-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px;
  align-items: baseline; padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.pay-line:last-child { border-bottom: 0; }
.pay-line-label { min-width: 0; }
.pay-line-amount {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  white-space: nowrap; font-weight: 500;
}
.pay-line-amount.is-minus { color: var(--danger); }
.pay-line-amount.is-good  { color: var(--good); }
.pay-line-amount.is-bad   { color: var(--danger); }

/* ---- the period calendar -------------------------------------------------
   Attendance is the one screen where the useful answer is WHICH day, not how
   many. A payroll period runs the 16th to the 15th, so this is deliberately
   not a month grid: it is one continuous seven-column strip starting on the
   period's own first weekday, padded only at the front. A month grid here
   would draw two half-months and invite somebody to add up the wrong one.

   Every state is carried by BOTH a fill and a border, never by hue alone —
   a red and a green square of the same lightness are the same square to a
   deuteranope, and this grid is the evidence somebody argues a lost day with.
   The number stays full-contrast on every fill for the same reason. */
.cal {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.cal-head {
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: .04em;
  text-align: center; padding-bottom: 4px;
}
.cal-pad { visibility: hidden; }
.cal-day {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1; min-height: 34px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2);
  font-family: var(--mono); font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
  color: var(--ink);
  cursor: default;
}
.cal-num { line-height: 1; }
.cal-day.is-present  { background: var(--good-wash);   border-color: var(--good); }
.cal-day.is-absent   { background: var(--danger-wash); border-color: var(--danger); }
.cal-day.is-leave    { background: var(--accent-wash); border-color: var(--accent); }
.cal-day.is-sandwich {
  background: var(--danger-wash); border-color: var(--danger);
  border-style: dashed;
}
.cal-day.is-off      { background: var(--surface-3); border-color: var(--line); color: var(--ink-3); }
/* Unmarked is not absent, and must not look like it. Hollow, dashed, and the
   only state with no fill at all: a gap in the record rather than a judgement. */
.cal-day.is-unmarked {
  background: transparent; border-color: var(--warn); border-style: dashed;
  color: var(--ink-2);
}
/* Nothing has happened here yet: the plain resting state, so an open month
   reads as mostly-unwritten rather than mostly-wrong. */
.cal-day.is-pending { background: transparent; border-style: dashed; color: var(--ink-3); }
/* Before they joined, or after they left. Drawn faintly rather than hidden:
   a blank first week makes a new joiner wonder whether days went missing,
   and an empty square says nothing about why it is empty. */
.cal-day.is-pad {
  background: transparent; border-color: transparent; color: var(--ink-3);
  opacity: .4;
}
.cal-flag {
  position: absolute; top: 1px; right: 3px;
  font-size: 9px; font-weight: 700; line-height: 1;
  color: var(--danger);
}
.cal-key {
  display: flex; flex-wrap: wrap; gap: 4px 14px;
  margin: 14px 0 0; padding: 0; list-style: none;
  font-size: var(--fs-xs); color: var(--ink-3);
}
.cal-key li { display: flex; align-items: center; gap: 6px; }
.cal-dot {
  width: 10px; height: 10px; flex: none;
  border: 1px solid var(--line); border-radius: 3px; background: var(--surface-2);
}
.cal-dot.is-present  { background: var(--good-wash);   border-color: var(--good); }
.cal-dot.is-absent   { background: var(--danger-wash); border-color: var(--danger); }
.cal-dot.is-leave    { background: var(--accent-wash); border-color: var(--accent); }
.cal-dot.is-sandwich { background: var(--danger-wash); border-color: var(--danger); border-style: dashed; }
.cal-dot.is-off      { background: var(--surface-3); }
.cal-dot.is-unmarked { background: transparent; border-color: var(--warn); border-style: dashed; }
.cal-dot.is-pending  { background: transparent; border-style: dashed; }

@media (max-width: 560px) {
  .cal { gap: 3px; }
  .cal-day { min-height: 30px; font-size: var(--fs-xs); }
}

/* ---- the phone dock ------------------------------------------------------
   A sales portal is two panels: the work on the left, the phone on the right.
   Zoom's embedded softphone needs at least 350x580, so the dock is fixed at a
   width that fits it without the agent resizing anything mid-call. */

.dock {
  grid-row: 1 / -1;
  background: var(--surface); border-left: 1px solid var(--line);
  position: sticky; top: 0; height: 100dvh;
  display: flex; flex-direction: column;
}
.dock-head {
  height: var(--topbar); flex: none; display: flex; align-items: center; gap: 9px;
  padding: 0 14px; border-bottom: 1px solid var(--line);
}
.dock-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.dock-body iframe { border: 0; width: 100%; flex: 1; min-height: 580px; }

/* ---- toast --------------------------------------------------------------- */

.toasts {
  position: fixed; right: 16px; bottom: 16px; z-index: 120;
  display: grid; gap: 8px; justify-items: end;
}
.toast {
  background: var(--surface-3); color: var(--ink); border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); padding: 10px 14px; font-size: var(--fs-sm);
  box-shadow: var(--shadow-2); animation: rise .18s var(--ease);
  max-width: 380px;
}
.toast.is-error { border-color: color-mix(in srgb, var(--danger) 50%, transparent); }
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }

/* ---- responsive ----------------------------------------------------------
   The rail becomes a drawer and the dock becomes a full-height sheet. An agent
   on a phone is usually on a call, so the phone panel wins the screen. */

@media (max-width: 1180px) {
  .shell.has-dock { grid-template-columns: var(--rail) minmax(0, 1fr); }
  .dock { position: fixed; right: 0; top: 0; width: min(var(--dock), 100vw); z-index: 60; box-shadow: var(--shadow-2); }
  .shell:not(.dock-open) .dock { display: none; }
}

@media (max-width: 900px) {
  .shell, .shell.has-dock { grid-template-columns: minmax(0, 1fr); }
  .topbar, main { grid-column: 1; }
  .rail, .dock { grid-column: 1; }
  .rail {
    position: fixed; left: 0; top: 0; z-index: 80; width: var(--rail);
    transform: translateX(-100%); transition: transform .18s var(--ease);
    box-shadow: var(--shadow-2);
  }
  .shell.rail-open .rail { transform: none; }
  .scrim {
    position: fixed; inset: 0; z-index: 70; background: rgba(0, 0, 0, .5);
    backdrop-filter: blur(2px);
  }
  .page { padding: 16px 16px 60px; }
  .topbar { padding: 0 14px; }
  .clocks { display: none; }
  .dock { width: 100vw; }
}

/* On a phone the top bar has more in it than the screen is wide: a title, a
   search box with a 200px floor, and four controls. Left alone it simply runs
   off the right edge — the page scrolls sideways and the account button is
   unreachable. The search collapses to its icon (the shortcut hint goes with
   it, since there is no Ctrl key here anyway) and the title truncates; every
   control stays on screen and tappable. */
@media (max-width: 560px) {
  .topbar { gap: 8px; padding: 0 12px; }
  .topbar h1 {
    min-width: 0; flex: 0 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .search { min-width: 0; width: 34px; flex: none; padding: 0; justify-content: center; }
  .search span, .search kbd { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
