/* =============================================================================
   ProducerPoint — production overrides.

   styles.css and design.css are the approved Gate 3 visual direction (Option C),
   adopted verbatim. This file changes only what production genuinely differs on,
   so the approved system stays the authority and drift is visible in one place.
   ============================================================================= */

:root {
  /* The prototype reserved space for its banner. Production has none, so the
     shell gets the whole viewport back rather than a 37px gap at the bottom. */
  --banner-h: 0px;
}

/* ------------------------------------------------------------- sign-in ----- */
/* The prototype's launch screen was a visual entry state with no authentication
   behind it. This one is a real gate: nothing past it renders until the server
   has issued a session. */
/* THE ENTRANCE. A full-viewport navy canvas, one compact card, and the brand
   itself as the room it stands in.

   The founder asked for oversized ghost wordmark typography behind the form —
   the one idea taken from elsewhere — in ProducerPoint's own language rather
   than the neon it came wrapped in. So: the product's navy, the product's
   wordmark face, a hairline horizon, and depth from a gradient instead of
   glow. Nothing here pulses, blooms or animates.

   `overflow: hidden` is load-bearing. The wordmark is deliberately wider than
   the screen and runs off both edges; without this it would scroll the page
   sideways at every width, which is the one thing the composition must not do. */
.signin {
  position: relative; overflow: hidden;
  min-height: 100vh; min-height: 100svh;
  display: grid; place-items: center;
  padding: 24px;
  background:
    /* A cool wash from above, a pale navy settling at the foot, and one very
       faint vertical seam pair — the public site's spine motif at its quietest.
       No glow, nothing animated: the depth comes from tone, not light. */
    linear-gradient(180deg, rgba(255, 255, 255, .85) 0%, transparent 34%),
    radial-gradient(1250px 700px at 50% -16%, rgba(37, 99, 235, .13), transparent 62%),
    radial-gradient(1000px 560px at 12% 108%, rgba(18, 48, 92, .16), transparent 60%),
    var(--canvas);
}
/* The seams. Two hairlines at the thirds, visible only as a change in tone. */
.signin::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, transparent calc(33.333% - 1px), var(--line) 33.333%,
      transparent calc(33.333% + 1px)),
    linear-gradient(90deg, transparent calc(66.666% - 1px), var(--line) 66.666%,
      transparent calc(66.666% + 1px));
  opacity: .5;
}
[data-theme="dark"] .signin {
  background:
    radial-gradient(1200px 680px at 50% -18%, rgba(37, 99, 235, .20), transparent 64%),
    radial-gradient(900px 520px at 8% 106%, rgba(13, 36, 71, .55), transparent 62%),
    var(--canvas);
}

/* The ghost layer. NO HORIZON RULE: a hairline across the middle crossed the
   card at exactly its waist and read as a seam through it rather than as
   architecture. The corner marks carry that job on their own. */
.signin__bg {
  position: absolute; inset: 0; pointer-events: none; user-select: none;
  overflow: hidden;
}

/* ---- the wordmark, as it was --------------------------------------------
   A pass replaced this with the NP mark blown up to min(158vh, 1180px) and the
   founder's answer was immediate: way too big, and he wanted the page he had
   with two things added to it. So the word is back exactly as it was, and the
   mark below is an ADDITION at the scale an addition takes. */
.signin__ghost {
  position: absolute; left: 50%; bottom: 3vh; transform: translateX(-50%);
  font-size: clamp(52px, 12.5vw, 240px);
  line-height: 1; letter-spacing: .02em; white-space: nowrap;
  font-weight: 700; color: var(--ink);
  opacity: .05;
}
[data-theme="dark"] .signin__ghost { opacity: .08; }

/* ---- ADDITION ONE: the founder's mark, at the size of an accent ----------
   `np-mark-knockout.png` is the supplied artwork (BRAND_MANIFEST.md) — the
   overlapping N+P with its halftone interior and the POINT lockup. It sits in
   the open space ABOVE the card, which is where the composition was thinnest,
   and it is deliberately smaller than the card it sits over: 168px against a
   428px card, so it reads as a mark placed on the page rather than as the page.

   It is a white knockout on transparency, so light inverts it — invert(1) turns
   white to black and leaves the alpha alone — and dark takes it as supplied.
   One file, both themes. */
.signin__emblem {
  position: absolute; left: 50%; top: 50%;
  width: 168px; height: 168px;
  transform: translate(-50%, calc(-50% - 300px));
  background: url("assets/brand/np-mark-knockout.png") center / contain no-repeat;
  filter: invert(1);
  opacity: .10;
}
[data-theme="dark"] .signin__emblem { filter: none; opacity: .11; }
/* It needs clear air above the card to be an accent rather than a collision. */
@media (max-height: 820px), (max-width: 560px) { .signin__emblem { display: none; } }

/* ---- ADDITION TWO: the approach, and the point it arrives at -------------
   One touch of the dashboard blue, doing something the product means. Not a
   signal passing through: a line that travels the width of the field and
   ARRIVES, and a single lit point where it stops — a day of leads, calls and
   follow-ups converging on the one place the producer has to be next. It is
   also the brand's own word, sitting directly beneath it.

   IT TRACKS THE WORDMARK. The word is `bottom: 3vh` with `line-height: 1`, so
   its cap line is `97vh` minus its own font size; the rule sits 26px above that
   at every viewport rather than at a number that happens to look right on one
   monitor. Twelve seconds a pass, so it reads as something the page does
   occasionally rather than as an animation running at you. */
.signin__approach {
  position: absolute; left: 0; right: 0;
  top: calc(97vh - clamp(52px, 12.5vw, 240px) - 26px); height: 1px;
  background: linear-gradient(90deg,
    transparent, var(--line-2) 16%, var(--line-2) 84%, transparent);
  opacity: .8;
}
/* The travelling segment. It eases in from the left edge, decelerates hard as
   it nears the middle, and stops — it does not run out the other side, because
   arriving is the point. */
.signin__approach::before {
  content: ""; position: absolute; top: 0; height: 1px;
  left: 0; width: 50%;
  background: linear-gradient(90deg,
    transparent, rgba(37, 99, 235, .45) 62%, var(--electric) 100%);
  transform-origin: left center;
  animation: signin-approach 12s cubic-bezier(.16, .84, .3, 1) infinite;
}
[data-theme="dark"] .signin__approach::before {
  background: linear-gradient(90deg,
    transparent, rgba(91, 156, 255, .5) 62%, var(--electric) 100%);
}
@keyframes signin-approach {
  0%   { transform: scaleX(0);  opacity: 0; }
  6%   { opacity: 1; }
  42%  { transform: scaleX(1);  opacity: 1; }
  56%  { transform: scaleX(1);  opacity: 0; }
  100% { transform: scaleX(0);  opacity: 0; }
}

/* The point itself. Lit at all times — the line arriving is the event, the
   point is the fact. */
.signin__point {
  position: absolute; left: 50%;
  top: calc(97vh - clamp(52px, 12.5vw, 240px) - 26px);
  width: 7px; height: 7px; margin: -3px 0 0 -3.5px;
  border-radius: 50%; background: var(--electric);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .13), 0 0 14px 1px rgba(37, 99, 235, .40);
}
[data-theme="dark"] .signin__point {
  box-shadow: 0 0 0 3px rgba(91, 156, 255, .16), 0 0 18px 2px rgba(91, 156, 255, .45);
}
/* One ring, released the moment the line lands, then nothing for six seconds. */
.signin__point::after {
  content: ""; position: absolute; inset: -3px;
  border-radius: 50%; border: 1px solid var(--electric);
  animation: signin-arrive 12s cubic-bezier(.16, .84, .3, 1) infinite;
}
@keyframes signin-arrive {
  0%, 40%  { transform: scale(.6); opacity: 0; }
  46%      { transform: scale(1);  opacity: .75; }
  62%      { transform: scale(2.6); opacity: 0; }
  100%     { transform: scale(2.6); opacity: 0; }
}
/* A producer who has asked their system to stop moving things gets the line and
   the point, and no travel at all. */
@media (prefers-reduced-motion: reduce) {
  .signin__approach::before { animation: none; transform: scaleX(1); opacity: .55; }
  .signin__point::after { animation: none; opacity: 0; }
}
/* It needs room between the card and the word. On a short or narrow viewport
   there is not any, and a hairline crossing either is a defect. */
@media (max-height: 700px), (max-width: 560px) {
  .signin__approach, .signin__point { display: none; }
}

/* Corner marks — the public site's editorial device, at its quietest. */
.signin__corner {
  position: absolute; z-index: 2; pointer-events: none;
  font-size: 10px; font-weight: 650; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3); opacity: .5;
}
.signin__corner--tl { top: 22px; left: 26px; }
.signin__corner--br { bottom: 22px; right: 26px; }
.signin__corner--bl { bottom: 22px; left: 26px; }

