/* =====================================================================
   OneBloc Calling — app shell.

   Follows the OneBloc App Shell (brand kit) and the OneBloc Design Standard:
   white sidebar, white cards on a soft grey canvas, one accent per module.
   Calling's hue is Lime — the only unclaimed slot on the module wheel, and
   the colour a phone uses for "answer".

   Nothing here hard-codes a colour that the kit already names: everything
   themes off --ob-* / --acc so ob-ui.css, datagrid.css and friends inherit it.
   ===================================================================== */

/* brand.css carries the module accent and the lockup — shared with the public
   landing so the signed-in app and the marketing page cannot drift apart. */
/* ob-tokens.css and brand.css are linked by the layout, in order, each with
   asp-append-version. They used to be pulled in with @import, which silently
   defeated cache busting: app.css?v=... was revalidated but the imported files
   were plain URLs, so a brand or token change never reached a returning user. */

:root {
  --ob-font: "Plus Jakarta Sans", system-ui, -apple-system, sans-serif;

  --radius: 14px;
  --radius-control: 11px;
}

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--ob-font);
  font-size: 14px;
  color: var(--ob-ink);
  background: var(--ob-bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.mono, .num {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

a { color: var(--acc-deep); }
button, input, select, textarea { font-family: inherit; font-size: inherit; }
button { cursor: pointer; }

/* ------------------------------------------------------------ shell */
.app { display: flex; min-height: 100vh; }

.sidebar {
  width: 272px; flex: none;
  background: var(--ob-card);
  border-right: 1px solid var(--ob-line);
  padding: 22px 16px 18px;
  display: flex; flex-direction: column; gap: 3px;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar .brand { padding: 4px 8px 22px; }
.nav-group {
  margin: 18px 12px 6px; font-size: 11px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ob-faint);
}
.nav-item {
  display: flex; align-items: center; gap: 13px;
  padding: 11px 13px; border-radius: var(--radius);
  font-size: 14.5px; font-weight: 600; color: var(--ob-slate);
  text-decoration: none; transition: background .14s, color .14s;
}
.nav-item .ic { width: 20px; height: 20px; flex: none; color: var(--ob-muted); transition: color .14s; }
.nav-item:hover { background: var(--ob-line-soft); color: var(--ob-ink); }
.nav-item.active { background: var(--acc-soft); color: var(--acc-deep); }
.nav-item.active .ic { color: var(--acc-deep); }
.nav-item .badge-count {
  margin-left: auto; font-size: 11.5px; font-weight: 700;
  background: var(--acc-soft); color: var(--acc-deep);
  padding: 2px 8px; border-radius: 999px;
}
.sidebar .grow { flex: 1; min-height: 16px; }
.sidebar .foot {
  border-top: 1px solid var(--ob-line-soft); padding-top: 14px;
  display: flex; align-items: center; gap: 11px;
}
.avatar {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--acc-soft); color: var(--acc-deep);
  font-weight: 800; font-size: 12.5px; letter-spacing: .02em;
}
.who { font-size: 13px; font-weight: 600; color: var(--ob-slate); line-height: 1.35; min-width: 0; }
.who small { display: block; color: var(--ob-faint); font-weight: 500; font-size: 11.5px; }
.who span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.content { flex: 1; min-width: 0; padding: 28px 34px 56px; }

/* ------------------------------------------------------------ page header */
.page-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 22px; flex-wrap: wrap; }
.page-head h1 { font-size: 26px; font-weight: 800; letter-spacing: -.02em; margin: 0 0 3px; }
.page-head .sub { color: var(--ob-muted); font-size: 13.5px; }
.page-head .actions { margin-left: auto; display: flex; gap: 9px; align-items: center; flex-wrap: wrap; }

/* ------------------------------------------------------------ buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 42px; padding: 0 17px; border-radius: var(--radius-control);
  font-weight: 600; font-size: 14px; text-decoration: none;
  border: 1px solid var(--ob-line); background: var(--ob-card); color: var(--ob-slate);
  transition: background .15s, border-color .15s, color .15s, transform .1s;
}
.btn:hover { border-color: #D5D9E0; background: #FBFCFD; color: var(--ob-ink); }
.btn:active { transform: translateY(1px); }
.btn svg { width: 17px; height: 17px; flex: none; }
.btn.primary { background: var(--acc-deep); border-color: var(--acc-deep); color: #fff; box-shadow: var(--ob-shadow-btn); }
.btn.primary:hover { background: #3F6A0B; border-color: #3F6A0B; color: #fff; }
.btn.danger { background: var(--ob-danger); border-color: var(--ob-danger); color: #fff; }
.btn.danger:hover { background: #A9391F; border-color: #A9391F; color: #fff; }
.btn.ghost { border-color: transparent; background: transparent; }
.btn.ghost:hover { background: var(--ob-line-soft); }
.btn.sm { height: 34px; padding: 0 12px; font-size: 13px; border-radius: 9px; }
.btn.wide { width: 100%; }
.btn[disabled], .btn.disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }

.btn.whatsapp { background: #128C4A; border-color: #128C4A; color: #fff; }
.btn.whatsapp:hover { background: #0E7038; border-color: #0E7038; color: #fff; }
.btn.dial { background: var(--acc-deep); border-color: var(--acc-deep); color: #fff; }
.btn.dial:hover { background: #3F6A0B; border-color: #3F6A0B; color: #fff; }

/* ------------------------------------------------------------ cards */
.card {
  background: var(--ob-card); border: 1px solid var(--ob-line);
  border-radius: 16px; padding: 20px 22px; margin-bottom: 18px;
}
.card.flush { padding: 0; overflow: hidden; }
.card-head {
  display: flex; align-items: center; gap: 10px;
  font-size: 15px; font-weight: 700; margin-bottom: 16px;
}
.card-head .sub { font-weight: 500; font-size: 12.5px; color: var(--ob-muted); margin-left: auto; }
.card-head svg { width: 18px; height: 18px; color: var(--acc-deep); }
.card-sec { padding: 16px 22px; border-top: 1px solid var(--ob-line-soft); }