.signin__card {
  position: relative; z-index: 2;
  width: 100%; max-width: 428px; padding: 34px 34px 30px;
  background: var(--surface); border-radius: var(--r-lg);
  border: 1px solid var(--line);
  /* Layered rather than one drop: a light top edge so the card reads as a
     physical plate, a close contact shadow, and a wide soft one for lift. The
     same construction the public site's command bar uses. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .9),
    0 1px 2px rgba(11, 26, 51, .06),
    0 18px 40px -22px rgba(11, 26, 51, .38);
}
[data-theme="dark"] .signin__card {
  /* A plate lit from above, the same way the public site's command bar reads. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 30px 70px -40px rgba(2, 8, 20, .9);
}
.signin__id { display: flex; align-items: center; gap: 13px; margin-bottom: 26px; }
.signin__mark {
  width: 44px; height: 44px; border-radius: 13px; overflow: hidden; flex: none;
  background: linear-gradient(158deg, #22315a 0%, #0b1a33 78%);
  display: grid; place-items: center;
}
.signin__mark img { width: 100%; height: 100%; object-fit: cover; }
.signin__name { font-size: 17px; line-height: 1.15; }
.signin__by {
  font-size: 10.5px; font-weight: 650; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3); margin-top: 3px;
}
.signin__welcome { font-size: 23px; line-height: 1.2; margin: 0 0 6px; letter-spacing: -.01em; }
.signin__body { font-size: 13.5px; color: var(--ink-2); line-height: 1.55; margin: 0 0 22px; }
.signin__field { display: block; margin-bottom: 16px; }
.signin__field span {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px;
}
.signin__submit { width: 100%; justify-content: center; }
.signin__secure {
  display: flex; align-items: center; gap: 7px;
  margin: 16px 0 0; font-size: 11.5px; color: var(--ink-3);
}
/* A lock from the product's own icon set, not a decorative dot. A coloured
   bullet beside "Secure passwordless access" was ornament standing in for
   meaning; the lock at least says the same thing the sentence does. */
.signin__secure svg { width: 12px; height: 12px; flex: none; opacity: .75; }

/* A refined focus ring on the field: the global 3px offset outline is right for
   dense interfaces and heavy on a single input in the middle of an empty page.
   Same colour, same visibility, drawn as a ring on the border instead. */
.signin__field .input:focus-visible {
  outline: none;
  border-color: var(--electric);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .18);
}
.signin__submit { transition: box-shadow 160ms ease, transform 160ms ease; }
.signin__submit:hover { box-shadow: 0 8px 18px -10px rgba(11, 26, 51, .5); }
.signin__note:empty { display: none; }
.signin__note {
  margin-top: 16px; font-size: 12px; color: var(--ink-2); line-height: 1.5;
  border-top: 1px solid var(--line); padding-top: 14px;
}

@media (max-width: 640px) {
  .signin { padding: 18px; }
  .signin__card { padding: 26px 22px 24px; }
  /* The corner marks are the first thing to go: at 320px they would sit on the
     card rather than around it. */
  .signin__corner { display: none; }
  /* On a phone the mark is wider than the screen, and that is the crop: it
     loses an equal sliver from each side rather than being scaled down into a
     stamp. `min(158vh, …)` already does it — nothing to override here. */
}

/* ------------------------------------------- call mode · readability ------ */
/* THE SCRIPT WAS NEARLY UNREADABLE WHEN THE DASHBOARD WAS IN DARK MODE, and the
   cause is one token borrowed from the wrong place.
 *
 * Call Mode's approved system is a soft-dark shell with a LIGHT, elevated
 * script surface — `--cm-surface: #f7f8fa`, declared once in `:root` and
 * deliberately never re-themed, because the script a producer reads aloud is
 * the same object under either dashboard theme.
 *
 * But those light cards set `color: var(--text)`, and `--text` is a GLOBAL
 * theme token: `#0b1a33` in light, `#e7edf9` in dark. So entering the Companion
 * from a dark dashboard produced near-white type on a near-white card. Every
 * secondary line was worse still — provenance, source lines, answer keys and
 * option labels all read `--text-muted`, `--text-faint` and `--border`, which
 * flip the same way.
 *
 * THE FIX IS SCOPED TO THE SURFACE, NOT TO THE APPLICATION. Nothing global
 * changes and no dark mode is disabled anywhere: these rules re-declare the ink
 * tokens LOCALLY on the three light cards inside Call Mode, so every descendant
 * that already asks for `var(--text)` resolves to the light-theme value it was
 * written for. One block instead of rewriting several dozen rules, and a card
 * that is added to Call Mode later inherits the correct behaviour by sitting
 * inside one of these. */
[data-theme="dark"] .script,
[data-theme="dark"] .postcall__card,
[data-theme="dark"] .cm__closedcard {
  --text: #0b1a33;
  --text-muted: #47546b;
  --text-faint: #5a657b;
  --ink: #0b1a33;
  --ink-2: #47546b;
  --ink-3: #5a657b;
  --border: rgba(17, 38, 74, .10);
  --border-strong: rgba(17, 38, 74, .16);
  --surface: #ffffff;
  --surface-sunken: #eaeef7;
  --canvas: #f4f6fb;
  --canvas-2: #eaeef7;

  /* AND THE ACCENTS THE CARD'S OWN CHILDREN READ. Pinning ink and surface got
     the card white with dark type, but every element INSIDE it still resolved
     the dark theme's accent family — so entering the Companion from a dark
     dashboard put a dark-navy notice slab, an electric-blue Continue and a
     washed-out selected answer inside a white card. Measured: the notice's
     background came back rgb(28,42,61) against a card of rgb(247,248,250).
     Readable, but not the approved surface.

     These are the light theme's own values (styles.css :root), re-declared on
     the same three light surfaces and nowhere else. No global token moves — the
     dashboard, the rails and the drawers stay dark, which is the point of the
     approved Call Mode: a focused soft-dark room around a light script. */
  --accent: #1d4e89;
  --accent-hover: #163d6d;
  --accent-soft: #e9eff7;
  --notice-ink: #21447f;
  --caution-soft: #fbf2e2;
  --danger-soft: #f8efed;
  --navy: #12305c;
  --navy-2: #0d2447;
  --electric: #2563eb;
  --wash: rgba(37, 99, 235, .10);
  --line: rgba(17, 38, 74, .10);
  --line-2: rgba(17, 38, 74, .16);
  color: var(--text);
}
/* The primary inside a light card is the light primary. styles.css:612 and
   design.css:742 both re-colour `.btn--primary` under `[data-theme="dark"]`,
   and those rules do not know they are inside a pinned surface. */
[data-theme="dark"] .script .btn--primary,
[data-theme="dark"] .postcall__card .btn--primary,
[data-theme="dark"] .cm__closedcard .btn--primary {
  background: var(--navy); border-color: transparent; color: #fff;
}
[data-theme="dark"] .script .btn--primary:hover,
[data-theme="dark"] .postcall__card .btn--primary:hover,
[data-theme="dark"] .cm__closedcard .btn--primary:hover { background: var(--navy-2); }

/* --------------------------------------------------------- call mode ------ */
/* THE LAUNCH CENTRE. Two entrances of equal weight, then the facts a producer
   about to dial actually reaches for.

   What this replaced: two generic white cards at the top of an otherwise empty
   viewport — "wack", correctly. The fix is not decoration. It is that the page
   now carries the counts, the approved scripts, whether the things a call needs
   are present, and the portals a call opens — every one of them a fact the
   product already held and was throwing away on this screen. */
.cl { display: flex; flex-direction: column; gap: 18px; }

/* The two entrances. Equal columns, because neither is the fallback: the
   founder is handed numbers that are not leads yet, so Quick Call is a first
   class door and is built to look like one. */
.cl__launch { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: stretch; }
.cl__card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 22px 24px 20px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--e1);
}
/* ACTIVE MEANS THERE IS SOMEONE TO CALL. The navy plate is the product's own
   primary surface, the same one Today's call card uses — it marks the path that
   is ready, and it stops being navy when it is not. */
.cl__card--active {
  background: linear-gradient(157deg, var(--navy) 0%, var(--navy-2) 100%);
  border-color: transparent; color: #eef3fb;
}
.cl__card--active .cl__eyebrow { color: #a9c4ec; }
.cl__card--active .cl__title, .cl__card--active .cl__n { color: #fff; }
.cl__card--active .cl__nl, .cl__card--active .cl__nextlabel { color: #9db8e2; }
.cl__card--active .cl__nextref { color: #eef3fb; }
.cl__card--active .btn--ghost {
  color: #dce7f8; background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .18);
}
.cl__card--active .btn--ghost:hover { background: rgba(255, 255, 255, .14); }

.cl__eyebrow {
  font-size: 10.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3);
}
.cl__title { font-size: 19px; line-height: 1.25; margin: 0; letter-spacing: -.01em; }
.cl__lede { font-size: 13px; color: var(--ink-2); line-height: 1.5; margin: 0; }
.cl__empty { font-size: 13px; color: var(--ink-2); line-height: 1.55; margin: 0; }
.cl__stats { display: flex; gap: 30px; }
.cl__n { font-size: 30px; font-weight: 680; line-height: 1; letter-spacing: -.02em; }
.cl__nl {
  margin-top: 4px; font-size: 10.5px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3);
}
.cl__next { display: flex; align-items: baseline; gap: 8px; }
.cl__nextlabel {
  font-size: 10.5px; font-weight: 650; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-3);
}
.cl__nextref { font-size: 14px; font-weight: 620; }
/* The actions sit at the bottom of both cards whatever is above them, so the
   two entrances line up rather than floating at different heights. */
.cl__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: auto; padding-top: 4px; }

/* The script's own name is the option; its provenance is underneath, in full. */
.cl__source { margin-top: 8px; display: flex; flex-direction: column; gap: 5px; }
.cl__meta { display: flex; flex-wrap: wrap; gap: 6px; }
.cl__tag {
  font-size: 10px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px;
  background: var(--canvas-2); color: var(--ink-2);
}
.cl__tag--ok { background: var(--teal-w); color: var(--teal); }
.cl__prov {
  font-size: 11.5px; color: var(--ink-3); line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis;
}

/* The supporting shelf reuses the wrapping flex row from Today, so the panels
   are content-proportional and every line fills rather than leaving a track. */