.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.split { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 18px; align-items: start; }

/* ------------------------------------------------------------ chips */
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 600; padding: 5px 11px; border-radius: 999px;
  background: var(--ob-line-soft); color: var(--ob-slate); white-space: nowrap;
}
.chip .d { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.chip.ok { background: var(--ob-success-soft); color: var(--ob-success); }
.chip.warn { background: var(--ob-warning-soft); color: var(--ob-warning); }
.chip.bad { background: var(--ob-danger-soft); color: var(--ob-danger); }
.chip.info { background: var(--acc-soft); color: var(--acc-deep); }
.chip.neutral { background: var(--ob-line-soft); color: var(--ob-muted); }

/* ------------------------------------------------------------ fields */
.field { margin-bottom: 15px; }
.field > label {
  display: flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: var(--ob-slate); margin-bottom: 6px;
}
.input, select.input, textarea.input {
  width: 100%; height: 42px; padding: 0 12px;
  border: 1px solid var(--ob-line); border-radius: var(--radius-control);
  background: #fff; color: var(--ob-ink); outline: none;
  transition: border-color .15s, box-shadow .15s;
}
textarea.input { height: auto; padding: 10px 12px; line-height: 1.55; resize: vertical; }
.input:focus, select.input:focus, textarea.input:focus {
  border-color: var(--acc); box-shadow: 0 0 0 3px rgba(101, 163, 13, .16);
}
.input[readonly] { background: var(--ob-bg); color: var(--ob-slate); }
.hint { font-size: 12px; color: var(--ob-faint); margin: 5px 0 0; }
.field-row { display: flex; gap: 14px; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 220px; margin-bottom: 15px; }

/* ------------------------------------------------------------ tables */
.tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.tbl th {
  text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ob-faint);
  padding: 11px 14px; border-bottom: 1px solid var(--ob-line); white-space: nowrap;
}
.tbl td { padding: 12px 14px; border-bottom: 1px solid var(--ob-line-soft); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: #FCFDFB; }
.tbl td.num, .tbl th.num { text-align: right; }
.tbl .muted { color: var(--ob-muted); }
.table-wrap { overflow-x: auto; }

/* ------------------------------------------------------------ empty / alerts */
.ob-empty .act + a, .ob-empty a.btn { margin-top: 14px; }

.alert {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 15px; border-radius: 12px; font-size: 13.5px;
  border: 1px solid var(--ob-line); background: var(--ob-line-soft); color: var(--ob-slate);
  margin-bottom: 16px;
}
.alert.ok { background: var(--ob-success-soft); border-color: #C9E6D8; color: var(--ob-success); }
.alert.warn { background: var(--ob-warning-soft); border-color: #EEDCB6; color: var(--ob-warning); }
.alert.bad { background: var(--ob-danger-soft); border-color: #F0CFC7; color: var(--ob-danger); }
.alert svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }

/* ------------------------------------------------------------ call card */
.meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.meta-box {
  background: var(--ob-bg); border: 1px solid var(--ob-line);
  border-radius: 12px; padding: 12px 14px; min-width: 0;
}
.meta-box .k {
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ob-faint); margin-bottom: 4px;
}
.meta-box .v { font-size: 15px; font-weight: 700; color: var(--ob-ink); word-break: break-word; }
.meta-box .v.accent { color: var(--acc-deep); }

.outcomes { display: flex; flex-wrap: wrap; gap: 10px; }

/* Desktop shape of the call card's action row; the phone block pins it to the bottom. */
.save-bar { display: flex; gap: 10px; flex-wrap: wrap; }

/* Sign-in-free dialer: one narrow column, sized for the phone it will be used on. */
.share-shell { max-width: 620px; margin: 0 auto; padding: 22px 16px 48px; }

/* Call script on a second screen: read aloud, so size it for reading at arm's length and keep
   the author's line breaks — the pauses in a phone script are deliberate. */
/* ------------------------------------------------------------ call script, second screen
   Two panes, because the agent is doing two things at once: reading the pitch, and finding the
   answer to whatever the customer just said. One column meant scrolling away from your place in
   the script to reach the objections at the bottom — with someone waiting on the line.
   Script on the left, replies pinned on the right, both readable at arm's length. */
.script-shell { max-width: 1500px; margin: 0 auto; padding: 18px 22px 60px; }

.script-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  position: sticky; top: 0; z-index: 40;
  background: var(--ob-bg); padding: 10px 0 12px; margin-bottom: 6px;
  border-bottom: 1px solid var(--ob-line);
}
.script-bar .sep { width: 1px; height: 22px; background: var(--ob-line); margin: 0 4px; }
.script-bar .spacer { flex: 1; }

.script-cols { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.script-side { position: sticky; top: 74px; max-height: calc(100vh - 96px); overflow-y: auto; }

/* Read aloud, so it is sized for glancing down at, not for reading closely. */
.script-read {
  white-space: pre-wrap;
  font-size: 17.5px;
  line-height: 1.8;
  color: var(--ob-ink);
}
.script-main .card-head { font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--ob-muted); }

/* A reply, opened in place in the right pane. */
.reply { border: 1px solid var(--ob-line); border-radius: 12px; margin-bottom: 8px; background: var(--ob-card); }
.reply > summary {
  cursor: pointer; padding: 11px 14px; font-weight: 650; font-size: 14px;
  color: var(--ob-slate); list-style: none; border-radius: 12px;
}
.reply > summary::-webkit-details-marker { display: none; }
.reply > summary::after { content: "+"; float: right; color: var(--ob-faint); font-weight: 700; }
.reply[open] > summary { color: var(--acc-deep); background: var(--acc-soft); border-radius: 12px 12px 0 0; }
.reply[open] > summary::after { content: "2"; }
.reply .script-read { padding: 4px 14px 14px; font-size: 16px; line-height: 1.75; }
.reply-group { margin: 0 0 14px; }
.reply-group > .k {
  font-size: 11px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ob-faint); margin: 0 0 6px 2px;
}

@media (max-width: 1100px) {
  .script-cols { grid-template-columns: minmax(0, 1fr); }
  .script-side { position: static; max-height: none; }
}

.script-read-old {
  white-space: pre-wrap;
  font-size: 16px;
  line-height: 1.75;
  color: var(--ob-ink);
}
details.card > summary { list-style: none; }
details.card > summary::-webkit-details-marker { display: none; }
details.card > summary::after { content: " ▾"; color: var(--ob-muted); }
details.card[open] > summary::after { content: " ▴"; }

/* Sign-in "keep me signed in" row. */
.remember { display: flex; gap: 8px; align-items: center; margin: 4px 0 20px; }
.save-bar .btn.ghost { margin-left: auto; }
.outcomes label {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 15px; border-radius: 12px;
  border: 1px solid var(--ob-line); background: var(--ob-card);
  font-weight: 600; font-size: 13.5px; color: var(--ob-slate);
  cursor: pointer; transition: .14s; user-select: none;
}
.outcomes label:hover { border-color: #CBD3DC; background: #FBFCFD; }
.outcomes input { position: absolute; opacity: 0; width: 0; height: 0; }
.outcomes input:focus-visible + span { outline: 2px solid var(--acc); outline-offset: 3px; border-radius: 4px; }
.outcomes label:has(input:checked) { border-color: var(--acc); background: var(--acc-soft); color: var(--acc-deep); }
.outcomes label svg { width: 16px; height: 16px; }

.script-box {
  background: var(--ob-bg); border: 1px solid var(--ob-line); border-radius: 12px; padding: 14px;
}
.script-box textarea {
  width: 100%; border: 1px solid var(--ob-line); border-radius: 10px; padding: 11px 12px;
  font-size: 13.5px; line-height: 1.6; background: #fff; color: var(--ob-ink);
  resize: vertical; min-height: 148px; outline: none;
}
.script-box textarea:focus { border-color: var(--acc); box-shadow: 0 0 0 3px rgba(101, 163, 13, .16); }

/* ------------------------------------------------------------ stat strip */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: 14px; margin-bottom: 20px; }
.stat {
  background: var(--ob-card); border: 1px solid var(--ob-line);
  border-radius: 16px; padding: 16px 18px;
}
.stat .k { font-size: 11.5px; font-weight: 600; color: var(--ob-muted); margin-bottom: 7px; }
.stat .v {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums;
  font-size: 27px; font-weight: 600; letter-spacing: -.02em; color: var(--ob-ink); line-height: 1.1;
}
.stat .foot { font-size: 12px; color: var(--ob-faint); margin-top: 6px; }
.stat.accent .v { color: var(--acc-deep); }