.cl__support > .panel { flex: 1 1 300px; }
.cl__row + .cl__row { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.cl__rowt { font-size: 13.5px; font-weight: 620; line-height: 1.3; }
.cl__rowm { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.cl__rowm--dim { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cl__check, .cl__reach {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 7px 8px; border-radius: 9px; font-size: 13px; color: var(--ink-2);
  background: transparent; border: 0;
}
.cl__check:hover, .cl__reach:hover { background: var(--canvas-2); color: var(--ink); }
.cl__checkmark {
  display: grid; place-items: center; flex: none; width: 18px; height: 18px;
  border-radius: 50%; background: var(--canvas-2); color: var(--ink-3);
}
.cl__checkmark svg { width: 11px; height: 11px; }
.cl__check.is-ok { color: var(--ink); }
.cl__check.is-ok .cl__checkmark { background: var(--teal-w); color: var(--teal); }
.cl__reach { justify-content: space-between; }
.cl__out { flex: none; font-size: 11px; color: var(--ink-3); }

@media (max-width: 900px) {
  /* One entrance at a time below this, because two 300px cards side by side is
     where the counts and the selector both start wrapping badly. */
  .cl__launch { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .cl__card { padding: 18px 16px 16px; }
  .cl__stats { gap: 22px; }
  .cl__actions .btn { flex: 1 1 100%; justify-content: center; }
}

/* ----------------------------------------------------------- provenance --- */
/* Every displayed lead field carries where it came from. A field with no source
   is labelled "Not supplied", never left blank — a blank and a failure to load
   look identical, and the producer must be able to tell them apart. */
.prov {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase;
  vertical-align: 1px;
}
.prov--imported      { background: var(--wash);      color: var(--electric); }
.prov--system        { background: var(--teal-w);    color: var(--teal); }
.prov--producer      { background: var(--violet-w);  color: var(--violet); }
.prov--not_supplied  { background: var(--canvas-2);  color: var(--ink-3); }

/* One cell per field, label stacked over value. Laying label and value out as
   separate grid items would let them wrap into different columns, which reads
   as one field's label against another field's value — the worst possible
   failure mode on a screen whose whole job is stating what is known. */
.fieldgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0 24px; }
.fieldgrid__f { padding: 10px 0; border-bottom: 1px solid var(--line); min-width: 0; }
.fieldgrid__k {
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 3px;
}
.fieldgrid__v { font-size: 14.5px; overflow-wrap: anywhere; }
.fieldgrid__v--masked { color: var(--ink-2); letter-spacing: .06em; }

/* --------------------------------------------------------- privacy mode --- */
.privacytoggle {
  display: inline-flex; align-items: center; gap: 7px;
  width: auto; padding: 0 12px; white-space: nowrap; font-size: 12.5px; font-weight: 600;
}
.privacytoggle svg { width: 15px; height: 15px; flex: none; }
.privacytoggle[aria-pressed="true"] { color: var(--violet); background: var(--violet-w); }

/* ------------------------------------------------------------- imports ----- */
.improws { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.improws th {
  text-align: left; font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); padding: 8px 10px;
  border-bottom: 1px solid var(--line-2); position: sticky; top: 0; background: var(--surface);
}
.improws td { padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
.improws tbody tr:hover { background: var(--canvas-2); }
.improws__scroll { max-height: 460px; overflow: auto; border-radius: var(--r); border: 1px solid var(--line); }

/* The outcome is a short verdict and must read as one line. Wrapped across two
   ("Will" / "create") it stops scanning as a status at a glance. */
.outcome { font-weight: 650; white-space: nowrap; }
.improws td:nth-child(2), .improws th:nth-child(2) { white-space: nowrap; width: 1%; }
.improws td:first-child, .improws th:first-child { width: 1%; white-space: nowrap; }
.outcome--created  { color: var(--emerald); }
.outcome--rejected { color: var(--rose); }
.outcome--duplicate { color: var(--amber); }
.outcome--review_required { color: var(--violet); }
.outcome--ignored { color: var(--ink-3); }

.dropzone {
  border: 2px dashed var(--line-2); border-radius: var(--r-lg); padding: 34px 24px;
  text-align: center; background: var(--canvas-2); cursor: pointer;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.dropzone:hover, .dropzone.is-over { border-color: var(--electric); background: var(--wash); }
.dropzone__t { font-weight: 650; font-size: 15px; margin-bottom: 4px; }
.dropzone__s { font-size: 12.5px; color: var(--ink-3); }

/* ---------------------------------------------------------- call prep ----- */
/* The approved Call Prep is THREE columns — script decision, merge-field
   readiness, action rail — laid out by `.prep__cols` in design.css. This file
   previously made `.prep` ITSELF a two-column grid, from the pre-port build.
   With the approved markup that turned `.prep__cols` into a 400px grid item,
   collapsing the script column to zero and pushing the rail off-screen.
   Only the sticky rail survives, which is the part design.css does not set. */
@media (min-width: 1100px) {
  .prep__rail { position: sticky; top: 14px; }
}

/* ------------------------------------------------------------ languages ---- */
.langseg { display: inline-flex; border: 1px solid var(--line-2); border-radius: var(--r-sm); overflow: hidden; }
.langseg button { padding: 5px 12px; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.langseg button[aria-pressed="true"] { background: var(--navy); color: #fff; }
[data-theme="dark"] .langseg button[aria-pressed="true"] { background: var(--electric); color: #06111f; }

/* ----------------------------------------------------- call companion ----- */
/* The approved system's `.cm__bar` is a progress bar and `.cm__script` is a
   rail pill — production needs a header toolbar and a full script column, so
   those get their own names rather than borrowing ones that mean something
   else. Everything below is layout only; the colours come from the system. */
.cmtop__left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cmtop__meta {
  font-size: 12px; color: var(--cm-text-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cmtop__actions { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }
.cmtop__status {
  padding: 6px 22px; font-size: 11.5px; letter-spacing: .04em;
  color: var(--cm-text-faint); border-bottom: 1px solid var(--cm-line);
}

/* Two columns when Ask Point is closed, three when it is open. The approved
   system reserves the right column unconditionally, which leaves a dark gutter
   beside the script whenever the panel is not showing. */
.cm__body--norail { grid-template-columns: 250px minmax(0, 1fr); }

.cm .langseg { border-color: var(--cm-line); }
.cm .langseg button { color: var(--cm-text-faint); }
.cm .langseg button[aria-pressed="true"] { background: var(--cm-accent); color: #16202c; }

/* A call that cannot be opened gets its own centred screen. Reusing `.cm__body`
   dropped the message into the 250px journey rail, where it wrapped one word
   per line. */
.cm__closed { flex: 1; display: grid; place-items: center; padding: 24px; }
.cm__closedcard {
  width: min(560px, 100%); background: var(--cm-surface); color: var(--text);
  border-radius: 14px; box-shadow: var(--shadow-lg); padding: 28px 30px;
}
.cm__closedcard h2 { font-size: 21px; font-weight: 650; letter-spacing: -.02em; }
.cm__closedcard .mutedline { color: var(--text-muted); margin-top: 8px; }
.cm__closedacts { display: flex; gap: 10px; margin-top: 20px; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

/* The drawer and the Ask Point panel are LIGHT panels that open over the
   soft-dark call shell. Being descendants of `.cm` they would otherwise inherit
   Call Mode's light-on-dark text colour and render nearly invisible on their own
   white surfaces — grey #c9cdd3 on white is about 1.5:1, which is unreadable.
   The Ask Point case is the one that matters most: the text it renders is
   script wording the producer is about to read aloud. */
.cm .drawer,
.cm .cp { color: var(--ink); }
.cm .drawer .rail-label,
.cm .drawer .mutedline,
.cm .cp .rail-label,
.cm .cp .mutedline,
.cm .cp .ap__empty,
.cm .cp .ap__cite { color: var(--ink-2); }
.cm .cp .ap__part { color: var(--ink-3); }
.cm .cp .ap__part--do_not_say { color: var(--rose); }
.cm .cp .ap__verbatim { color: var(--ink); background: var(--surface); }
.cm .cp .cpa__t { color: var(--ink); }

/* The docked panel is a fixed 400px column on the right (design.css .cp), and
   Call Mode must give up exactly that much width WHILE IT IS OPEN or the panel
   sits on top of the script the producer is reading.

   ONLY WHILE IT IS OPEN. This rule had no condition on it, so the right column
   was 400px permanently — for a panel that is closed almost all of the time.
   The approved rail is 268px (styles.css), so every call ran with 132px of the
   script's width handed to nothing, and that is a large part of why the founder
   said production "still feels different" beside the prototype: the rail reads
   heavy and the script column is squeezed. Measured at 1440, the prototype
   gives the script 922px; production was giving it 790.

   The dock still gets its exact 400 the moment it opens, and the transition is
   a grid change on one element rather than a re-render. */
.cm.is-asking .cm__body { grid-template-columns: 250px minmax(0, 1fr) 400px; }

/* THE PRESS IS VISIBLE BEFORE THE SERVER IS. A transition is one authoritative
   round trip and the founder measured ~1s of it on the deployed build; what a
   producer must never experience in that second is a control that looks
   unpressed. The pressed control keeps its full weight and shows a progress
   cursor, the rest of the card quiets and stops taking input, and NOTHING
   moves — no spinner is inserted, no height changes, no content is predicted.
   When the server answers, the real position replaces this. */
.script.is-working { pointer-events: none; }
.script.is-working .answer,
.script.is-working .script__nav .btn { opacity: .55; transition: opacity .12s ease; }
.script.is-working [aria-busy="true"] { opacity: 1; cursor: progress; }
.script.is-working .answer[aria-busy="true"] {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.script.is-working .answer[aria-busy="true"] .answer__dest { color: rgba(255, 255, 255, .8); }
.script.is-working .answer[aria-busy="true"] .answer__key {
  color: #dce7f8; border-color: rgba(255, 255, 255, .45);
}

/* AND IT MUST STOP APPLYING WHERE THE RAILS DO NOT EXIST. styles.css collapses
   `.cm__body` to a single column at 1023px, where both rails are hidden — but
   this file loads after it, so the three-column rule above won overrode that
   at EVERY width. At 375px the grid was still reserving 250px and 400px for
   columns that render nothing, leaving the script card 190px of a 375px screen.
   Pre-existing, and unchanged at any width where the approved cockpit is
   actually on screen: this only restates the collapse the approved stylesheet
   already asked for. */
@media (max-width: 1023px) {
  .cm__body { grid-template-columns: minmax(0, 1fr); }
}
.cm__rail--right { background: transparent; border-left: none; }
.drawer__listtitle { font-size: 14.5px; font-weight: 600; margin-bottom: 3px; }
.drawer__listitem .drawer__sectionlabel { margin-bottom: 0; }
/* A chosen objection is separated from the list it came out of. */
.drawer__section:not(:empty) { border-top: 1px solid var(--border); padding-top: 18px; }

/* ------------------------------------------------------------ ask point ---- */
.ap__empty { font-size: 13px; color: var(--ink-3); line-height: 1.6; }
.ap__refusal {
  border-left: 3px solid var(--amber); background: var(--amber-w);
  padding: 12px 14px; border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 13.5px; line-height: 1.6; color: var(--ink);
}
.ap__verbatim {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 12px 14px; font-size: 14px; line-height: 1.6; white-space: pre-wrap;
}
.ap__part {
  font-size: 10.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-3); margin: 12px 0 5px;
}
.ap__part--do_not_say { color: var(--rose); }
.ap__cite { font-size: 11.5px; color: var(--ink-3); margin-top: 8px; }

/* --------------------------------------------------------------- misc ----- */
/* Rows and cards that happen to be links are still rows and cards. The browser
   default underline turns a lead card into a wall of blue rules. */
a.rowlist__row, a.leadcard, a.nav__item, a.brand, a.btn { text-decoration: none; color: inherit; }
a.rowlist__row:hover .rowlist__title, a.leadcard:hover .leadcard__name { color: var(--accent); }

/* THERE ARE NO INLINE STYLES IN THIS CLIENT.
   The Content-Security-Policy is `style-src 'self'` with no 'unsafe-inline',
   so a `style` attribute is silently ignored by the browser — the rule holds
   whether or not anyone remembers it. Everything cosmetic gets a class. */
.mutedline--footnote { margin-top: 18px; }
.chip--inline { margin-left: 10px; }

.checkline { display: flex; align-items: center; gap: 9px; font-size: 14.5px; cursor: pointer; }
.checkline input { width: 17px; height: 17px; flex: none; accent-color: var(--accent); }

.stackgap > * + * { margin-top: 14px; }
.stackgap > [hidden] { display: none; }
.mutedline { font-size: 12.5px; color: var(--ink-3); line-height: 1.6; }
.errline { font-size: 13px; color: var(--rose); }

/* ===================================================== the approved shell ===
   The command bar is the only H1 in the workspace, which is why design.css
   demotes in-page headers to 17px. Restoring `.cmd` restores the hierarchy on
   every screen at once. These are layout gaps only — every visual token comes
   from the approved system. */
.appcol { min-width: 0; display: flex; flex-direction: column; }

/* The search result list the approved header opens under its input. */
.searchpop__r {
  display: grid; grid-template-columns: 70px 1fr auto; gap: 10px; align-items: baseline;
  width: 100%; text-align: left; padding: 9px 12px; font-size: 13.5px;
  border-radius: var(--r-sm);
}
.searchpop__r:hover { background: var(--wash); }
/* THE COMBOBOX BORROWS THE POPOVER SKIN, NOT ITS GRID. The 70px first track is
   the command bar's group badge (`.searchpop__g`). A combobox row emits only a
   label and a secondary value, so the LABEL fell into the badge track and was
   ellipsised at 70px — the timezone picker read "Eastern ...", "Mountai...",
   with 254px given to the raw IANA id beside it. Two tracks here, sized by
   content, and the command bar is untouched. */
.cbx .searchpop__r { grid-template-columns: minmax(0, 1fr) auto; }
.searchpop__g {
  font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3);
}
.searchpop__t { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.searchpop__s { font-size: 11.5px; color: var(--ink-3); }

/* The identity cluster's initials mark, in place of the prototype's photo. */
.cmd .avatar {
  display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;
  background: linear-gradient(150deg, var(--electric), var(--navy) 78%); color: #fff;
  font-size: 12.5px; font-weight: 650; letter-spacing: .02em; flex: none;
}

/* An approved destination that is not yet ported says so plainly. */
.pempty__i { width: 34px; height: 34px; color: var(--ink-3); margin-bottom: 10px; }
.pempty__i svg { width: 34px; height: 34px; }

/* Source blocks the approved script carries, inside the script card. */
.srcblock__ifyes {
  display: block; font-size: 13px; color: var(--text-muted); margin-top: 3px; padding-left: 14px;
}
.srcblock__setkey {
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted); margin: 10px 0 4px;
}

/* --------------------------------------------------------------- leads ----
   The approved board and filter bar are styled in styles.css, adopted verbatim
   from the prototype. These five rules exist ONLY because the prototype set
   their equivalents with inline `style` attributes, which `style-src 'self'`
   refuses. Same measurements, expressed as classes.

   (Where a value is genuinely computed per element — a histogram bar's height —
   it goes through the CSSOM instead; see el() in web/lib/dom.js.) */

/* prototype/app.js:1780 — sel(): style="width:auto;min-width:130px" */
/* `select.input` in styles.css sets width:100%, so this must match on the
   element too — a class alone loses the cascade and every filter stacks. */
select.filters__sel { width: auto; min-width: 130px; }
/* prototype/app.js:1786 — search input: style="width:190px" */
input.filters__q { width: 190px; }
/* prototype/app.js:1795 — the trailing hint: style="margin-left:auto" */
.filters__hint { margin-left: auto; }

/* prototype/app.js:1824 — tray rows:
   style="justify-content:space-between;padding:8px 0;border-top:1px solid var(--border)" */
.tray__row {
  display: flex; align-items: center; gap: 10px;
  justify-content: space-between; padding: 8px 0; border-top: 1px solid var(--border);
}

/* prototype/app.js:1897 — the paired required fields: style="flex:1" on each
   field, style="gap:12px" on the row that holds them. */
.modal__row { gap: 12px; }
.field--grow { flex: 1; }
/* A currency figure and a datetime do not need 572px of modal. */
.field--sized { max-width: 260px; }
/* prototype/app.js:1896 — explanatory paragraphs inside a modal:
   style="font-size:14px" */
.modal__p { font-size: 14px; }

/* The board is 8 columns of 268px and scrolls horizontally on purpose
   (`.board { overflow-x: auto }`, styles.css). Without this, `.page` — a flex
   item with the default `min-width: auto` — sizes itself to that content
   instead, so the whole workspace scrolls sideways and the board never
   scrolls at all. `min-width: 0` lets the overflow land where it was designed
   to land. No measurement changes; the board is the only thing that moves. */
/* `.page` carries `margin: 0 auto` (styles.css) for centring. In a column
   flex container an auto cross-axis margin DISABLES stretch, so the page sized
   itself to its max-content — the 8-column board — and capped at its own
   max-width instead of its parent's. `width: 100%` restores the intended size
   while keeping the centring, and `min-width: 0` lets the board's own
   `overflow-x: auto` take the overflow. No measurement changes. */
.appcol > .page { width: 100%; min-width: 0; }

/* ---------------------------------------------------- lead workspace ------
   Three inline styles from viewLeadWorkspace (prototype/app.js:2062), which
   `style-src 'self'` refuses. Same measurements, expressed as classes. */

/* prototype/app.js:2100 — the facts row: style="gap:26px" */
.lw-facts { gap: 26px; margin-top: 16px; }
/* prototype/app.js:2101 — a single figure: style="font-size:16px;font-weight:600" */
.lw-fact { font-size: 16px; font-weight: 600; }
/* prototype/app.js:2119 — objection rows are readouts, not links */
.lw-static { cursor: default; }
/* prototype/app.js:2131 — application rows: style="padding:10px 0;border-top:..." */
.lw-app { padding: 10px 0; border-top: 1px solid var(--border); }
.lw-app__top { justify-content: space-between; }

/* ------------------------------------------------------------ call prep ---
   Inline styles from viewPrepare (prototype/app.js:2156) that `style-src
   'self'` refuses, expressed as classes at the same measurements. */

/* prototype/app.js:2172 — style="gap:8px;align-items:center" */
.prep__eyebrowrow { gap: 8px; align-items: center; }
/* prototype/app.js:2189 — style="max-width:200px" */
.prep__inboundnote { max-width: 200px; }
/* prototype/app.js:2306 — style="margin-top:10px" / :14px on rail notes */
.prep__railnote { margin-top: 14px; }
/* prototype/app.js:2318 — style="margin-top:10px" on the plan panel */
.prep__planpanel { margin-top: 10px; }
/* prototype/app.js:2319 — style="padding-top:14px" */
.prep__planbd { padding-top: 14px; }
/* prototype/app.js:2323 — style="min-width:0" so long values ellipsize */
.prep__planmain { min-width: 0; }
/* prototype/app.js:2274 — style="gap:10px;align-items:baseline" */
.prep__grouphead { gap: 10px; align-items: baseline; }

/* ------------------------------------------------------------ Ask Point ---
   The capsule and panel live in their own roots outside the view tree, so a
   re-render never disturbs them. Their appearance comes from the approved
   system (`.capsule`, `.cp` in design.css); these rules only give the two roots
   somewhere to be, and style the answer blocks the production panel renders
   that the prototype's synthetic one did not have. */
#askpoint-capsule, #askpoint-panel { position: relative; z-index: 70; }

.cp__block { margin-top: 10px; }
.cp__blocklabel {
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 3px;
}
/* A do-not-say guardrail is not wording to read aloud, and must never be
   mistaken for it. */
.cp__block--warn {
  border-left: 3px solid var(--rose, #be123c); padding-left: 10px;
}
.cp__block--warn .cp__blocklabel { color: var(--rose, #be123c); }
.cp__a--refusal .cp__atext { color: var(--ink-2); }
/* A DOCUMENT provenance line — version, language, section, passage ordinal.
   It had no rule at all, so the one place Ask Point states where an answer came
   from was set in body copy indistinguishable from the answer itself. */
.cp__cite {
  font-size: 11px; color: var(--ink-3); line-height: 1.45; margin-top: 6px;
  display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline;
}
.cp__cites { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }

/* ------------------------------------------------------- import wizard ----
   The approved wizard's source row (prototype/app.js:2018). The two source
   buttons sit side by side; the Google Sheet one is present and disabled
   because no Sheets connection is built or authorized, and deleting an
   approved control is not the same as saying so. */
.wiz__sources { gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.wiz__sources .btn[disabled] { opacity: .62; cursor: default; }

/* --------------------------------------------------------- post-call ------
   Two inline styles from renderPostCall (prototype/app.js:2977, :2984),
   expressed as classes at the same measurements. */
.postcall__pair { gap: 14px; align-items: flex-start; }
.postcall__save { flex: 1; }

/* ------------------------------------------- applications & follow-ups ----
   Inline styles from viewApplications (prototype/app.js:3115) and
   viewFollowUps (:3055), which `style-src 'self'` refuses. Same measurements,
   expressed as classes. */

/* prototype/app.js:3123, :3148 — style="justify-content:space-between" */
.wq-app__top { justify-content: space-between; }
/* prototype/app.js:3124 — style="font-size:16px" on the lead name */
.wq-app__lead { font-size: 16px; }
/* prototype/app.js:3128 — style="gap:26px" on the date row */
.wq-app__dates { gap: 26px; flex-wrap: wrap; }
/* prototype/app.js:3134 — style="gap:14px;align-items:stretch" */
.wq-app__money { gap: 14px; align-items: stretch; flex-wrap: wrap; }
/* prototype/app.js:3135 — the premium block:
   style="flex:1;background:var(--surface-sunken);border-radius:7px;
          padding:11px 14px;display:flex;gap:26px;flex-wrap:wrap" */
.wq-premium {
  flex: 1; min-width: 260px; background: var(--surface-sunken); border-radius: 7px;
  padding: 11px 14px; display: flex; gap: 26px; flex-wrap: wrap;
}
/* prototype/app.js:3140 — style="flex:0 0 250px" */
/* Was `flex: 0 0 250px`, which could neither grow nor shrink, so every spare
   pixel in the money row was dumped into `.wq-premium` — half a sunken panel of
   nothing at full width, and a dropped line inside a two-up card. */
.wq-app__money .estimatebox { flex: 1 1 220px; }
/* prototype/app.js:3145 — style="font-size:17px;font-weight:640;margin-top:4px" */
.wq-estimate__n { font-size: 17px; font-weight: 640; margin-top: 4px; }
/* prototype/app.js:3150 — style="gap:8px" on the action row */
.wq-app__actions { gap: 8px; }
/* prototype/app.js:3169 — kv value: style="font-size:14.5px;font-weight:560" */
.wq-kv__v { font-size: 14.5px; font-weight: 560; }
/* prototype/app.js:3078 — style="gap:6px" on the follow-up action group */
.wq-follow__actions { gap: 6px; }
/* prototype/app.js:3070, :3160 — style="cursor:default" on readout rows */
.wq-static { cursor: default; }

/* --------------------------------------------------- licensed-state capsule
   Selecting a state on the map replaces the panel's hint IN PLACE with
   everything recorded for that state. Replacing rather than appending means
   the panel never grows or jumps under the producer's cursor.

   READ-ONLY by founder decision. Editing licensing is a scoped post-Gate-5E
   enhancement — see docs/gate5/14. */
.stcap__slot { margin-top: 10px; }
.stcap {
  border: 1px solid var(--line-2, var(--border));
  border-radius: var(--r-sm, 8px);
  background: var(--canvas-2);
  padding: 11px 13px;
}
.stcap__hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 9px; }
.stcap__name { font-size: 14px; font-weight: 640; letter-spacing: -.01em; }
.stcap__rows { display: flex; flex-direction: column; gap: 4px; }
.stcap__row { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; }
.stcap__k {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-faint); flex: none;
}
.stcap__v { font-size: 12.5px; text-align: right; min-width: 0; }
/* An absent value is styled as absent, so "Not supplied" never reads as data. */
.stcap__v--absent { color: var(--text-faint); font-style: italic; }
.stcap__sec { margin-top: 11px; padding-top: 9px; border-top: 1px solid var(--border); }
.stcap__appts { display: flex; flex-direction: column; gap: 6px; margin-top: 5px; }
.stcap__appt { display: flex; flex-direction: column; }
.stcap__apptp { font-size: 12.5px; font-weight: 560; }
.stcap__apptm { font-size: 11.5px; color: var(--text-faint); }
.stcap__empty { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; line-height: 1.5; }
.stcap__foot {
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-faint); line-height: 1.5;
}
.stcap__close { margin-top: 8px; }

/* ------------------------------------------- call prep, small script library
   Founder decision 2026-08-04. The approved three-column layout is kept; only
   the PROPORTIONS respond to how many scripts are actually eligible. With a
   full library the approved widths are restored by .prep__cols--full below. */
.prep__cols--sparse {
  grid-template-columns: minmax(0, .78fr) minmax(300px, 1.25fr) minmax(260px, .85fr);
}

/* ------------------------------------------------ Gate 5F-3, Knowledge review
   The ONE class this gate adds. Everything else on the Knowledge surface —
   page-head, card, rowlist, chip, notice, field, btn — is the approved system
   used exactly as the other views use it.

   This exists because the product has never had to show a document's exact
   text before. It is deliberately plain: a reviewer is deciding whether the
   WORDING is right, so the surface must not restyle, reflow or prettify it.
   `white-space: pre-wrap` keeps every space and line break the extractor
   preserved, and long lines wrap instead of scrolling the page sideways. */
.knowledge-extract {
  /* `flex: none` and a min-height because the modal body is a flex column:
     without them this <pre> is treated as a shrinkable flex item and collapses
     to a single 26px line while its content is 274px tall — which is exactly
     what it did, showing the reviewer one line of the document they are being
     asked to approve. */
  flex: none;
  margin: 8px 0 0;
  padding: 12px 14px;
  min-height: 9lh;
  max-height: 44vh;
  overflow: auto;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.6;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  tab-size: 4;
}

/* ------------------------------------------------- Gate 5F-4: library search
   Two classes, for the two shapes the approved system does not already have:
   a search row whose controls stay on one line until the viewport is narrow,
   and a result that stacks its citation above its verbatim text.

   Spacing is written in pixels, as everywhere else in this file — the approved
   system has no spacing scale, and inventing one here would be a design change
   smuggled in as a layout fix. No colour and no type size is introduced. */

/* Bounded at the ROW, not at the card, so the card stack keeps one right edge
   while the query field stops growing to 670px. */
.libsearch { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; max-width: 720px; }
.libsearch .input { flex: 1 1 16rem; min-width: 0; }
.libsearch__mode, .libsearch__lang { flex: 0 0 auto; width: auto; }

/* A result is a column: citation line, verbatim label, then the wording. The
   row default is a horizontal strip, which would set a paragraph of document
   text beside its own heading. */
.libresult { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }

/* The verbatim chip hugs its text rather than stretching across the row. */
.libresult > .chip { align-self: flex-start; }

/* A cited passage is usually a paragraph, not a whole page, so it does not need
   the tall minimum the full-document viewer uses. */
/* A measure, because this is script wording a producer reads aloud. At full
   width it set roughly 150 monospace characters to the line. */
.libresult .knowledge-extract { min-height: 0; width: 100%; max-width: 88ch; }

/* -------------------------------------------------- Gate 5F-4: what next
   Groups of queued work, each with its label and its rows. Layout only —
   colours, chips and rows are the approved system's. */

.nextup__group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.nextup__group:last-child { margin-bottom: 0; }

.nextup__label {
  display: flex; align-items: center; gap: 8px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3);
}

/* The rows are links, so they need the row's own look rather than a link's. */
.nextup__row { text-decoration: none; color: inherit; }
.nextup__row:hover { background: var(--surface-sunken); }

/* ------------------------------- Gate 5F-4 revision: objection drawer back
   The detail used to render below the whole list, so opening an objection
   scrolled the answer off-screen. List and detail are now separate views and
   this is the way back. */
.drawer__back { align-self: flex-start; margin-bottom: 10px; }

/* ================================================== Gate 6: bounded widths ==
   The founder's standing requirement: nothing stretches across the viewport
   merely because the space exists. Width follows purpose —

     .page--form     a settings form reads best narrow
     .page--split    a working surface: primary workspace + supporting panel
     .page--reading  documents and previews at a comfortable measure
     .page--wide     (existing) call mode and tables that genuinely need width

   Today keeps its own approved dashboard grid and is none of these. */

.page--form { max-width: 720px; }
.page--reading { max-width: 880px; }

/* `margin-inline: auto` because the cap without it is a LEFT-aligned column:
   at a 1504px content box the 1240px split sat hard left with 264px of ragged
   nothing down its right side, which is the Imports screenshot. */
.page--split { display: grid; grid-template-columns: minmax(0, 1fr) 340px;
  gap: 20px; align-items: start; max-width: 1240px; margin-inline: auto; }
.page--split > .split__side { position: sticky; top: 76px; display: flex;
  flex-direction: column; gap: 14px; }
@media (max-width: 980px) {
  .page--split { grid-template-columns: minmax(0, 1fr); }
  .page--split > .split__side { position: static; }
}

/* Compact stacked forms for the new surfaces. Labels above inputs, content-
   sized controls, no full-width rows holding six words. */
.fform { display: flex; flex-direction: column; gap: 12px; }
.fform .frow, .fform__row { display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
/* `:not(.checkline)` because a checkbox row is a LABEL too, and the stacked
   form-field treatment turned "Start each session in Privacy Mode" into a
   centred column with the box above its own words. `.fform label` is (0,1,1)
   and `.checkline` is (0,1,0), so the form always won. */
.fform label:not(.checkline) { display: flex; flex-direction: column; gap: 4px;
  font-size: 12px; font-weight: 600; color: var(--ink-2); }
.fform input, .fform select, .fform textarea {
  font: inherit; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel); color: var(--ink-1);
  min-width: 0; }
.fform textarea { min-height: 72px; resize: vertical; }
.fform .factions { display: flex; gap: 10px; align-items: center; }
/* A BUTTON IS NOT A FIELD. `.fform` is a stretch column, which blockifies an
   inline-flex `.btn` into a full-card-width slab carrying one word. */
.fform > .btn { align-self: flex-start; }
/* THE EQUAL-FRACTION ROW, CORRECTED WHERE IT LIES ABOUT THE CONTENT. A
   two-letter state code and a date picker are not the same width, and the
   equal-thirds grid made them so. Same numbers as `.bz__range`, which fixed
   this on Business first. */
.fform__row--fit { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.fform__row--fit .field { flex: 0 1 190px; min-width: min(150px, 100%); }
.fform__row--fit .field--sm { flex: 0 0 104px; }

/* Record lists shared by tools, training, licensing, appointments. */
.rlist { display: flex; flex-direction: column; gap: 8px; }
.rlist__item { display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); }
.rlist__main { flex: 1 1 auto; min-width: 0; }
.rlist__t { font-weight: 600; }
.rlist__s { font-size: 12px; color: var(--ink-3); }
.rlist__acts { display: flex; gap: 6px; flex: 0 0 auto; }

/* Metric cards for Business — number, then its definition, honestly. */
.mgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px; }
.mcard { border: 1px solid var(--line); border-radius: 12px; background: var(--panel);
  padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.mcard__v { font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.mcard__na { color: var(--ink-3); font-size: 18px; font-weight: 600; }
.mcard__l { font-size: 12px; font-weight: 600; color: var(--ink-2); }
.mcard__d { font-size: 11px; color: var(--ink-3);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; }

/* An input that must exist for the browser but never for the eye — the file
   picker behind the Upload document button. Not display:none, which some
   browsers treat as unclickable for programmatic .click(). */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Gate 6A — the searchable combobox reuses the command bar's popover skin,
   anchored to its own field instead of the bar. */
.cbx { position: relative; display: block; }
.cbx .searchpop { position: absolute; left: 0; right: 0; top: calc(100% + 4px);
  max-height: 300px; overflow-y: auto; z-index: 60; }
.cbx__opt { cursor: pointer; }
.cbx__opt.is-active { background: var(--panel-2, rgba(127,127,127,.12)); }

/* Gate 6A — a Home jump-panel is an mcard that acts like a door. */
button.mcard--go { cursor: pointer; text-align: left; font: inherit;
  color: inherit; width: 100%; }
button.mcard--go:focus-visible { outline: 2px solid var(--accent, #2563eb);
  outline-offset: 2px; }

/* Gate 6A — the Training tab rail. Producer-created tabs make the row
   unbounded, so overflow is contained inside the rail itself: the tabs zone
   scrolls horizontally with edge affordances, and the Add/Manage actions are
   pinned outside the scrolling region so they can never leave the screen. */
/* SIZED BY ITS TABS. Three tabs in a 1140px tinted pill is 820px of empty
   strip - the widest element on the page carrying the least. `.ptabs` already
   sets overflow-x, so `max-width: 100%` keeps a phone scrolling rather than
   overflowing. NOT applied to `.ptabs--rail`, which is the founder-approved
   full-width Training rail and must keep filling. */
.ptabs--fit { width: max-content; max-width: 100%; }
.ptabs--rail { display: flex; align-items: center; gap: 4px; overflow: hidden; }
.ptabs__scroll {
  display: flex; align-items: center; gap: 4px; flex: 1 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.ptabs__scroll::-webkit-scrollbar { display: none; }
.ptabs__scroll .ptab { flex: 0 0 auto; white-space: nowrap; }
.ptabs__actions { display: flex; align-items: center; gap: 4px; flex: 0 0 auto;
  padding-left: 6px; border-left: 1px solid var(--line); }
.ptabs__edge {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink-2);
  background: var(--canvas-2); border: 1px solid var(--line);
}
.ptabs__edge:hover { color: var(--ink-1); background: var(--wash); }
.ptabs__edge:focus-visible { outline: 2px solid var(--electric); outline-offset: 1px; }
.ptabs__edge--left svg { transform: rotate(90deg); }
.ptabs__edge--right svg { transform: rotate(-90deg); }
.ptabs__edge[hidden] { display: none; }

/* The drawer's close control reads as a control: the product's own glyph beside
   its word, the way the approved prototype labelled it ("\u2715 Close"). An
   unlabelled 12px glyph is not a target a producer finds while talking. */
.drawer__close { display: inline-flex; align-items: center; gap: 6px; }
.drawer__close svg { width: 13px; height: 13px; }

/* THE CHOSEN ANSWER'S DESTINATION LINE, ON THE CHOSEN ANSWER'S BACKGROUND.
   The approved stylesheet carries `.answer.is-picked .answer__dest { color:
   rgba(255,255,255,.8) }` (styles.css:814) — but production marks the choice
   with `aria-pressed="true"`, the accessible form, so that rule has never
   matched anything. The line kept `--text-muted` on a navy fill: measured 1.1:1
   in both themes, which is not dim, it is gone. The producer could see WHICH
   response they picked and not what the source does with it.

   aria-pressed rather than a second class, so the state a screen reader hears
   and the state the eye sees stay the same fact. */
.answer[aria-pressed="true"] .answer__dest { color: rgba(255, 255, 255, .82); }

/* ===================================== Training · the video's own thumbnail ==
   The cached image fills the poster box the card already had. Nothing about the
   card's geometry moves: same tile, same aspect, same height, so a grid of
   cards with and without thumbnails stays even.

   `object-fit: cover` is what keeps it even — providers return 16:9, 4:3 and
   the occasional square, and letting those set the box would give one row three
   different heights. The image is cropped to the box instead.

   The play control, the provider chip and the state labels all sit ABOVE it and
   are unchanged; a scrim goes between so white text stays legible over a bright
   frame. Measured against the brightest thumbnail in the founder's library. */
.vcard__thumb {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  /* Behind the mesh, the chip, the play button and the labels. */
  z-index: 0;
}
/* A restrained gradient, heavier at the corners the labels occupy. Without it a
   bright frame turns "YOUTUBE", "Plays here" and "Not started" into noise. */
.vcard__thumb + .vcard__mesh {
  background-image:
    linear-gradient(180deg, rgba(6, 14, 30, .55) 0%, rgba(6, 14, 30, .12) 32%,
      rgba(6, 14, 30, .18) 62%, rgba(6, 14, 30, .62) 100%),
    repeating-linear-gradient(62deg, rgba(255, 255, 255, .05) 0 1px,
      rgba(255, 255, 255, 0) 1px 11px);
  opacity: 1;
}
/* The play control reads as glass over a photograph rather than a white disc
   on a flat colour. */
.vcard__thumb ~ .vcard__play {
  background: rgba(255, 255, 255, .92);
  box-shadow: 0 10px 26px -8px rgba(0, 0, 0, .7);
}

/* ================================ Training · the producer's own thumbnail ==
   A picture for a card the automatic path cannot help — a PDF, a portal, an
   agency page. It sits in the resource's edit form beside a preview at the
   card's own aspect, so what the producer picks is what they will see.

   The preview keeps 16:9 whether or not there is an image in it, so choosing a
   file does not make the dialog jump. */
.thumbpick { display: flex; gap: 14px; align-items: flex-start; }
.thumbpick__preview {
  flex: none; width: 168px; aspect-ratio: 16 / 9;
  border-radius: 10px; overflow: hidden;
  background: var(--canvas-2); border: 1px solid var(--line);
  display: grid; place-items: center;
}
.thumbpick__preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumbpick__empty {
  font-size: 11px; line-height: 1.4; color: var(--ink-3);
  text-align: center; padding: 0 10px;
}
.thumbpick__side { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.thumbpick__file { font-size: 12.5px; color: var(--ink-2); max-width: 100%; }
.thumbpick__note { font-size: 11.5px; line-height: 1.45; color: var(--ink-3); }
/* The Remove control belongs to the producer's own image and is hidden for a
   fetched one — a picture the product fetched is not theirs to delete, it is
   replaced by uploading over it or by changing the link. */
.thumbpick__side .btn { align-self: flex-start; }
@media (max-width: 520px) {
  .thumbpick { flex-direction: column; }
  .thumbpick__preview { width: 100%; }
}

/* ============================================ a destructive button looks it ==
   `.btn--danger` is defined in styles.css, but design.css redefines `.btn`
   AFTER it at the same specificity, so the danger colour lost and every
   destructive confirm rendered as an ordinary button. Two of them: rolling back
   an import (imports.js) and clearing the Business Snapshot's activity
   (today.js). A dialog whose confirm looks exactly like its cancel is a dialog
   that gets clicked through.

   Restated here rather than edited into design.css, which is adopted verbatim
   from the approved prototype — production-only differences live in this file
   so the drift stays visible in one place. */
.btn--danger,
.btn.btn--danger { border-color: var(--danger); color: var(--danger); background: var(--surface); }
.btn--danger:hover,
.btn.btn--danger:hover { background: var(--danger-soft); border-color: var(--danger); }
[data-theme="dark"] .btn--danger,
[data-theme="dark"] .btn.btn--danger { background: transparent; }

/* ==================================================== Lead Protection ======
   Protection built INTO the workspace, not a second application. Small chips,
   one warning strip, a compact review card. The only colour that raises its
   voice is the window with under a day left and work still on it — everything
   else is ordinary text, because a screen that is always urgent is a screen
   nobody reads. */
.lp { display: flex; flex-direction: column; gap: 12px; }

.lp__card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  padding: 14px 16px; box-shadow: var(--e1);
  display: flex; flex-direction: column; gap: 10px;
}
.lp__head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; }
.lp__vendor { font-size: 14.5px; font-weight: 640; letter-spacing: -.012em; }
.lp__meta { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.lp__winbox { text-align: right; flex: none; }
.lp__winlabel {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3);
}
.lp__win { font-size: 14px; font-weight: 640; font-variant-numeric: tabular-nums; }
/* Under a day, with work outstanding. This is the one thing on the page that
   is allowed to shout, and it earns it: after this the money is gone. */
.lp__win--soon { color: var(--amber-ink); }
.lp__winmuted { font-size: 12.5px; color: var(--ink-3); }

.lp__counts { display: flex; gap: 22px; flex-wrap: wrap; }
.lp__count { display: flex; flex-direction: column; gap: 1px; }
.lp__countn { font-size: 18px; font-weight: 660; font-variant-numeric: tabular-nums; }
.lp__count--live .lp__countn { color: var(--amber-ink); }
.lp__countl { font-size: 11px; color: var(--ink-3); }

.lp__reasons { display: flex; gap: 6px; flex-wrap: wrap; }
.lp__cap { font-size: 11.5px; color: var(--ink-3); }
.lp__acts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* The retroactive lead-age control sits with the card's other actions rather
   than in a panel of its own: it is one bounded correction, not a form. Bounded
   width for the same reason every working surface here is — a select that
   stretches to the card edge would read as the card's subject.

   `select.lp__agesel`, NOT `.lp__agesel`, and that is the whole reason this
   comment is longer than the rule. styles.css:193 is
   `.input, select.input, textarea.input { width: 100%; font-size: 14.5px;
   padding: 9px 12px; min-height: 44px }`. `select.input` has specificity
   (0,1,1); a bare class has (0,1,0) and loses no matter which file loads last,
   so four of the five declarations below would simply be discarded and the
   control would render full-bleed at 44px. Same trap, same fix, as
   `select.filters__sel` above. */
.lp__ageedit { display: flex; gap: 6px; align-items: center; margin-left: auto; }
select.lp__agesel {
  width: auto; min-width: 150px; max-width: 220px;
  font-size: 12px; padding: 5px 8px; min-height: 32px;
}

.lp__flags { display: flex; flex-direction: column; gap: 8px; }
.lp__flags:empty { display: none; }
.lp__flag {
  display: flex; justify-content: space-between; gap: 14px; align-items: flex-start;
  padding: 10px 12px; border-radius: 9px; background: var(--canvas-2);
  border: 1px solid var(--line);
}
.lp__flag--done { opacity: .6; }
.lp__flagmain { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.lp__flagtop { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lp__flagreason { font-size: 13px; font-weight: 600; }
.lp__flagref { font-size: 11.5px; color: var(--ink-2); }
.lp__flagev { font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }
.lp__flagacts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex: none; }
.lp__ref { max-width: 190px; }

.lp__policy { margin-top: 16px; display: flex; flex-direction: column; gap: 3px; }
.lp__policyline { font-size: 11px; color: var(--ink-3); }
.lp__policyline--muted { font-style: italic; }

/* ------------------------------------------- the compact area on one lead -- */
.lpmini {
  border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
  background: var(--canvas-2); display: flex; flex-direction: column; gap: 5px;
}
.lpmini--live { border-color: var(--amber); background: var(--amber-w); }
.lpmini__h {
  display: flex; align-items: center; gap: 6px;
  font-size: 10.5px; font-weight: 680; letter-spacing: .06em; color: var(--ink-2);
}
.lpmini__h svg { width: 13px; height: 13px; }
.lpmini--live .lpmini__h { color: var(--amber-ink); }
.lpmini__row { display: flex; flex-direction: column; gap: 1px; }
.lpmini__reason { font-size: 12.5px; font-weight: 600; }
.lpmini__ev { font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }
.lpmini__deadline { font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------ the Today warning strip -- */
.lpstrip {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 10px 14px; border-radius: 10px;
  background: var(--canvas-2); border: 1px solid var(--line);
}
.lpstrip--soon { background: var(--amber-w); border-color: var(--amber); }
.lpstrip__m { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* THE ICON NEEDS A SIZE OR IT TAKES THE STRIP. `ico()` returns an SVG with no
   intrinsic dimensions, so an unconstrained one grows to whatever the flex row
   will give it — which, next to two short lines of text, is enormous. */
.lpstrip__m svg { width: 18px; height: 18px; flex: none; color: var(--ink-3); }
.lpstrip--soon .lpstrip__m svg { color: var(--amber-ink); }
.lpstrip__t { font-size: 13px; font-weight: 620; }
.lpstrip--soon .lpstrip__t { color: var(--amber-ink); }
.lpstrip__s { font-size: 11.5px; color: var(--ink-3); }

/* --------------------------------------- the optional import batch fields -- */
.lpimport { display: flex; flex-direction: column; gap: 10px; }
.lpimport__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px;
}
.lpimport__checks { display: flex; gap: 16px; flex-wrap: wrap; }

@media (max-width: 720px) {
  .lp__head { flex-direction: column; gap: 8px; }
  .lp__winbox { text-align: left; }
  .lp__flag { flex-direction: column; }
  .lp__flagacts { justify-content: flex-start; }
  .lp__ref { max-width: 100%; }
}

/* ------------------------------------------- pinned to the dashboard ------
   The featured-on-Today toggle, styled as the other card states are. Filled
   only when it IS the featured one — there is exactly one per workspace, so a
   filled pin on a card is an unambiguous answer to "which video is on Today?". */
.check--pin.is-on {
  background: var(--navy); color: #fff; border-color: transparent;
}
.check--pin.is-on svg { color: #fff; }
[data-theme="dark"] .check--pin.is-on { background: var(--electric); color: #06101f; }
[data-theme="dark"] .check--pin.is-on svg { color: #06101f; }

/* ============================================================================
   THE MODAL SCROLLS FROM ONE CONTAINER, NOT TWO
   ============================================================================

   `.modal` was `max-height: 88vh; overflow-y: auto` and `.modal__body` was
   `max-height: 68vh; overflow: auto` — two nested scroll containers with
   independent, unrelated limits. When the body is long the inner one reaches
   its end while the outer still has content clipped beneath it, and the
   producer is left looking at a half-rendered table that will not move. That
   is what happened on the import wizard with a file whose column list ran to
   hundreds of rows: the buttons were reachable, the content was not.

   A flex column fixes it properly. The head and the footer take their natural
   height, the body takes everything left over and scrolls inside it, and there
   is exactly one scrollbar however long the content is.

   `min-height: 0` is the load-bearing line: without it a flex child refuses to
   shrink below its content height and `overflow: auto` never engages, which is
   the single most common way this layout is written and quietly broken.
   ------------------------------------------------------------------------- */
.modal {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.modal__body {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* The head and footer must not be squeezed by a long body. */
.modal__head, .modal__foot { flex: 0 0 auto; }

/* --------------------------------------- the blanks, filled before the call */
.cl__fills {
  margin-top: 12px; padding: 12px; border-radius: 10px;
  background: var(--canvas-2); border: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 10px;
}
.cl__fillhead { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cl__fillnote { font-size: 11.5px; color: var(--ink-3); line-height: 1.5; margin: 0; }

/* A FILL MUST NEVER READ AS SOURCE WORDING. `.mf--unmapped` is the source's own
   token; this is the producer's typing, and it is tinted differently on purpose
   so a glance tells them which words came from the upline and which came from
   them. */
.mf--filled {
  background: var(--emerald-w); color: var(--emerald-ink);
  border-radius: 5px; padding: 0 5px; font-weight: 640;
}

/* ONE ROW PER SENTENCE, with the fields sitting where the blanks are.
   Stacking a full-width input under a truncated fragment produced a very tall
   list of near-identical rows; reading the line and typing into it is both
   shorter and unambiguous about which blank is which. */
.cl__fillgrid { display: flex; flex-direction: column; gap: 10px; }
.cl__fillrow { display: flex; flex-direction: column; gap: 4px; }
.cl__fillctx { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cl__fillstage {
  font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); margin-top: 10px;
}
.cl__fillstage:first-child { margin-top: 0; }
.cl__fillquote {
  font-size: 12.5px; color: var(--ink-2); overflow-wrap: anywhere;
  line-height: 2.15; margin: 0;
}
/* Sized to the answer, not to the container — a name, an age and a premium are
   all short, and a full-width box for each is what made this scroll. */
.cl__fillslot {
  display: inline-block; width: auto; min-width: 9ch; max-width: 22ch;
  height: auto; padding: 1px 6px; margin: 0 2px;
  font-size: 12.5px; vertical-align: baseline;
}

/* a blank inside the running script: tappable, and obviously so */
.slot { display: inline-block; }
.slot__blank, .slot__val { border: 0; font: inherit; cursor: pointer; }
.slot__blank { text-decoration: underline dotted; text-underline-offset: 3px; }
.slot__input {
  font: inherit; padding: 0 6px; min-width: 7ch; max-width: 22ch;
  border: 1px solid var(--electric); border-radius: 5px;
  background: var(--surface); color: var(--ink-1);
}
.cl__filltarget {
  background: var(--amber-w); color: var(--amber-ink);
  border-radius: 4px; padding: 0 4px; font-weight: 700;
}

/* ============================================================ script text ==
   READ ALOUD, NOT READ THROUGH.

   The packet separates its question runs with "•". Rendered inline they became
   nine questions in one grey wall, which is what a producer is looking at while
   someone is on the phone waiting for the next one. Split into a lead-in
   sentence and a spaced list, the eye finds the next line without re-reading.

   The words are the source's own and unchanged; only the separator it wrote is
   drawn as the separator it is. */
.script__para { margin: 0 0 14px; }
.script__para:last-child { margin-bottom: 0; }
.script__list {
  margin: 0 0 14px; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 10px;
}
.script__list:last-child { margin-bottom: 0; }
.script__item {
  position: relative; padding-left: 22px;
}
/* The marker is drawn, not typed, so it can never be mistaken for script text
   and can never be selected and pasted into a record. */
.script__item::before {
  content: ''; position: absolute; left: 6px; top: .62em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--electric); opacity: .55;
}

/* ---------------------------------------------------- the blanks, folded --
   Fourteen stacked rows made Call Mode a scrolling page and stretched the lead
   card beside it into a tall empty rectangle. Closed by default, and when open
   the list scrolls inside itself instead of growing the page. */
.cl__fillhead { flex-wrap: wrap; }
.cl__fillacts { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.cl__fillcount { font-size: 11.5px; color: var(--ink-3); }
.cl__fills .cl__fillgrid,
.cl__fills .cl__fillnote--open { display: none; }
.cl__fills--open .cl__fillgrid {
  display: flex; max-height: 42vh; overflow-y: auto; padding-right: 4px;
}
.cl__fills--open .cl__fillnote--open { display: block; }
/* Content-height cards: with the blanks closed there is nothing to stretch to,
   and a launch card should not be tall because its neighbour is. */
.cl__launch { align-items: start; }

/* The call-language note sits above a live call, so it states the cause and
   carries its own way out rather than filling the width with an alarm. */
.cm__langnote {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  max-width: 980px; margin-left: auto; margin-right: auto;
}
.cm__langnote .btn { margin-left: auto; }

/* The eight rarer call outcomes, folded. Nothing is removed — the day a producer
   takes an application he needs that button — but fourteen at once is a wall to
   read with the phone still warm. */
.pillmore { display: none; margin-top: 10px; }
.pillmore--open { display: block; }

/* ==========================================================================
   THE FIVE-COLUMN LEADS BOARD (founder direction, 2026-08-10)

   The board was one column per stored stage — ten `.board__col` at a fixed
   268px inside `.board { overflow-x: auto }` — so working 142 paid leads meant
   scrolling sideways to drag. These are NEW classes rather than an override of
   the Gate 4 `.board*` ones: `styles.css` is the approved system adopted
   verbatim and production-only differences belong here, in one file, where the
   drift is visible.

   THE GRID NEVER OVERFLOWS. `minmax(0, 1fr)` on every track means the columns
   divide whatever width there is instead of demanding a width and pushing the
   page sideways. Do not call is given a narrower track: it holds fewer cards,
   carries no next action and needs the least room, so the four columns the
   producer actually works in get the rest.

   COLOUR IS THE POINT AND IS STILL QUIET. Each column is tinted with the
   already-approved soft token for its meaning — no new hue, no neon, both
   themes paired because the tokens are — and the cards inside stay on
   `--surface` so the text keeps its contrast.
   ========================================================================== */

.pboard {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(0, .8fr);
  gap: 10px;
  align-items: start;
}
.pboard__col {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--border); border-top: 3px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface); padding: 9px 9px 11px;
}
.pboard__head { display: flex; align-items: center; gap: 7px; padding: 0 3px; }
.pboard__swatch {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--border-strong);
}
.pboard__name {
  font-size: 12.5px; font-weight: 650; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pboard__count { margin-left: auto; font-size: 13px; font-weight: 620; color: var(--text-muted); }
/* The stored stages, verbatim. Wraps rather than truncates: a stage list that
   ends in an ellipsis is a column claiming to contain something it will not
   name. */
.pboard__stages {
  font-size: 10.5px; line-height: 1.35; color: var(--text-faint);
  padding: 3px 3px 0; overflow-wrap: anywhere;
}
.pboard__facts { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 3px 0; }
.pboard__drop {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 8px; min-height: 84px;
  /* A column of ninety cards must not make the page ninety cards tall, and the
     limit is measured against the space LEFT rather than against the viewport.
     `min(64vh, 760px)` ignored the ~290px of shell, page head and filter bar
     above the board, so on a 1280x700 laptop the off-board row and the drag
     hint were both below the fold and the producer scrolled the page and then
     scrolled inside a column — two scrollers for one board. The floor keeps a
     column usable on a short window. */
  max-height: max(240px, calc(100vh - 360px)); overflow-y: auto;
  border-radius: var(--radius-sm); padding: 3px;
  transition: box-shadow var(--dur) var(--ease);
}
.pboard__drop.is-over { box-shadow: inset 0 0 0 2px var(--accent); }
.pboard__drop.is-protected.is-over { box-shadow: inset 0 0 0 2px var(--caution-line); }
.pboard__empty { font-size: 11.5px; color: var(--text-faint); padding: 10px 5px; }

/* WHITE · YELLOW · BLUE · GREEN · RED, in the founder's own terms. */
.pboard__col--new { background: var(--surface); }
.pboard__col--working { background: var(--caution-soft); border-top-color: var(--caution); }
.pboard__col--working .pboard__swatch { background: var(--caution); }
.pboard__col--appointment { background: var(--accent-soft); border-top-color: var(--accent); }
.pboard__col--appointment .pboard__swatch { background: var(--accent); }
.pboard__col--sold { background: var(--positive-soft); border-top-color: var(--positive); }
.pboard__col--sold .pboard__swatch { background: var(--positive); }
.pboard__col--blocked { background: var(--danger-soft); border-top-color: var(--danger); }
.pboard__col--blocked .pboard__swatch { background: var(--danger); }

/* Cards are denser on a five-column board than they were on a 268px one. */
.pboard .leadcard { padding: 10px 11px; min-width: 0; }
.pboard .leadcard__name { font-size: 13.5px; }
.pboard .leadcard__meta { font-size: 11.5px; }
.pboard .leadcard__chips { gap: 4px; margin-top: 7px; min-width: 0; }
.pboard .leadcard__next { font-size: 11.5px; margin-top: 7px; }
/* A chip is `white-space: nowrap` and has no width limit, so a long stored
   value — "App: Pending Requirements" is 180px — pushed straight through a
   138px card. Because the drop zone scrolls vertically its overflow-x resolves
   to auto, so every column grew its own horizontal scrollbar: the board stopped
   overflowing sideways and the columns started. The value is truncated with an
   ellipsis and kept in full in the title, so nothing is hidden outright. */
.pboard .leadcard__chips .chip {
  max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; display: block;
}

/* A lead on Do Not Contact. Still draggable, so a producer who tries to pull
   one back out gets the reason instead of nothing happening. */
.leadcard--blocked { border-color: var(--danger); }
.leadcard__dnc {
  font-size: 11px; line-height: 1.4; color: var(--danger); margin-top: 7px;
  overflow-wrap: anywhere;
}

/* The two per-card actions. Quiet until the card is under the pointer or holds
   focus — 142 cards each showing two buttons is the clutter this work was asked
   to remove — but always in the DOM, so they are reachable by keyboard and by
   a screen reader rather than existing only on hover. */
.leadcard__acts {
  display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px;
  opacity: 0; transition: opacity var(--dur) var(--ease);
}
.leadcard:hover .leadcard__acts,
.leadcard:focus .leadcard__acts,
.leadcard:focus-within .leadcard__acts { opacity: 1; }
.leadcard__act { padding: 3px 8px; font-size: 11px; min-height: 0; }
/* No pointer, no hover state to reveal them with. */
@media (hover: none) { .leadcard__acts { opacity: 1; } }

/* Off the board: 'Closed / Lost' and Suppressed, as counts that reveal
   themselves in the List View where every record shows its own stored stage. */
.pboard__off {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin-top: 14px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-sunken);
}
.pboard__offlabel { font-size: 12px; font-weight: 650; color: var(--text-muted); }
.pboard__offchip { cursor: pointer; }
.pboard__offchip:hover { border-color: var(--accent); color: var(--accent); }
.pboard__offhint { flex: 1 1 240px; min-width: 0; }

/* Below the desktop layout the five columns stop being five. Two is the last
   width at which a drag between columns is still a real gesture; below that the
   board is a single ordered stack and the per-card actions do the work. */
/* THE COLUMNS KEEP THEIR OWN SCROLLERS AT EVERY WIDTH. `max-height: none` was
   here, and with 142 leads two-up it made the page 17,830px tall: Appointment
   started 11,533px down and Do not call 16,016px down, so reaching the red
   column on an iPad or a narrow laptop window meant twenty screens of
   scrolling — a worse version of the sideways scroll this work removed. */
@media (max-width: 1023px) {
  .pboard { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .pboard { grid-template-columns: minmax(0, 1fr); }
}

/* ====================================================== the glance card ====
   Who is on the phone, readable without leaving the sentence. It sits ABOVE the
   right-hand panel rather than inside it, because that panel is replaced by Ask
   Point when it docks and a card that disappears when the assistant opens is one
   the producer cannot rely on mid-call. */
.cm__rightcol { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.cm__glance {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 12px 14px;
}
.cm__glancelist {
  margin: 8px 0 0; display: grid; grid-template-columns: auto 1fr;
  gap: 4px 12px; align-items: baseline;
}
.cm__glancek { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); margin: 0; }
.cm__glancev { margin: 0; font-size: 13.5px; font-weight: 600; color: var(--ink-1);
  overflow-wrap: anywhere; }

/* THE RIGHT COLUMN SCROLLS INSTEAD OF BEING CUT OFF.
   `.cm__rightcol` is the third GRID ITEM of `.cm__body`, so it stretches to the
   row height and needs `min-height: 0` before it can scroll — without it the
   content simply overflowed and the founder lost the last objection off the
   bottom of the screen while a prospect was talking.
   ONE SCROLLER, NOT TWO. `styles.css` already gives `.cm__rail` its own
   `overflow-y: auto`; leaving that on while the wrapper also scrolls puts a
   scrollbar inside a scrollbar and traps the wheel in whichever the pointer
   happens to be over. The column scrolls; the rail inside it does not. */
.cm__rightcol { min-height: 0; overflow-y: auto; padding-bottom: 14px; }
.cm__rightcol .cm__rail { overflow: visible; }

/* Dial count on a lead card. Quiet by default; the number itself carries the
   weight, because "4" is the thing being scanned for, not the word "calls". */
.leadcard__calls { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.leadcard__calln { font-weight: 700; color: var(--ink-2); }

/* ------------------------------------------------------------ call line --- */
/* THE POST-CALL SCREEN'S NEW LAST STEP. `postcall__foot` is already
   `display:flex; gap:10px` in styles.css, so the three choices after a save need
   no layout of their own — only the "next up" line does, and it borrows the
   Call Mode launch card's two type roles (`cl__nextlabel` + `cl__nextref`) so
   "Next in the call line" reads identically on both screens. */
.postcall__next {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px;
  padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--canvas-2);
}

/* One worked-out lead, in the review panel. The reference is what he scans, so
   it holds the weight; the Reviewed button sits at the end of the row and the
   row wraps rather than truncating a display reference. */
.cl__worked {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px;
  padding: 7px 0; border-top: 1px solid var(--line);
}
.cl__worked:first-of-type { border-top: 0; }
.cl__workedref { font-size: 13px; font-weight: 640; color: var(--ink-1); }
.cl__worked .btn { margin-left: auto; }

/* A LABEL MUST NOT PAINT OUTSIDE ITS OWN BUTTON. `.leadcard__act` pinned
   `min-height: 0` against a `.btn` whose line-height assumes one line, so a
   card action whose text wrapped — "Añadir a la fila de llamadas" at 1280, and
   every one of these three at 1024 — drew its second line over the card
   underneath the pill. The board takes a third action now, so this stopped
   being hypothetical. Height follows the text; the row already wraps. */
.leadcard__act { line-height: 1.25; height: auto; white-space: normal; }

/* THE OUTCOME IS RECORDED AND THE FORM IS NO LONGER ONE. Every control above
   the result is disabled by `afterSave`; this is what that looks like, so a
   producer does not click a disposition pill that can no longer go anywhere.
   Not `pointer-events: none` — the controls stay focusable and announced, they
   simply state that they are finished with. */
.postcall__body--saved { opacity: 0.62; }
.postcall__body--saved .pill,
.postcall__body--saved .input,
.postcall__body--saved .checkline { cursor: default; }