/* ------------------------------------------------------------ pills / tabs */
.pills { display: flex; flex-wrap: wrap; gap: 8px; }
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 14px; border-radius: 999px; font-size: 13px; font-weight: 600;
  border: 1px solid var(--ob-line); background: var(--ob-card); color: var(--ob-slate); text-decoration: none;
}
.pill:hover { border-color: #CBD3DC; background: #FBFCFD; }
.pill.active { background: var(--acc-soft); border-color: var(--acc); color: var(--acc-deep); }

.lang-switch { display: inline-flex; border: 1px solid var(--ob-line); border-radius: 999px; overflow: hidden; }
.lang-switch button {
  border: 0; background: transparent; padding: 6px 13px;
  font-size: 12.5px; font-weight: 700; color: var(--ob-muted);
}
.lang-switch button.active { background: var(--acc-soft); color: var(--acc-deep); }

/* ------------------------------------------------------------ auth page */
.auth {
  min-height: 100vh; display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
}
.auth-side {
  background: linear-gradient(155deg, #F7FBEF 0%, #EAF3DA 55%, #E1EFCB 100%);
  padding: 46px 52px; display: flex; flex-direction: column; justify-content: space-between;
  border-right: 1px solid var(--ob-line);
}
.auth-side h2 { font-size: 30px; font-weight: 800; letter-spacing: -.025em; margin: 0 0 12px; max-width: 15ch; }
.auth-side p { color: var(--ob-slate); font-size: 14.5px; line-height: 1.65; max-width: 44ch; margin: 0; }
.auth-points { list-style: none; padding: 0; margin: 26px 0 0; display: grid; gap: 12px; }
.auth-points li { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; color: var(--ob-slate); }
.auth-points svg { width: 17px; height: 17px; color: var(--acc-deep); flex: none; margin-top: 2px; }
.auth-form { display: flex; align-items: center; justify-content: center; padding: 40px 32px; }

/* "or" divider between the password form and OneBloc Connect */
.auth-or { display: flex; align-items: center; gap: 12px; margin: 20px 0; color: var(--ob-faint); font-size: 12.5px; }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--ob-line); }
.auth-form .inner { width: 100%; max-width: 380px; }
.auth-form h1 { font-size: 24px; font-weight: 800; letter-spacing: -.02em; margin: 22px 0 5px; }
.auth-form .sub { color: var(--ob-muted); font-size: 13.5px; margin: 0 0 24px; }

/* ------------------------------------------------------------ misc */
.muted { color: var(--ob-muted); }
.faint { color: var(--ob-faint); }
.small { font-size: 12.5px; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.stack { display: flex; flex-direction: column; gap: 4px; }
.row-actions { display: flex; gap: 7px; align-items: center; }
.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;
}
.divider { height: 1px; background: var(--ob-line-soft); margin: 18px 0; }

/* ------------------------------------------------------------ responsive */
@media (max-width: 1080px) {
  .split, .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .app { flex-direction: column; }
  .sidebar {
    width: 100%; height: auto; position: static;
    border-right: 0; border-bottom: 1px solid var(--ob-line);
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 6px; padding: 12px 16px;
  }
  .sidebar .brand { padding: 0 12px 0 0; }
  .sidebar .grow, .nav-group { display: none; }
  .nav-item { padding: 8px 11px; font-size: 13.5px; }
  .sidebar .foot { border-top: 0; padding-top: 0; margin-left: auto; }
  .content { padding: 20px 18px 44px; }
  .auth { grid-template-columns: minmax(0, 1fr); }
  .auth-side { display: none; }
}
/* ------------------------------------------------------------ phone
   Agents work the queue one-handed, on a phone, while the call is live — so this
   block is about thumbs, not just width. Measured at 375x812 before it existed:

   - the sidebar flattened into a WRAPPED block 227px tall, 28% of the screen, and
     the agent scrolled past the whole nav on every single lead. Now one row that
     scrolls sideways, ~60px, and it sticks so the queue is always one tap away.
   - every control was under the 44px minimum touch target: outcome chips 39px,
     Call now / WhatsApp / Save 42px, Copy and Sign out 34px, EN/中文 28px.
   - inputs were 14px. Under 16px, iOS Safari zooms the whole page when the field
     takes focus, and the agent is pinching back out with a customer on the line.
     16px is the exact threshold, so it is a hard floor, not a preference.
   - the card runs ~4 screens tall, which put Save an entire scroll away from the
     outcome the agent just tapped. The save row is pinned to the bottom instead. */
@media (max-width: 640px) {
  .sidebar {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    gap: 4px; padding: 8px 12px;
    position: sticky; top: 0; z-index: 30;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .sidebar::-webkit-scrollbar { display: none; }
  .sidebar .brand { padding: 0 8px 0 0; flex: none; }
  .sidebar .foot { flex: none; margin-left: 8px; padding-top: 0; }
  .nav-item {
    flex: none; white-space: nowrap;
    min-height: 44px; padding: 0 12px;
    display: inline-flex; align-items: center;
  }

  .content { padding: 16px 14px 40px; }

  .btn { min-height: 46px; }
  .btn.sm { min-height: 44px; }

  .input, select.input, textarea.input, .script-box textarea { font-size: 16px; }
  /* height is set outright at 42px above, so min-height alone would lose to it. */
  .input, select.input { height: auto; min-height: 46px; }

  .lang-switch button { min-height: 44px; padding: 0 14px; }
  /* A 13px checkbox is not a handset target; grow the box and the whole row. */
  .remember { min-height: 44px; }
  .remember input[type=checkbox] { width: 22px; height: 22px; }
  /* Period filters and the cross-links on My day — 32px by default. */
  .pill { min-height: 44px; display: inline-flex; align-items: center; }
  .sidebar .brand { display: inline-flex; align-items: center; min-height: 44px; }

  /* Tagging: two large targets per row, wording allowed to wrap onto two lines
     rather than setting each chip's width from its own text — ragged pills of
     96px to 167px are what made this fiddly to hit accurately. */
  .outcomes { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .outcomes label {
    min-height: 54px; padding: 8px 10px; font-size: 14px;
    justify-content: center; text-align: center; line-height: 1.25;
  }

  /* DataGrid (My calls, Leads). These overrides live HERE rather than in datagrid.css because
     the custom-control kit is shared and locked — this app widens the targets for its own phone
     users without changing the control for every other OneBloc site. Row actions were 18–30px,
     which is roughly half a fingertip. */
  .dg-search, .ctrl, .tbtn, .dg-vTable, .dg-vList, .pg { min-height: 44px; }
  .dg-search, .dg-pageSize, .dg-status { font-size: 16px; }
  .copy, .qedit, .kebab { min-width: 40px; min-height: 40px; }
  .pg { min-width: 44px; }

  .save-bar {
    position: sticky; bottom: 0; z-index: 20;
    margin: 0 -14px -40px; padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
    background: var(--ob-card); border-top: 1px solid var(--ob-line);
  }
  .save-bar .btn { flex: 1 1 calc(50% - 5px); }
  .save-bar .btn.primary { flex: 1 1 100%; }
}

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

/* ------------------------------------------------------------ help page */
.help-h { font-size: 15.5px; font-weight: 700; margin: 22px 0 8px; }
.help-h:first-child { margin-top: 0; }
.help-p { margin: 0 0 10px; font-size: 14.5px; color: var(--ob-slate); line-height: 1.65; max-width: 82ch; }
/* The kit's tab rail is a single flex row; with eleven topics it has to wrap
   rather than overflow the card. */
.obtabs > nav { flex-wrap: wrap; }
.obtabs [data-tabpanel] table.tbl { margin-top: 6px; }

/* ---------------------------------------------------------------- kit bridge
   The shared kit controls declare their palette scoped to the component class:

     --accent: var(--ob-accent, #6366F1);  --accent-weak: #EEF0FE;  --accent-ink: #4F46E5;

   Only --accent has an --ob-* hook, so it picks up the module hue while the other
   two stay hardcoded INDIGO. That is a kit bug, not a Calling one, and it was
   already tinting OBSelect and OBUpload indigo before the help page existed.

   This correction lives HERE, not in brand.css, and the reason is cascade order.
   brand.css must load BEFORE the kit files so their var(--ob-accent) fallback can
   resolve - which also puts it before ob-tabs.css, where an equal-specificity rule
   would then lose to the kit's own. app.css is the last sheet in every layout, so
   this is the only place a .obtabs rule can win.

   Fix upstream in the CustomControl kit by giving both an --ob-* fallback; then
   this block can go. */
.obtabs, .obsteps, .obsel, .obup {
  --accent-ink: var(--acc-deep);
  --accent-weak: var(--acc-soft);
}

/* ------------------------------------------------------------ outstanding balance */
.owing {
  display: flex; gap: 28px; align-items: center; flex-wrap: wrap;
  border: 1px solid var(--ob-line); border-left: 4px solid var(--acc);
  border-radius: var(--radius); padding: 14px 18px; margin-bottom: 16px;
  background: var(--acc-soft);
}
.owing.late { border-left-color: var(--ob-danger); background: var(--ob-danger-soft); }
.owing .k { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ob-muted); }
.owing .amt { font-size: 27px; font-weight: 800; letter-spacing: -.02em; color: var(--acc-deep); margin-top: 2px; }
.owing.late .amt { color: var(--ob-danger); }
.owing .v { font-size: 15px; font-weight: 600; margin-top: 3px; }

/* ------------------------------------------------------------ import samples */
.samples { margin-bottom: 18px; }
.samples > .k { font-size: 12.5px; font-weight: 600; color: var(--ob-slate); margin-bottom: 8px; }
.sample {
  display: flex; gap: 12px; align-items: flex-start; text-decoration: none;
  border: 1px solid var(--ob-line); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 8px; color: inherit;
  transition: border-color .14s, background .14s;
}
.sample:hover { border-color: var(--acc); background: var(--acc-soft); }
.sample svg { width: 20px; height: 20px; flex: none; color: var(--acc-deep); margin-top: 2px; }
.sample strong { display: block; font-size: 14px; }
.sample small { display: block; font-size: 12.5px; color: var(--ob-muted); margin-top: 2px; line-height: 1.5; }
.sample em {
  display: block; font-style: normal; margin-top: 6px;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 11.5px; color: var(--ob-faint);
}

/* ------------------------------------------------------------ collapsible nav group */
.nav-fold > summary.nav-group {
  display: flex; align-items: center; gap: 6px;
  cursor: pointer; list-style: none; user-select: none;
  border-radius: 8px; padding: 6px 12px; margin: 18px 0 6px;
}
/* Safari draws its own triangle unless this is suppressed. */
.nav-fold > summary.nav-group::-webkit-details-marker { display: none; }
.nav-fold > summary.nav-group:hover { background: var(--ob-line-soft); color: var(--ob-slate); }
.nav-fold > summary.nav-group:focus-visible { outline: 2px solid var(--acc); outline-offset: 1px; }
.nav-fold .chev { width: 14px; height: 14px; margin-left: auto; transition: transform .16s ease; }
.nav-fold[open] .chev { transform: rotate(180deg); }
/* A folded group still has to advertise that something lives in it. */
.nav-fold:not([open]) > summary.nav-group { color: var(--ob-muted); }

/* ------------------------------------------------------------ reports */
/* rate bar, used wherever a percentage needs to read at a glance */
.rate { display: flex; align-items: center; gap: 9px; }
.rate-bar { flex: 1; min-width: 60px; height: 7px; border-radius: 999px; background: var(--ob-line); overflow: hidden; }
.rate-bar i { display: block; height: 100%; border-radius: 999px; background: var(--acc); }

/* promise funnel */
.funnel { display: grid; gap: 9px; }
.fn-row { display: grid; grid-template-columns: 130px 1fr 52px 110px; gap: 12px; align-items: center; }
.fn-bar { height: 22px; border-radius: 6px; background: var(--ob-line-soft); overflow: hidden; }
.fn-bar i { display: block; height: 100%; border-radius: 6px; }
.fn-n, .fn-amt { text-align: right; font-size: 13px; font-variant-numeric: tabular-nums; }
.fn-amt { color: var(--ob-slate); font-weight: 600; }

/* campaign burn-down */
.burn-bar { display: flex; height: 28px; border-radius: 8px; overflow: hidden; border: 1px solid var(--ob-line); }
.burn-bar i { display: block; }
.burn-key { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 12px; font-size: 12.5px; color: var(--ob-slate); }
.burn-key b { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 6px; }
.split-note {
  display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--ob-line);
  font-size: 13px; color: var(--ob-muted);
}
.split-note strong { color: var(--ob-ink); }

/* attempt effectiveness */
.attempts { display: grid; gap: 8px; }
.attempt-row { display: grid; grid-template-columns: 66px 1fr 44px 56px; gap: 10px; align-items: center; }
.attempt-row .muted { text-align: right; }

/* best-time heat map */
.heat { display: grid; gap: 3px; min-width: 520px; }
.heat .lbl { font-size: 11px; color: var(--ob-muted); text-align: center; line-height: 20px; font-family: "JetBrains Mono", ui-monospace, monospace; }
.heat .lbl.day { text-align: left; }
.heat i { display: block; height: 20px; border-radius: 3px; background: var(--acc-deep); }
/* "too few calls to judge" reads as absence, not as a low rate */
.heat i.thin { background: var(--ob-line-soft); border: 1px dashed var(--ob-line); height: 18px; }

/* compliance alarm */
.alarm { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; flex: none; }
.alarm svg { width: 22px; height: 22px; }
.alarm.ok { background: var(--ob-success-soft); color: var(--ob-success); }
.alarm.bad { background: #fff; color: var(--ob-danger); }

/* ------------------------------------------------------------ report charts */
.rate { display: flex; align-items: center; gap: 9px; }
.rate-bar { display: block; flex: 1; min-width: 60px; height: 8px; border-radius: 999px; background: var(--ob-line); overflow: hidden; }
.rate-bar i { display: block; height: 100%; border-radius: 999px; background: var(--acc); }

.funnel { display: grid; gap: 8px; }
.fn-row { display: grid; grid-template-columns: 132px 1fr 46px 116px; gap: 12px; align-items: center; }
.fn-bar { display: block; height: 22px; border-radius: 6px; background: var(--ob-line-soft); overflow: hidden; }
.fn-bar i { display: block; height: 100%; border-radius: 6px; }
.fn-n, .fn-amt { text-align: right; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.fn-amt { color: var(--ob-muted); }

.burn-bar { display: flex; height: 26px; border-radius: 8px; overflow: hidden; border: 1px solid var(--ob-line); }
.burn-bar i { display: block; }
.burn-key { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; font-size: 12.5px; color: var(--ob-slate); }
.burn-key b { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; }

.split-note {
  display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  border-top: 1px dashed var(--ob-line); margin-top: 14px; padding-top: 12px;
  font-size: 13px; color: var(--ob-muted);
}

.attempts { display: grid; gap: 8px; }
.attempt-row { display: grid; grid-template-columns: 132px 1fr 44px 56px; gap: 12px; align-items: center; }
.attempt-row .mono { font-variant-numeric: tabular-nums; text-align: right; }

/* day × hour heat grid */
.heat { display: grid; gap: 3px; align-items: center; }
.heat .lbl { font-size: 10.5px; color: var(--ob-faint); text-align: center; font-family: "JetBrains Mono", ui-monospace, monospace; }
.heat .lbl.day { text-align: left; }
.heat i { display: block; height: 20px; border-radius: 3px; background: var(--acc-deep); }
/* Not enough calls to judge: outlined rather than pale, so it reads as "no data" not "low". */
.heat i.thin { background: transparent; border: 1px dashed var(--ob-line); }

/* daily columns */
.daybars { display: flex; align-items: flex-end; gap: 4px; height: 132px; min-width: 320px; }
.daybars.tall { height: 168px; }
.daybar { flex: 1 1 0; min-width: 12px; display: flex; flex-direction: column; align-items: center; gap: 5px; height: 100%; justify-content: flex-end; }
.daybar .col {
  display: flex; flex-direction: column-reverse; justify-content: flex-start;
  width: 100%; min-height: 3px; border-radius: 4px 4px 0 0;
  background: var(--acc); overflow: hidden;
}
.daybar .col.stack { background: var(--ob-line-soft); }
.daybar .col i.fail { display: block; width: 100%; background: var(--ob-danger); }
.daybar .lbl { font-size: 10px; color: var(--ob-faint); }

.seg.ok { background: var(--ob-success); }
.seg.warn { background: var(--ob-warning); }
.seg.bad { background: var(--ob-danger); }
.seg.info { background: var(--acc); }
.seg.neutral { background: var(--ob-faint); }
b.seg { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; }
i.seg { display: block; width: 100%; }

/* Deleting a campaign is the one irreversible action in the product, so it hides behind a
   disclosure and asks for the code typed out. Styled as a quiet link until opened — a red
   button sitting permanently in every row invites the click we are trying to prevent. */
.danger-zone > summary {
    list-style: none; cursor: pointer; font-size: 13px; font-weight: 600;
    color: var(--ob-muted); padding: 6px 10px; border-radius: var(--ob-radius, 8px);
}
.danger-zone > summary::-webkit-details-marker { display: none; }
.danger-zone > summary:hover { color: var(--ob-danger); background: var(--ob-danger-soft, rgba(200,60,30,.08)); }
.danger-zone[open] > summary { color: var(--ob-danger); }
.danger-zone form {
    margin-top: 8px; padding: 12px; text-align: left;
    border: 1px solid var(--ob-danger); border-radius: 12px;
    background: var(--ob-danger-soft, rgba(200,60,30,.06));
    display: flex; flex-direction: column; gap: 8px; min-width: 260px;
}
.danger-zone form p { margin: 0; }
.danger-zone input { width: 100%; }

@media (max-width: 640px) {
    .danger-zone form { min-width: 0; width: 100%; }
}

/* ---------------------------------------------------------------- follower screen
   Read across a desk, not held in the hand: the agent is on the phone and glancing at this.
   Everything is a size up from the call card. */
.follow { max-width: 1100px; margin: 0 auto; padding: 8px 0 24px; }
.follow-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; }
.follow-bar .spacer { flex: 1; }
.follow-who { min-height: 220px; }
.follow-who h1 { font-size: 40px; line-height: 1.15; margin: 0 0 6px; }
.follow-who .big { font-size: 22px; color: var(--ob-muted); margin: 0 0 18px; }
.follow-money {
    display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 6px 14px;
    align-items: baseline; padding: 16px 18px; margin-bottom: 18px;
    border-left: 4px solid var(--ob-danger); border-radius: 0 12px 12px 0;
    background: var(--ob-danger-soft, rgba(200,60,30,.06));
}
.follow-money .k { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--ob-muted); }
.follow-money .v { font-size: 26px; font-weight: 700; }
.follow-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px; }
.follow-fields > div { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; background: var(--ob-surface-2, #f6f7f5); border-radius: 10px; }
.follow-fields .k { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ob-muted); }
.follow-fields .v { font-size: 17px; }

/* On a screen that has lost contact the identity is gone, so what is left must read as an
   explanation rather than an empty page someone assumes is just quiet. */
.follow-who .tt { font-size: 20px; font-weight: 600; color: var(--ob-danger); }

/* ---------------------------------------------------------------- campaign type
   An irreversible choice. The explanation sits in the option, not in a tooltip, and the
   warning is next to the legend where it cannot be missed. */
.kind-pick { border: 0; padding: 0; margin: 0 0 18px; }
.kind-pick > legend { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding: 0; margin-bottom: 8px; font-size: 13px; font-weight: 600; }
.kind-pick .warn-note { font-size: 12px; font-weight: 500; color: var(--ob-danger); }
.kind-opt {
    display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline;
    padding: 12px 14px; margin-bottom: 8px; cursor: pointer;
    border: 1px solid var(--ob-line); border-radius: 12px;
}
.kind-opt:hover { border-color: var(--acc); }
.kind-opt:has(input:checked) { border-color: var(--acc); background: var(--acc-soft); }
.kind-opt input { grid-row: 1 / span 2; margin: 3px 0 0; }
.kind-opt .t { font-weight: 600; font-size: 14px; }
.kind-opt .d { grid-column: 2; font-size: 13px; line-height: 1.55; color: var(--ob-muted); }

/* Country under the number on the dialer. Overseas is tinted, local is quiet: the agent needs to
   notice the exception, and on this book Malaysia is the rule. */
.meta-box .country {
    margin-top: 6px; font-size: 13px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 9px; border-radius: 999px;
}
.meta-box .country.local    { background: var(--ob-surface-2, #f1f3ef); color: var(--ob-muted); }
.meta-box .country.overseas { background: var(--acc-soft); color: var(--acc-deep); }

/* The agreements behind a combined balance. Worst first, because that is the one the agent leads
   with — a customer three months down on one bike and current on another is one conversation. */
.assets { margin: 0 0 16px; padding: 12px 14px; background: var(--ob-surface-2, #f6f7f5); border-radius: 12px; }
.assets .k { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ob-muted); margin-bottom: 6px; }
.assets .tbl.compact td { padding: 5px 8px; font-size: 14px; }
.assets .tbl.compact tr + tr td { border-top: 1px solid var(--ob-line, #e6e8e3); }

/* Rejected rows, on the page rather than only in a download. Closed by default: on a clean file
   this is a line of text, and on a bad one it is the first thing you open. */
.rejects { margin: 14px 0 0; border: 1px solid var(--ob-line, #e6e8e3); border-radius: 12px; }
.rejects > summary {
    list-style: none; cursor: pointer; padding: 10px 14px;
    font-size: 14px; font-weight: 600; color: var(--ob-danger);
}
.rejects > summary::-webkit-details-marker { display: none; }
.rejects > summary::after { content: " ▾"; color: var(--ob-muted); font-weight: 400; }
.rejects[open] > summary { border-bottom: 1px solid var(--ob-line, #e6e8e3); }
.rejects > div, .rejects > p { padding: 0 14px; }
.rejects .tbl { margin: 10px 0; }

/* ---------------------------------------------------------------- import review
   The check result gets the page to itself: someone confirming ten thousand rows is reading
   carefully, and the upload form beside it was only ever in the way. */
.review-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 16px; flex-wrap: wrap; }
.review-head .k { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ob-muted); }
.review-head .f { font-size: 17px; font-weight: 600; }

.review-nums { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 0 0 20px; }
.review-nums .n { padding: 14px 16px; background: var(--ob-surface-2, #f6f7f5); border-radius: 12px; }
.review-nums .n .v { font-size: 26px; font-weight: 700; line-height: 1.1; }
.review-nums .n .k { font-size: 12px; color: var(--ob-muted); margin-top: 2px; }
.review-nums .n.big { background: var(--acc-soft); }
.review-nums .n.big .v { color: var(--acc-deep); font-size: 34px; }
.review-nums .n.warn .v { color: var(--ob-warn, #9a6b00); }
.review-nums .n.bad  .v { color: var(--ob-danger); }

/* An indeterminate bar, deliberately. The file is parsed in one pass, so any percentage here
   would be a number we made up. */
.reading { margin-top: 14px; }
.reading .bar { height: 6px; border-radius: 999px; background: var(--ob-line, #e6e8e3); overflow: hidden; }
.reading .bar > span { display: block; width: 40%; height: 100%; border-radius: 999px; background: var(--acc); animation: ob-slide 1.1s ease-in-out infinite; }
@keyframes ob-slide { 0% { margin-left: -40%; } 100% { margin-left: 100%; } }

/* ---------------------------------------------------------------- agent activity
   A heat grid rather than a chart: a supervisor scanning fifteen agents over thirty days is
   looking for the empty cells, and empty is far easier to see in a grid than a missing bar.

   The class names are deliberately not "heat" and "who", which both already exist above for
   other things. The first version used them and "heat" carries display:grid for the best-time
   map — which took every cell out of the table layout, so the browser wrapped a whole month of
   them into one anonymous cell and the grid rendered as a single stacked column. The markup was
   correct throughout, which is why nothing that reads HTML noticed. */
.tbl.activity th.rowlabel, .tbl.activity td.rowlabel {
    position: sticky; left: 0; background: var(--ob-surface-1, #fff);
    text-align: left; white-space: nowrap; font-weight: 600; z-index: 1;
}
.tbl.activity th.day { padding: 6px 4px; font-weight: 500; line-height: 1.1; }
.tbl.activity th.day .d { display: block; font-size: 13px; }
.tbl.activity th.day .m { display: block; font-size: 10px; color: var(--ob-muted); text-transform: uppercase; }
.tbl.activity td.heatcell {
    padding: 6px 4px; min-width: 34px; font-variant-numeric: tabular-nums;
    /* --s is 0 for an unworked day, so it stays the page colour and reads as a gap. */
    background: color-mix(in srgb, var(--acc) calc(var(--s, 0) * 100%), transparent);
    color: var(--ob-ink, #1c1d1a);
}
/* The day a supervisor clicked, and the link that gets them there. The anchor fills the cell so
   the whole coloured box is the target; the cell itself stays a table-cell, which is the mistake
   this grid already made once. */
.tbl.activity td.heatcell a.drill {
    display: block; margin: -6px -4px; padding: 6px 4px;
    color: inherit; text-decoration: none;
}
.tbl.activity td.heatcell a.drill:hover { text-decoration: underline; }
.tbl.activity td.heatcell.picked { outline: 2px solid var(--acc-deep, #4a7c2f); outline-offset: -2px; }
.tbl.activity td.rowlabel .note { display: block; font-weight: 500; }
.tbl.activity td.heatcell:empty::after { content: "·"; color: var(--ob-faint, #c9ccc4); }
.tbl.activity tfoot td { border-top: 2px solid var(--ob-line, #e6e8e3); font-weight: 600; }
