/* ══════════════════════════════════════════════════════════════════════════
   LABBIE AI COLD CALLING — product layer

   WHAT THIS FILE IS NOT: a design system. It used to be one — a self-contained
   cream-and-frosted-glass language with its own tokens, its own typography,
   its own radii and its own shell — which meant Labbie had two dashboards that
   looked like two different products, and a token change in either one could
   never reach the other.

   That system is gone. The console now wears the Labbie dashboard's, served
   from the SAME files the user dashboard links:

     /assets/brand/labbie-logo.css              the brand mark
     /assets/labbie-dashboard-shell.css         tokens, shell, nav, cards, ≤720px drawer
     /assets/glass.css                          the global Labbie DNA
     /assets/labbie-dashboard-components.css    buttons, inputs, tables, notices, drawer
     /assets/labbie-dashboard-desktop.css       the ≥901px redesign

   (Reached here as /coldcalling/assets/labbie/… — see the allowlist in
   coldcalling/index.js. Same bytes, one mount away.)

   What remains below is only what cold calling alone has: the PIN gate, the
   live-call panel and its metrics, the event timeline, the import stepper and
   duplicate resolution, and the voice test. Every one is built from the shared
   tokens — --text, --muted, --line, --card, --radius-*, --token-* — so it
   cannot drift away from the dashboard it sits inside.

   NO SECOND PALETTE. There is no amber, no orange and no status hue anywhere
   in this file. Black, white, grey, and the one red (#b91c1c) that Labbie
   already uses for error. A simulation console has a lot to say about its own
   limitations, and none of it is an emergency.
   ══════════════════════════════════════════════════════════════════════════ */


/* ── THE PAGE CANVAS ───────────────────────────────────────────────────────
   Pure white (#FFFFFF), flat, everywhere.

   It was Anti-Flash White (#F2F3F4) — a grey a shade off white, which is the
   worst of both: not white enough to read as paper, not grey enough to make a
   white card stand off it. The canvas is now the same white as the cards, and
   what separates them is the shadow below rather than a tint. Nothing else
   about the palette moves; --cc-card-shadow is what takes over the job the
   grey was doing badly.

   The shared desktop layer paints a warm cream canvas for the USER dashboard —
   labbie-dashboard-desktop.css sets `html { background: #f4efe4 !important }`
   plus a three-gradient golden wash on <body> above 901px, and the mirrored
   gradient stack on <html> below it. That is the Labbie dashboard's identity
   and it stays exactly as it is: this console links the same bytes as
   frontend/dashboard.html, so the canvas is overridden HERE, in the only file
   the dashboard does not load, and never in the shared sheet.

   Both breakpoints are answered by ONE unconditional pair of rules, because a
   media query adds no specificity: `body.lb-console` (0,1,1) outranks the
   shared `body` (0,0,1), and `html:has(> body.lb-console)` (0,1,2) outranks
   the shared `html` (0,0,1), in both blocks and with !important on both sides.
   Source order settles nothing here and is not relied on.

   BOTH elements are painted, and neither is decorative. <html> is the one the
   browser propagates to the viewport canvas — the overscroll gutter, the strip
   under short content, the area behind the transparent desktop sidebar — and
   because it carries its own background, <body>'s never propagates. <body> is
   what covers the layout box itself and is what replaces the desktop gradient.
   Painting only one of them leaves cream somewhere.

   .lb-console is on <body> in coldcalling/public/index.html and nowhere else
   in the repository, which is what keeps this off every other route.

   Surfaces are deliberately untouched: no card, sidebar, button, input or
   control is named here. This rule pair changes the canvas and nothing else.

   ONE token holds the colour, so the canvas has a single definition and the
   two rules below cannot drift apart. Only this file declares it, and only
   this file reads it. */

:root { --cc-canvas: #FFFFFF; }

html:has(> body.lb-console) { background: var(--cc-canvas) !important; }
body.lb-console             { background: var(--cc-canvas) !important; }


/* ══════════════════════════════════════════════════════════════════════════
   THE CONSOLE'S SHARED MEASUREMENTS

   The measurements the whole product reads, declared once. Everything below
   that is about a card's shadow, a pill's height, a title's weight or the gap
   under it takes its value from here rather than restating one — which is what
   let the Leads page's title drift to 1.97rem/700 while every other page
   stayed at 1.45rem/400, and nothing could tell that they had.

   (The lead-list card's three own numbers are declared with it, on
   .cc-ll-grid, because nothing outside that grid reads them.)

   None of these is a colour. The only colour this file changes is the canvas
   above.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── THE APPLE-STYLE CARD SHADOW ────────────────────────────────────
     Two layers: a 1px contact shadow that draws the card's edge, and a wide
     soft one that lifts it off the page. Both neutral ink at very low alpha,
     which is what keeps a white card legible on a white canvas without a
     border drawn round it.

     Deliberately NOT var(--dash-shadow). That token is the user dashboard's
     and it changes with the breakpoint: below 901px it becomes
     `0 14px 34px rgba(146,104,32,0.10)` — a GOLDEN shadow, right for a cream
     canvas and wrong for this one. This console's is one value at every
     width, and it is neutral at every width. */
  --cc-card-shadow: 0 1px 2px rgba(17, 24, 39, 0.04), 0 10px 30px rgba(17, 24, 39, 0.05);

  /* ── THE TWO PILLS ──────────────────────────────────────────────────
     The product switcher (sidebar, top left) and the account menu (main,
     top right) are one visual row and must be one height. Their padding and
     their contents are set by two different shared sheets and had no reason
     to agree — they happened to, at one breakpoint, which is not the same as
     being the same.

     44px is the product switcher's OWN natural height and is not a new
     number: 28px brand mark + 7px padding top and bottom + 1px border. It is
     written down so the account pill can be told to match it, and so a test
     can read one value rather than compare two computed boxes. */
  --cc-pill-h: 44px;

  /* The shared desktop sheet's own `.main { padding: 56px 18px 28px 4px }`
     (labbie-dashboard-desktop.css). The account pill is lifted by exactly
     this to sit in the band that padding reserves, level with the switcher.
     If that padding changes, this follows it — it is a mirror of a value
     this file does not own, not a measurement of a viewport. */
  --cc-main-pad-top: 56px;

  /* ── THE PAGE TITLE ─────────────────────────────────────────────────
     One family, size, weight and line height for every page title in the
     product. The values are the Agents/Dashboard treatment, which is the
     one the shared sheets already set and the one the design is built
     around; the Leads page's heavier pair is what this replaces.

     The line height is written down rather than left at `normal`, because
     `normal` is a property of whichever font actually resolved and a title
     that falls back to a system face would then sit at a different height
     from one that got Inter. 1.2 is what the shared sheet already uses for
     the dashboard greeting. */
  --cc-title-size:   1.45rem;
  --cc-title-weight: 400;
  --cc-title-lh:     1.2;
  --cc-title-track:  -0.025em;

  /* The description sits DIRECTLY under the title. 4px is the Leads page's
     gap, which is the one that reads as a single header block; every other
     page inherited the shared sheet's 24px and read as two. */
  --cc-title-gap: 4px;
}

/* ── COLD CALLING TYPOGRAPHY — THE ONLY FONT-FAMILY AUTHORITY HERE ─────────
   One rule, scoped to this product's body class, consuming --cc-font-ui:
   Inter, declared ONCE in assets/fonts/inter/inter.css together with its
   three faces and the reason for their weight ranges. This file declares no
   font stack and no @font-face of its own. Should that sheet ever fail to
   load, glass.css's platform --font-ui stands in, so the console falls back
   to the face it used before rather than to the browser's default serif.

   EVERYTHING ELSE INHERITS. The shared form primitives (.lb-input,
   .lb-select, .lb-textarea) and the shared `button` rule already declare
   `font: inherit`, so no control needs naming here.

   THE SEVEN EXTRA SELECTORS ARE NOT PAGE OVERRIDES. Each exists solely
   because a rule in a SHARED sheet targets that element directly with
   !important, and inheritance never competes with a direct rule. They are
   deleted — not multiplied — when the platform-wide migration is authorised.

   `!important` and the `body.` prefix are load-bearing. glass.css sets
   `body` !important and labbie-dashboard-desktop.css sets
   `body .section-head .section-title` !important at (0,2,1) — which this
   file ties, and wins only because it loads last. Dropping `body.` would
   lose that tie and silently leave section titles on the old face. */
body.lb-console,
body.lb-console .brand-word,
body.lb-console .sidebar-brand-word,
body.lb-console .sb-ws-label,
body.lb-console .page-title,
body.lb-console .section-title,
body.lb-console .cb-title,
body.lb-console .cb-card-name {
  font-family: var(--cc-font-ui, var(--font-ui)) !important;
}

/* Narrow screens step the title down — ALL of them, together. This is the
   whole of the console's responsive title behaviour: one token moves and the
   single rule that reads it follows, rather than each page carrying its own
   `font-size: …!important` at each breakpoint, which is how the Leads page
   came to have three title sizes nobody else had. */
@media (max-width: 900px) { :root { --cc-title-size: 1.3rem; } }
@media (max-width: 720px) { :root { --cc-title-size: 1.25rem; } }


/* ── ICONS ─────────────────────────────────────────────────────────────────
   The product's sprite, at a single optical weight, inheriting ink from its
   container so one rule tints an icon and its label together. */

.cc-i { width: 18px; height: 18px; stroke: currentColor; stroke-width: 1.6;
        fill: none; stroke-linecap: round; stroke-linejoin: round;
        display: block; flex: none; }
.cc-i--sm { width: 15px; height: 15px; stroke-width: 1.7; }
.cc-i--lg { width: 20px; height: 20px; }
.cc-nav .cc-i { stroke-width: 1.9125; }

/* The `hidden` attribute must beat any display rule this file sets. An author
   `display` wins over the UA sheet's `[hidden] { display: none }` regardless of
   specificity, so `.cc-gate { display: grid }` once left the signed-in page
   reserving a whole viewport for an invisible PIN screen. Declared once, here,
   so no component added later can reintroduce it. */
[hidden] { display: none !important; }


/* ══════════════════════════════════════════════════════════════════════════
   THE PIN SCREEN
   ══════════════════════════════════════════════════════════════════════════ */

.cc-gate {
  display: grid;
  place-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 24px;
}

.cc-gate__card {
  width: 100%;
  max-width: 372px;
  text-align: center;
  padding: 34px 30px 26px !important;
}
.cc-gate__mark { width: 42px; height: 42px; margin: 0 auto 16px; }
.cc-gate__title { font-size: 1.3rem !important; margin-bottom: 4px; }
.cc-gate__sub   { margin-bottom: 22px !important; }
.cc-gate__form  { display: flex; flex-direction: column; gap: 12px; }

.cc-pin-input {
  text-align: center;
  font-size: 1.15rem;
  letter-spacing: 0.4em;
  padding: 13px 12px;
}
.cc-pin-input::placeholder { letter-spacing: 0.3em; }

/* Reserved height, so a message appearing never moves the button under a
   cursor that is already on its way there. */
.cc-gate__msg {
  min-height: 20px;
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
  line-height: 1.45;
}
.cc-gate__msg[data-tone="error"] { color: #b91c1c; }
.cc-gate__msg[data-tone="wait"]  { color: var(--muted); }
.cc-gate__foot { margin-top: 20px; font-size: 0.76rem; color: var(--muted); }

@keyframes cc-shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(3px); }
  30%, 50%, 70% { transform: translateX(-5px); }
  40%, 60% { transform: translateX(5px); }
}
.cc-shake { animation: cc-shake 380ms cubic-bezier(0.32, 0.72, 0, 1); }


/* ══════════════════════════════════════════════════════════════════════════
   THE APP SHELL

   The structure is the shared one (.layout / .sidebar / .main). All this file
   adds is the reveal — the console stays hidden until the session answer says
   which of the two screens to show, so neither ever flashes.
   ══════════════════════════════════════════════════════════════════════════ */

.cc-app { display: none; }
.cc-app[data-ready="true"] { display: contents; }

/* The nav is a transparent wrapper: script renders .nav-item buttons into it
   and .sidebar-menu-box lays them out, so cold calling's navigation gets the
   dashboard's own spacing, gap and active state rather than a copy of them. */
.cc-nav { display: contents; }

/* ── Simulation Mode ──────────────────────────────────────────────────────
   Anchored above Sign out inside the stationary sidebar, so it is visible on
   every screen and at every scroll position.

   Monochrome ON PURPOSE. Simulation is the product's configured state, not a
   fault: an amber panel would report an emergency where there is none, and
   would be the only warm colour in a black-and-white dashboard. The slightly
   reduced opacity says "this is chrome, not content" without hiding it. */
.cc-mode {
  margin: 0 4px 10px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--token-surface-secondary, #f9fafb);
  opacity: 0.9;
}
.cc-mode__row  { display: flex; align-items: center; gap: 7px; }
.cc-mode__icon { color: var(--muted); }
.cc-mode__name { font-size: 0.82rem; font-weight: 500; color: var(--text); }
.cc-mode__meta { margin-top: 3px; font-size: 0.76rem; color: var(--muted); line-height: 1.4; }

/* ── The brand pill ───────────────────────────────────────────────────────
   Nothing about its height, radius, padding, typography, divider, border,
   shadow or hover is set here — all of that is the shared component's, and
   changing any of it would be the start of a second design system.

   The one thing this product owns is its LABEL, and "AI COLD CALLING" is
   longer than "DASHBOARD": at Inter it measures ~263px against a 264px
   sidebar, so the pill fills the column instead of hugging its text. That is
   the same component at its natural size and is fine — but with barely a pixel
   of slack, a font that resolves to a wider fallback would push the pill over
   the content beside it. Bounded here so it cannot.

   STATIC. The shared component is a menu trigger — a caret after the label
   and a pointer cursor. Here it is branding only and opens nothing, so both
   come off, scoped to this console so the Labbie dashboard's own switcher is
   untouched. Not selectable either, exactly as it was while it was a button.

   The caret's glyph comes from a symbol font taller than the label's own, and
   it was what set this line's height: a 16px box with the words 2px from its
   top. Without it the box is 13px and the words ride 0.5px higher. The
   padding restores that box, so the words stay exactly where they were. */
@media (min-width: 901px) {
  .sidebar-brand      { max-width: 100%; }
  .sidebar-brand-pill { max-width: 100%; min-width: 0; }
  .sidebar-brand-word { flex: none; }
  .sb-ws-label        { min-width: 0; overflow: hidden; white-space: nowrap; }

  .lb-console .sidebar-brand-pill { cursor: default; -webkit-user-select: none; user-select: none; }
  .lb-console .sb-ws-label::after { content: none; }
  .lb-console .sb-ws-label        { padding: 2px 0 1px; }
}

/* ── The account / workspace popover ─────────────────────────────────────── */
.cc-acct__head { padding: 9px 12px 7px; }
.cc-acct__name { font-size: 0.88rem; font-weight: 500; color: var(--text); }
.cc-acct__meta { margin-top: 2px; font-size: 0.76rem; color: var(--muted); }

/* Sign out carries the sidebar Sign out's own icon. The shared sheet hides
   every popover-item svg; this one row is the exception. */
.account-popover .cc-acct__signout svg { display: block; flex-shrink: 0; }

/* Sits above the first heading and scrolls away with the page, exactly like
   the dashboard's account pill. Float keeps the page title beside it on a wide
   screen; below the drawer breakpoint it takes a row of its own, because a
   floated pill and a wrapping title fight for the same 375 pixels. */
@media (max-width: 720px) {
  .view-user-slot { float: none; display: flex; justify-content: flex-end; margin: 0 0 14px; }
}

/* ── THE TWO PILLS ARE ONE ROW ────────────────────────────────────────────
   The product switcher sits at the top of the sidebar; the account menu sat
   at the top of the main content, 56px lower, which put it level with the
   page title instead of with the switcher. They read as two unrelated
   controls at two heights when they are the shell's one top row.

   THE LIFT. The shared desktop sheet already reserves a 56px band at the top
   of .main and puts nothing in it (`.main { padding: 56px 18px 28px 4px }`).
   Pulling the pill up by exactly that lands it in the band — same top, same
   bottom, same row as the switcher — and leaves the page head starting where
   it always did, because the float's box now ends above .main's content box
   and no longer shortens the title's line.

   Not position:fixed and not sticky. The pill stays IN FLOW and scrolls away
   with the page, which is what the shared sheet's own note calls Bug 2.

   THE HEIGHT. Both pills are told the same number rather than left to arrive
   at it. --cc-pill-h IS the switcher's natural height, so this cannot make it
   grow; what it does is stop the account pill from drifting when either
   sheet's padding changes underneath it. min-height, not height, so a longer
   account name still wraps rather than being clipped. */
@media (min-width: 901px) {
  .lb-console .sidebar-brand,
  .lb-console .view-user-slot .tb-user-trigger { min-height: var(--cc-pill-h); }
  .lb-console .view-user-slot { margin-top: calc(var(--cc-main-pad-top) * -1); }
}

/* Notices sit in the content flow, above everything they qualify. */
.cc-notice { margin-bottom: 14px; }


/* ── THE MAIN PANEL REACHES THE SIDEBAR'S BOTTOM LINE ─────────────────────
   On a page whose content is one white surface — the Leads Library, Needs
   Attention, a campaign list — that surface stopped wherever its content did
   and left a few hundred pixels of canvas beside a sidebar that ran to the
   bottom of the screen. Two columns, two different lengths, no reason.

   A MINIMUM, NOT A HEIGHT. The panel is grown to the line by flex-grow, and
   `flex: 1 0 auto` is the whole of the behaviour: grow to fill what is left,
   NEVER shrink (0), and start from the content's own size (auto). A short
   page reaches the line; a long one pushes past it and the page scrolls, with
   nothing clipped, nothing squashed and no scroll region trapped inside the
   card. There is no fixed height anywhere in this block.

   WHY min-height: 100% WORKS. .layout is 100vh and .main is the flex child
   that takes what is left of it, so .main has a definite height and a
   percentage minimum on its child resolves against it. 100% is .main's
   CONTENT box, which is why its bottom padding is dropped just below: with
   28px of it the panel would stop 28px short of the line, which is the one
   thing this block exists to prevent. The 14px gutter under the panel is
   .layout's own padding and is still there — the sidebar has exactly the same
   one, which is what makes the two bottoms a single line rather than two
   coincidental ones.

   :only-of-type IS THE "ONE PANEL" TEST. It matches a .section-card that is
   the only <section> among its siblings — which is precisely "this page has
   one main content surface". Settings and the Dashboard stack several, so
   nothing there matches and nothing there moves; no page needs a class of its
   own and no page-specific rule is written.

   THE ONE EXCLUSION is the Agents landing card, and it is not this block's
   invention: that card is 440px wide, centred, and its own rules already say
   in as many words that stretching it "would be 650px of white around four
   lines of text". It is an onboarding surface, not a page panel. */
@media (min-width: 901px) {
  .lb-console .main { padding-bottom: 0; }

  .lb-console #dashboardPanel,
  .lb-console #viewPanel { display: flex; flex-direction: column; min-height: 100%; }

  .lb-console #dashboardPanel > .section-card:only-of-type,
  .lb-console #viewPanel > .section-card:only-of-type:not(.cc-agents-card) {
    flex: 1 0 auto;
    /* Its own bottom margin would otherwise hold it that far off the line. */
    margin-bottom: 0;
    /* A column, so the body below can be told to take the height the card
       just gained. Without this the card grows and its contents do not. */
    display: flex; flex-direction: column;
  }

  /* A filled panel's body fills it too, so an empty state inside one is
     centred in the surface rather than stranded against its top edge. This is
     the rule .cc-ll-empty-card already carried on its own; it belongs to every
     panel that grows, not to one screen. */
  .lb-console #viewPanel > .section-card:only-of-type > .cc-card__body,
  .lb-console #dashboardPanel > .section-card:only-of-type > .cc-card__body {
    flex: 1; display: flex; flex-direction: column;
  }
  .lb-console #viewPanel > .section-card:only-of-type > .cc-card__body > .lb-empty {
    margin-block: auto;
  }
}


/* ── ONE PAGE HEADER, EVERY PAGE ──────────────────────────────────────────
   .page-title and .page-sub are set by THREE sheets this product does not
   own — glass.css pins the title to 1.25rem/400 with !important, the desktop
   sheet to 1.45rem/400 with !important, the shell sheet to 1.25rem/400 — and
   the Leads Library then overrode all three with 1.97rem/700 to match a
   reference. The result was a product whose pages were not the same product:
   "Leads" was half again the size of "Agents" and heavy where it was regular,
   and the two had no shared definition that could have said so.

   ONE RULE, ON THE SHARED COMPONENT. Every page head in this console is
   viewHead()'s .lb-page-head — the Leads page included, which passes
   'cc-ll-head' only as an extra class for its two buttons. So the typography
   is stated once, here, against the component, and no page can restate it
   without deleting this.

   WHY !important. Two of the three sheets above use it, and an author
   !important is beaten only by another. Specificity settles the rest: this is
   (0,3,0) against the desktop sheet's `body .page-title` (0,1,1), so it wins
   without depending on link order. Exactly the reasoning the Leads override
   used; what changes is that there is now one of these rather than one per
   page.

   THE PIN SCREEN IS NOT A PAGE HEAD. Its title is .cc-gate__title and is not
   inside .lb-page-head, so this leaves it alone. */

.lb-console .lb-page-head .page-title {
  font-family: var(--cc-font-ui, var(--font-ui)) !important;
  font-size: var(--cc-title-size) !important;
  font-weight: var(--cc-title-weight) !important;
  line-height: var(--cc-title-lh) !important;
  letter-spacing: var(--cc-title-track) !important;
  color: var(--text);
  /* The description belongs to the title. The shared sheet's 24px put a
     paragraph's worth of air between them and made the pair read as two
     things; this is the gap the Leads page already had right. */
  margin-bottom: var(--cc-title-gap) !important;
}

.lb-console .lb-page-head .page-sub {
  font-size: 0.92rem !important;
  font-weight: 400 !important;
  color: var(--muted);
  line-height: 1.45;
  /* The block's own spacing is .lb-page-head's margin-bottom, which every
     page already shares. A second margin here would add a per-page gap under
     a header that is supposed to be one component. */
  margin-bottom: 0 !important;
}


/* ── ONE CARD SHADOW, EVERY SURFACE ───────────────────────────────────────
   With the canvas at pure white, a card is told apart from the page by its
   shadow alone — so every card has to have the same one. They did not: the
   section card carried the dashboard's two-layer shadow, the stat card a
   single `0 1px 3px rgba(0,0,0,0.03)` that is invisible against white, and
   below 901px both inherited the user dashboard's GOLDEN shadow, which is
   right on cream and wrong here.

   One token, one treatment, every width. No border is added, no radius is
   changed and no card background becomes grey — the shadow is the whole of
   the separation. */
.lb-console .section-card,
.lb-console .stat-card,
.lb-console .cc-metric { box-shadow: var(--cc-card-shadow) !important; }

/* The account pill too, and for the same reason twice over: it carried
   `0 10px 26px rgba(146,104,32,0.10)` — the golden one — while the product
   switcher it now sits level with carried the neutral one. Two pills, one
   row, two shadows. */
.lb-console .view-user-slot .tb-user-trigger { box-shadow: var(--cc-card-shadow); }

/* ── …AND THE SAME SHADOW WHILE A POINTER IS OVER THE CARD ────────────────
   The rule above answers the card at REST. It never answered the HOVERED
   one, and it could not: `body .section-card:hover` in
   labbie-dashboard-desktop.css is (0,2,1) and `.lb-console .section-card`
   is (0,2,0), so above 901px the shared sheet won the moment a pointer
   crossed a card and swapped this console's neutral shadow for

       0 22px 56px rgba(146, 104, 32, 0.11)

   a GOLDEN glow, 56px of blur thrown 22px downwards. On the user
   dashboard's cream canvas that is warmth on warmth and reads as intended.
   On this white one it is a beige haze — in the 14px gutter between the
   sidebar and the content, and as a band under the card. The Campaign page
   is where it showed most, because the creation card is the one tall
   .section-card on it, so its glow had the height to reach both places.

   THE SAME DECLARATION ALSO CHANGES THE SURFACE, and both halves belong to
   this defect: it drops the card to 90% opacity and repaints the hairline
   `rgba(255,255,255,0.72)`, so the card's edge disappears at the very
   moment the haze appears. All three properties are answered here with the
   values the card already carries at rest, which makes hovering it a no-op
   — correct for a surface that is a form, not a link.

   NO NEW COLOUR IS INVENTED. --cc-card-shadow is this file's own token, the
   one declared for exactly this purpose above; --dash-card and
   --dash-card-border are the tokens the resting card is painted with one
   sheet away, so rest and hover cannot drift apart.

   SCOPED TO THE MEDIA QUERY IT ANSWERS, deliberately, rather than being
   unconditional like the canvas pair: the rule being corrected exists only
   above 901px. Below it the neutral `.section-card:hover` in glass.css is
   already the one that wins, and an unconditional rule here would displace
   that for no reason. */
@media (min-width: 901px) {
  .lb-console .section-card:hover {
    background: var(--dash-card, #fff) !important;
    border-color: var(--dash-card-border, rgba(17, 24, 39, 0.07)) !important;
    box-shadow: var(--cc-card-shadow) !important;
  }
}


/* ── ONE CARD DENSITY, EVERY REPEATED SURFACE ─────────────────────────────
   The repeated cards were padded for a page with far more room than this one
   has. Every number below is padding or empty space, and every one of them is
   the reason a screen of four summary cards was taller than the summary.

   WHAT IS NOT TOUCHED. Nothing here changes a colour, a radius, a border or a
   font family, and no content is hidden to make a card smaller — the cards
   below say exactly what they said before, in the same words. The large main
   content panel is not shrunk either: Section 2's rule above GROWS it to the
   sidebar's line, and its padding is the only thing this block reduces.

   THE STACKED PAGES ARE WHERE THIS PAYS. Settings and Usage & Billing are a
   column of section cards, so their padding is repeated seven times down a
   page; the single-panel screens spend it once. */
.lb-console .section-card { padding: 14px 18px !important; }
.lb-console .section-head { margin-bottom: 12px; }

/* THE STATISTIC CARD. 20px/22px of padding and a 38px icon around one number
   and two short lines. The icon is still an icon, the label is still legible
   at the shared uppercase size, and the card is the height of what is in it. */
.lb-console .stat-card { padding: 13px 15px !important; border-radius: 12px; }


/* ══════════════════════════════════════════════════════════════════════════
   DASHBOARD
   ══════════════════════════════════════════════════════════════════════════ */

/* Four tiles, and four is the point: they are a glance, not a report. The
   shared .stats-grid auto-fills, which is right for a variable list; here the
   count is fixed, so the columns are too. */
.cc-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.cc-stat { display: flex; align-items: center; gap: 11px; }
.cc-stat__icon {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--token-empty-icon-bg, rgba(17, 24, 39, 0.10));
  color: var(--token-empty-icon-color, rgba(17, 24, 39, 0.70));
}
/* The glyph follows the disc it sits in rather than staying at the .cc-i--lg
   20px it was drawn at, which would fill a 30px disc corner to corner. */
.cc-stat__icon .cc-i { width: 16px; height: 16px; }
.cc-stat__body { min-width: 0; }
.cc-stat .stat-label { margin-bottom: 2px; }
/* The figure is the card. It stays the largest thing on it, one step down
   from where it was, because the card around it came down further. */
.cc-stat .stat-value { font-size: 1.5rem !important; line-height: 1.15; }
/* An unmeasured value is an em dash at reduced weight — never a 0, which would
   claim a measurement that was never taken. */
.cc-stat .stat-value[data-empty="true"] { color: var(--muted); }
.cc-stat__note { margin-top: 2px; font-size: 0.74rem; color: var(--muted); line-height: 1.35; }

.cc-cols { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.cc-col  { display: flex; flex-direction: column; min-width: 0; }
/* .section-card already carries its own bottom margin; the column just stacks. */

.cc-card__body { min-width: 0; }
.cc-subhead { font-size: 0.92rem !important; }

/* ── Live call ────────────────────────────────────────────────────────────── */
.cc-live__who  { display: flex; align-items: center; gap: 13px; min-width: 0; }
.cc-live__name { font-size: 1rem; color: var(--text); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cc-live__num  { margin-top: 2px; font-size: 0.85rem; color: var(--muted); font-variant-numeric: tabular-nums; }

.cc-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-top: 16px; }
.cc-metric {
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--token-surface-secondary, #f9fafb);
  min-width: 0;
}
.cc-metric__label { font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.cc-metric__value { margin-top: 3px; font-size: 1rem; color: var(--text); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-metric__k { font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.cc-metric__v { margin-top: 3px; font-size: 1rem; color: var(--text); font-variant-numeric: tabular-nums; }

.cc-actions { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }

/* ── Event timeline ──────────────────────────────────────────────────────── */
.cc-events { display: flex; flex-direction: column; gap: 12px; }
.cc-event  { display: grid; grid-template-columns: 7px 84px minmax(0, 1fr); align-items: center; gap: 12px; }
.cc-event__time { font-size: 0.76rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.cc-event__what { font-size: 0.86rem; color: var(--text); min-width: 0; }

/* ── Small text roles ────────────────────────────────────────────────────── */
/* The number as it will actually be dialled, and the text it came from. Paired
   so a normalisation is legible as a change rather than asserted silently. */
.cc-now  { font-variant-numeric: tabular-nums; color: var(--text); }
.cc-was  { color: var(--muted); text-decoration: line-through; }
/* Ordinary UI text, not machine output: this is the key/value and phone
   normalisation display an operator reads, so it belongs to the product's
   normal typography. The class name is unchanged because renaming it would
   require a DOM edit in coldcalling.js. */
.cc-mono { font-family: var(--cc-font-ui, var(--font-ui)); font-size: 0.78rem; letter-spacing: -0.01em; }
.cc-break { word-break: break-all; }
.cc-list { margin: 6px 0 14px 18px; color: var(--muted); font-size: 0.85rem; line-height: 1.65; }
.cc-quote {
  margin: 8px 0 0;
  padding: 11px 13px;
  border-left: 2px solid var(--line);
  background: var(--token-surface-secondary, #f9fafb);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.55;
}
.cc-formula { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.76rem; color: var(--muted); }

/* The simulation marker on an individual record. Same restraint as the sidebar
   panel: this labels a record's origin, it does not raise an alarm. */
.cc-sim {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--token-surface-secondary, #f9fafb);
  color: var(--muted);
  font-size: 0.72rem;
  white-space: nowrap;
}


/* ══════════════════════════════════════════════════════════════════════════
   IMPORT FLOWS
   ══════════════════════════════════════════════════════════════════════════ */

.cc-steps { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 16px; }
.cc-step {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--muted);
  font-size: 0.78rem;
  white-space: nowrap;
}
.cc-step[aria-current="step"] { background: var(--accent, #111827); border-color: var(--accent, #111827); color: #fff; }
.cc-step[data-done="true"] { color: var(--text); }
.cc-step__sep { color: var(--muted); font-size: 0.86rem; }

/* Duplicate resolution: one group, the rows it contains, and the decision. */
.cc-dupe {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--card);
  padding: 14px 16px;
  margin-bottom: 12px;
}
.cc-dupe__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.cc-dupe__key { font-size: 0.92rem; color: var(--text); word-break: break-word; }
.cc-dupe__body { display: flex; flex-direction: column; gap: 8px; }
/* One candidate row. The kept one is marked by fill, not by hue — the same
   monochrome rule the badges follow. */
.cc-dupe__opt {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--card);
  cursor: pointer;
  font-size: 0.86rem;
  line-height: 1.5;
  transition: border-color 140ms, background 140ms;
}
.cc-dupe__opt:hover { border-color: rgba(17, 24, 39, 0.24); }
.cc-dupe__opt[data-kept="true"] { background: var(--token-surface-secondary, #f9fafb); border-color: rgba(17, 24, 39, 0.28); }
.cc-dupe__conflict { font-size: 0.82rem; color: var(--muted); line-height: 1.55; }
.cc-dupe__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* Needs review: one row per record the parser could not decide on. */
.cc-review { display: flex; flex-direction: column; gap: 10px; }
.cc-review__item {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--card);
  padding: 12px 14px;
}
.cc-review__top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cc-review__row { font-size: 0.86rem; color: var(--text); }
/* The record exactly as the file had it, before any interpretation. */
.cc-review__raw {
  margin-top: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.5;
  word-break: break-word;
}
.cc-review__why { margin-top: 5px; font-size: 0.8rem; color: var(--muted); line-height: 1.5; }


/* ══════════════════════════════════════════════════════════════════════════
   VOICE SETUP AND THE VOICE SIMULATION TEST
   ══════════════════════════════════════════════════════════════════════════ */

/* The whole voice test lives in one card, so navigating away from it is
   unmistakably leaving the microphone behind. */
.cc-vtest { margin-top: 4px; }

/* One provider, one line: what it is, and whether it is configured. */
/* A provider row is a row of RECORD, not a control — but it is the height of
   one, and on a phone a finger lands on it while scrolling the list. 44px is
   the platform minimum and the height this row has always had. */
.cc-vrow {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 13px 0;
  min-height: 44px;
  border-bottom: 1px solid var(--line);
}
.cc-vrow:last-child { border-bottom: 0; }
.cc-vrow__name  { font-size: 0.9rem; color: var(--text); min-width: 0; }
.cc-vrow__state { display: inline-flex; align-items: center; gap: 7px; font-size: 0.82rem; color: var(--muted); flex: none; }

.cc-langs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.cc-chip {
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--token-surface-secondary, #f9fafb);
  color: var(--muted);
  font-size: 0.78rem;
}

/* ── WHAT THE REPLY IS COMING OUT OF ───────────────────────────────────────
   A two-button switch, because the browser cannot tell a handset's speaker
   from a pair of earphones and guessing would attenuate the wrong people. The
   pressed one is filled in; the other is an ordinary ghost button. */
.lb-console .cc-outmode { align-items: center; gap: 8px; margin-top: 10px; }
.lb-console .cc-outmode > .lb-hint { margin: 0; }
.lb-console .cc-outmode__btn[aria-pressed="true"] {
  background: var(--accent, #111827);
  border-color: var(--accent, #111827);
  color: #fff;
}

/* ── THE ONE INSTRUMENT ON THIS CARD ───────────────────────────────────────
   Folded away by default: it is here so a speakerphone fault can be read at
   the phone, not because Voice Setup is an instrument panel. Counters only. */
.lb-console .cc-vdiag { margin: 10px 0 4px; }
.lb-console .cc-vdiag > summary {
  cursor: pointer;
  font-size: 0.78rem;
  color: var(--muted, #6b7280);
  padding: 4px 0;
}
.lb-console .cc-vdiag__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.76rem;
  padding: 2px 0;
  border-bottom: 1px solid rgba(17, 24, 39, 0.05);
}
/* Numbers line up and never wrap mid-figure; the label takes the squeeze. */
.lb-console .cc-vdiag__row > b { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── THE A/B SWITCH, AND WHY IT LOOKS LIKE THE OUTPUT ONE ─────────────────
   Same two-button shape as the output switch, because it is the same kind
   of choice: two named alternatives, one of them currently in force. Making
   it look like a debug affordance would not make it safer — it is already
   folded inside a collapsed disclosure, which is what keeps it away from an
   operator taking a call — it would only make it harder to read at a phone
   held in one hand, which is the one place it has to be usable.

   Scoped to .lb-console like everything else here: these class names are
   unprefixed enough to collide with the shared dashboard sheets. */
.lb-console .cc-abmode { align-items: center; gap: 8px; margin: 6px 0 2px; flex-wrap: wrap; }
.lb-console .cc-abmode > .lb-hint { margin: 0; }
.lb-console .cc-abmode__btn[aria-pressed="true"] {
  background: var(--accent, #111827);
  border-color: var(--accent, #111827);
  color: #fff;
}
/* The note under the buttons carries the two things a person has to know
   before touching them: it is not retroactive, and it is not remembered. */
.lb-console .cc-abmode__note {
  margin: 0 0 8px;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--muted, #6b7280);
}

/* The live test. */
.cc-vlive { display: flex; align-items: center; gap: 12px; margin: 14px 0 10px; }
.cc-vmeter {
  flex: 1;
  height: 6px;
  border-radius: 999px;
  background: rgba(17, 24, 39, 0.08);
  overflow: hidden;
  min-width: 0;
}
/* The level bar's fill is the meter's only child — see the analyser loop,
   which writes firstElementChild.style.width. */
.cc-vmeter > i { display: block; height: 100%; width: 0; border-radius: inherit; background: var(--accent, #111827); transition: width 90ms linear; }

/* The running transcript SCROLLS INSIDE ITSELF. A live test appends a line
   every few seconds and does not stop while the operator is reading, so an
   unbounded list walks the card's own controls — Stop test among them — off
   the bottom of the screen. Capping it keeps the panel a fixed size no matter
   how long the conversation runs. */
.cc-vscript {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 12px;
  max-height: 280px;
  overflow-y: auto;
}
.cc-vline {
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--token-surface-secondary, #f9fafb);
  border: 1px solid var(--line);
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--text);
}
.cc-vline[data-who="assistant"] { background: var(--card); }
.cc-vline__who  { display: block; font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
/* `anywhere`, not `break-word`: recognised speech arrives as whatever the
   recogniser heard, and one long unbroken token — a spelled-out URL, a run of
   digits — is enough to push the whole card wider than the phone and give the
   page a horizontal scrollbar. */
.cc-vline__text { display: block; min-width: 0; overflow-wrap: anywhere; }
/* The language the recogniser reported for this line. A label, not a claim
   about the speaker. */
.cc-vline__lang { display: inline-block; margin-top: 5px; font-size: 0.7rem; color: var(--muted); }
/* The recogniser was not confident. Said with reduced opacity and a dashed
   edge rather than a colour, because "unclear" is a degree of certainty and
   not an error. */
.cc-vline--unclear { border-style: dashed; opacity: 0.82; }

/* ── STILL BEING SAID ────────────────────────────────────────────────────
   The recogniser's current guess at an utterance that is not finished. It has
   to look DIFFERENT from a committed line, because the whole point of showing
   it is that it may still change — a guess drawn identically to the record
   would be the panel claiming to have heard something it is still working
   out. Dimmed and dashed, the same vocabulary --unclear already uses for the
   same reason: this is a degree of certainty, not an error.

   The caret is the only motion on this row, and it is what says "more is
   coming" without a spinner or a percentage nobody can verify. */
.cc-vline--partial {
  border-style: dashed;
  opacity: 0.72;
  background: transparent;
}
.cc-vline--partial .cc-vline__text::after {
  content: '';
  display: inline-block;
  width: 2px; height: 0.95em;
  margin-left: 3px;
  vertical-align: text-bottom;
  background: currentColor;
  animation: cc-vcaret 1s steps(2, start) infinite;
}
@keyframes cc-vcaret { 50% { opacity: 0; } }

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE

   The shell's breakpoints are the dashboard's and are not repeated here: the
   sidebar becomes a drawer at ≤720px, the topbar appears, the cream canvas
   switches gradient — all upstream. Below is only what this product's own
   content needs at each width.
   ══════════════════════════════════════════════════════════════════════════ */

/* Below this the content column is too narrow to give four tiles a readable
   label, so they go two-up and the two-column region stacks. Four cramped
   tiles are worse than two legible rows of two. */
@media (max-width: 1180px) {
  .cc-cols  { grid-template-columns: minmax(0, 1fr); }
  .cc-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .cc-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  .cc-event { grid-template-columns: 7px 70px minmax(0, 1fr); gap: 9px; }
  .cc-actions > .btn-dark,
  .cc-actions > .btn-ghost,
  .cc-actions > .btn-danger { flex: 1 1 auto; min-height: 44px; justify-content: center; }
  .cc-vlive { flex-wrap: wrap; }
  /* WRAP, never truncate. The right-hand cell of a provider row carries the
     server's own words about why a provider is unconfigured; an ellipsis
     there hides the reason and leaves a row that says only "not configured". */
  .cc-vrow { flex-wrap: wrap; }
  .cc-vrow__state { margin-left: 0; }
  /* The preview-sentence chips are real buttons. */
  .cc-langs > .btn-ghost,
  .cc-langs > .btn-dark { min-height: 44px; }
}

@media (max-width: 420px) {
  .cc-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cc-stat { gap: 11px; }
  .cc-stat__icon { width: 34px; height: 34px; }
}

/* Coarse pointers get the platform's 44px minimum on anything tappable —
   including the small buttons, which are sized for a mouse. */
@media (pointer: coarse) {
  .btn-sm { min-height: 44px !important; }
  .btn-icon { width: 44px; height: 44px; }
  .nav-item { min-height: 44px; }
}

/* The same 44px for the Campaign controls a touch screen reaches that the
   rule above does not: a card menu's items (40px for a mouse), the shared
   drawer's footer buttons (Keep changes, Remove from Campaign, Pause,
   Continue editing — plain buttons, not .btn-sm), and the editor's two
   text-link buttons, Open Agent and Leads Library. The links stay links:
   the box grows, the text and its underline do not. */
@media (pointer: coarse) {
  .lb-console .cc-camp-menu__item { min-height: 44px; }
  .lb-console .lb-drawer__foot > .btn-ghost,
  .lb-console .lb-drawer__foot > .btn-dark,
  .lb-console .lb-drawer__foot > .btn-danger,
  .lb-console .lb-drawer__foot > .btn-quiet { min-height: 44px; }
  .lb-console .cc-linkish[data-campaign-edit-open-agent],
  .lb-console .cc-linkish[data-campaign-edit-open-leads] {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cc-shake { animation: none !important; }
  .cc-vmeter > i { transition: none !important; }
  /* The caret stops blinking and stays visible. Removing it entirely would
     take away the only thing on the row that says the text is not final. */
  .cc-vline--partial .cc-vline__text::after { animation: none !important; }
}

/* Windows High Contrast: the browser replaces colour, so borders have to come
   back or every card, notice and metric collapses into one flat plane. */
@media (forced-colors: active) {
  .cc-mode,
  .cc-metric,
  .cc-step,
  .cc-sim,
  .cc-chip,
  .cc-vline,
  .cc-dupe,
  .cc-review__item { border: 1px solid CanvasText; }
  .cc-vmeter > i { background: CanvasText; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE BUSY BUTTON, THE TOAST, AND THE END OF THE COMPRESSED COLUMN
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The loading orb ─────────────────────────────────────────────────────
   <cc-loading-orb> (loading-orb.js) draws the vendor's breathing orb on a
   canvas of exactly its preset size. Sized here as well, so the box is
   reserved from the first paint, before the element's script has run. */
cc-loading-orb {
  display: inline-block;
  flex: none;
  width: 64px;
  height: 64px;
}
cc-loading-orb[size="20"] {
  width: 20px;
  height: 20px;
}

/* ── A button that is doing something ────────────────────────────────────
   The 20px orb in the slot the spinner always had: 0.85em square, tracking
   the button's own text, and 0.5em from the label. The canvas is 20px; the
   negative margins give back what it adds over the slot, so a busy button —
   and a busy status line, below — is exactly the size it was and its words
   do not move. The canvas is transparent past its dots. */
.cc-spin {
  display: inline-block;
  flex: none;
  margin: calc((0.85em - 20px) / 2);
  margin-right: calc(0.5em + (0.85em - 20px) / 2);
  vertical-align: -0.1em;
}

/* A busy button keeps its size so the row does not reflow under the cursor. */
button[aria-busy="true"] { cursor: progress; }

/* ── The confirmation, where the eye already is ──────────────────────────
   Fixed, so it does not depend on where the page is scrolled — the whole
   point is that actions no longer move the page, so a confirmation rendered
   at the top of the document would be invisible exactly when it is needed. */
.cc-toasts {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(360px, calc(100vw - 36px));
  pointer-events: none;
}
/* The card and its status icon are the console's one notification look —
   see NOTIFICATIONS at the end of this sheet, which gives the toast its icon
   from data-tone. Only the floating shadow is the toast's own. */
.cc-toast {
  position: relative;
  padding: 12px 16px 12px 44px;
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: 14px;
  background: var(--token-surface-primary, #fff);
  color: var(--text);
  font-size: 0.86rem;
  line-height: 1.4;
  box-shadow: 0 12px 32px -10px rgba(16, 24, 40, 0.22), 0 2px 6px rgba(16, 24, 40, 0.06);
  /* Long provider errors arrive here verbatim. */
  overflow-wrap: anywhere;
  animation: cc-toast-in 0.16s ease-out;
}
@keyframes cc-toast-in { from { opacity: 0; transform: translateY(6px); } }

/* Reduced motion and the orb: the vendor's own rule, in loading-orb.js — one
   still frame, no animation. */
@media (prefers-reduced-motion: reduce) {
  .cc-toast { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   NOTIFICATIONS — ONE LOOK (owner, 8 October 2026)
   Every notice, warning, refusal and toast in the console is the same white
   card: a soft hairline, a soft shadow, a rounded corner and one coloured
   status icon on the left. Appearance only — where a notice sits, when it
   shows, whether it can be dismissed and every word in it are unchanged.

   The shared .lb-notice is the Labbie dashboard's too, so all of this is
   scoped to .lb-console and frontend/assets is not touched.

   The icon is a ::before drawn with a mask, so no markup or script changes
   to carry it, and it is absolutely placed so it works whatever display a
   notice has been given (flex, block, a prose section). Its glyphs follow
   the console sprite's i-info / i-warn / i-check, filled, with the mark cut
   out so the card shows through it.

   Which icon: .lb-notice--error and the notices only ever written with a
   failure are errors; refusals, the unsaved draft and the storage warning are
   warnings; a stale channel and a reconnect are connection notices; every
   other notice is information. A toast takes its icon from its data-tone.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console {
  --cc-note-success: #1f9d55;
  --cc-note-warning: #dd8e06;
  --cc-note-error: #b91c1c;
  --cc-note-info: #2f6fe4;
  --cc-glyph-info: url("data:image/svg+xml,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='m'%3E%3Crect width='20' height='20' fill='white'/%3E%3Ccircle cx='10' cy='5.9' r='1.3'/%3E%3Cpath d='M10 9.1v5.3' stroke='black' stroke-width='2.1' stroke-linecap='round'/%3E%3C/mask%3E%3Ccircle cx='10' cy='10' r='10' mask='url(%23m)'/%3E%3C/svg%3E");
  --cc-glyph-success: url("data:image/svg+xml,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='m'%3E%3Crect width='20' height='20' fill='white'/%3E%3Cpath d='M5.8 10.4l2.8 2.8 5.6-5.8' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/mask%3E%3Ccircle cx='10' cy='10' r='10' mask='url(%23m)'/%3E%3C/svg%3E");
  --cc-glyph-warning: url("data:image/svg+xml,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='m'%3E%3Crect width='20' height='20' fill='white'/%3E%3Cpath d='M10 7.7v4.4' stroke='black' stroke-width='2.1' stroke-linecap='round'/%3E%3Ccircle cx='10' cy='14.9' r='1.25'/%3E%3C/mask%3E%3Cpath d='M10 2.6 18.4 17.3H1.6Z' stroke='black' stroke-width='2.4' stroke-linejoin='round' mask='url(%23m)'/%3E%3C/svg%3E");
  --cc-glyph-error: url("data:image/svg+xml,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='m'%3E%3Crect width='20' height='20' fill='white'/%3E%3Cpath d='M10 5.6v5.3' stroke='black' stroke-width='2.1' stroke-linecap='round'/%3E%3Ccircle cx='10' cy='14.1' r='1.25'/%3E%3C/mask%3E%3Cpath d='M6.6 1.2h6.8l5.4 5.4v6.8l-5.4 5.4H6.6l-5.4-5.4V6.6Z' stroke='black' stroke-width='1.6' stroke-linejoin='round' mask='url(%23m)'/%3E%3C/svg%3E");
  --cc-glyph-connection: url("data:image/svg+xml,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='m'%3E%3Crect width='20' height='20' fill='white'/%3E%3Cpath d='M4.9 8.6a7.3 7.3 0 0 1 10.2 0M7.1 11a4.1 4.1 0 0 1 5.8 0' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'/%3E%3Ccircle cx='10' cy='13.9' r='1.2'/%3E%3C/mask%3E%3Ccircle cx='10' cy='10' r='10' mask='url(%23m)'/%3E%3C/svg%3E");
}

/* ── A SCREEN THAT IS BEING READ ─────────────────────────────────────────
   pageLoading() in coldcalling.js: one orb and one word, centred in whatever
   is left of the content area — the page panel under its heading, the setup
   card's body between its header and footer, or the whole window while the
   session is still being asked. It replaces the page-level "Loading…" lines
   only; a button, an upload or an analysis keeps its own truthful state.

   Nothing here waits: it is drawn with the request and replaced by its
   answer. The orb is the console's one loading mark (loading-orb.js), the
   same one a busy button carries. */
.cc-loading {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  width: 100%;
  /* Phone width only — every other placement below replaces it. The page
     there scrolls as a whole, so there is no box to fill: this is the
     viewport less the topbar, .main's padding and a page heading (325px
     measured at 375px wide), which puts the ring on the middle of what is
     left. 60vh is the fallback where svh is unknown. */
  min-height: 60vh;
  min-height: max(240px, calc(100svh - 325px));
  padding: 24px;
  text-align: center;
}
/* The 64px orb in the 56px slot the ring held: its dots never reach the
   canvas's outer 6px, so the negative margins keep the column — and
   "Loading..." under it — exactly where it was. */
.cc-loading__orb {
  display: block;
  margin: -4px 0;
}
.cc-loading__text {
  margin: 0;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--text, #111827);
}
/* The page panel is a column that fills .main on a desktop, so the loader
   takes the height the heading leaves and centres inside it. */
@media (min-width: 901px) {
  .lb-console #viewPanel > .cc-loading { flex: 1 0 auto; min-height: 0; }
}
/* Inside the setup card the body row already holds the slack between the
   header and the footer; the loader fills that row. Step 2's loader has no
   header of its own, so its body spans the whole card. */
.lb-console .cc-onb__body:has(> .cc-loading) {
  display: flex;
  flex-direction: column;
  /* The body's own padding is 4px over 28px (a form's rhythm); equal halves
     here, same total, so the loader sits on the true middle of the row. */
  padding-top: 16px;
  padding-bottom: 16px;
}
.lb-console .cc-onb__body:has(> .cc-loading) > .cc-loading { flex: 1 0 auto; min-height: 0; }
.lb-console .cc-onb > .cc-onb__body:only-child:has(> .cc-loading) { grid-row: 1 / -1; }
/* Startup: the whole app is hidden until the session answers. The loader
   stands in for it across the window and goes the instant either the app
   or the sign-in card is shown — no script, no timer. */
.cc-boot {
  position: fixed;
  inset: 0;
  display: flex;
  background: var(--card, #fff);
}
.cc-boot > .cc-loading { flex: 1 1 auto; min-height: 0; }
.cc-gate:not([hidden]) ~ .cc-boot,
.cc-app[data-ready="true"] ~ .cc-boot { display: none; }

/* ── SETTINGS — two quiet, border-defined surfaces ─────────────────────── */
.lb-console .cc-settings-head { margin-bottom: 20px; }
.lb-console .cc-settings-card {
  padding: 20px 22px !important;
  border: 1px solid #e7e7e7 !important;
  border-radius: 14px !important;
  background: var(--card, #fff) !important;
  box-shadow: none !important;
  margin-bottom: 16px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.lb-console .cc-settings-card:hover {
  border-color: #e7e7e7 !important;
  background: var(--card, #fff) !important;
  box-shadow: none !important;
}
/* Keep the Account flows intact while removing their Settings card from view. */
.lb-console .cc-settings-account { display: none; }
.lb-console .cc-settings-card .section-head { margin-bottom: 16px; }
.lb-console .cc-settings-card .section-title {
  margin: 0;
  font-family: var(--cc-font-ui, var(--font-ui)) !important;
  font-size: 0.94rem !important;
  font-weight: 600 !important;
  line-height: 1.35;
  letter-spacing: -0.01em !important;
  color: var(--text);
}

/* ── SETTINGS · PROFILE — avatar left, account fields right ────────────── */
.lb-console .cc-settings__profile-grid {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  align-items: start;
  gap: 26px;
  min-width: 0;
}
.lb-console .cc-settings__photo-wrap { align-self: start; }
.lb-console .cc-settings__photo {
  position: relative;
  display: block;
  width: 104px;
  height: 104px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.lb-console .cc-settings__photo .avatar-lg {
  width: 104px;
  height: 104px;
  background: var(--token-avatar-bg, #111827);
  font-size: 1.5rem;
  transition: filter 120ms ease, opacity 120ms ease;
}
.lb-console .cc-settings__photo .avatar-lg.has-image { background: none; }
.lb-console .cc-settings__photo:hover .avatar-lg { filter: brightness(0.96); }
.lb-console .cc-settings__photo:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px #d4d4d4;
}
.lb-console .cc-settings__photo[aria-busy="true"] { cursor: pointer; }
.lb-console .cc-settings__fields { min-width: 0; }
.lb-console .cc-settings__field { margin-bottom: 12px; }
.lb-console .cc-settings__field .lb-label {
  display: block;
  margin: 0 0 6px;
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--text);
}
.lb-console .cc-settings__field .lb-input {
  width: 100%;
  min-width: 0;
  height: 42px;
  box-sizing: border-box;
  padding: 0 12px;
  border: 1px solid #dedede;
  border-radius: 8px;
  background: var(--card, #fff);
  box-shadow: none;
  color: var(--text);
  font-size: 0.86rem;
}
.lb-console .cc-settings__field .lb-input:focus {
  border-color: #9a9a9a;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.06);
}
.lb-console .cc-settings__field .cc-settings__readonly {
  background: var(--token-surface-secondary, #f9fafb);
  color: #555;
  cursor: default;
}
.lb-console .cc-settings__dob { appearance: none; }
.lb-console .cc-settings__dob::-webkit-calendar-picker-indicator {
  display: none;
  -webkit-appearance: none;
}
.lb-console .cc-settings__save-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 2px;
}
.lb-console .cc-settings__save-row[hidden] { display: none; }
.lb-console .cc-settings__fields > .lb-notice { margin: 10px 0 0; }

/* ── DASHBOARD · PROFILE ────────────────────────────────────────────────── */
.lb-console #dashboardPanel .cc-settings-card.cc-settings-profile--ready,
.lb-console #dashboardPanel .cc-settings-card.cc-settings-profile--ready:hover {
  box-sizing: border-box;
  width: 100%;
  min-height: clamp(190px, 17vw, 250px);
  display: flex;
  flex-direction: column;
  margin-bottom: 16px;
  padding: 0 !important;
  overflow: hidden;
  border: 6px solid #d4d4d4 !important;
  border-radius: 20px !important;
  background: linear-gradient(180deg,
    #000000 0%, #1c1c1c 10%, #484848 20%, #7b7a7b 30%,
    #a8a7a8 40%, #cac9ca 50%, #dddddd 62%, #e3e3e3 76%,
    #e5e5e5 100%) !important;
  box-shadow: none !important;
}
.lb-console #dashboardPanel .cc-settings-profile .cc-card__body {
  box-sizing: border-box;
  display: flex;
  flex: 1 0 auto;
  width: 100%;
  min-height: 0;
}
.lb-console #dashboardPanel .cc-settings__profile-content {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex: 1 0 auto;
  flex-direction: column;
  width: 100%;
  min-height: 0;
  padding: 14px clamp(20px, 4.2%, 58px) 13px;
}
.lb-console #dashboardPanel .cc-settings__cover-picker {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  overflow: hidden;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.lb-console #dashboardPanel .cc-settings__cover-picker.has-cover { cursor: grab; touch-action: none; }
.lb-console #dashboardPanel .cc-settings__cover-picker.has-cover { background: #e5e5e5; }
.lb-console #dashboardPanel .cc-settings__cover-picker.is-dragging { cursor: grabbing; }
.lb-console #dashboardPanel .cc-settings__cover-picker[aria-busy="true"] { opacity: 1 !important; cursor: pointer; }
.lb-console #dashboardPanel .cc-settings__cover-picker.has-cover[aria-busy="true"] { cursor: grab; }
.lb-console #dashboardPanel .cc-settings__cover-picker.has-cover::before {
  position: absolute;
  z-index: 1;
  inset: 0;
  content: '';
  background: linear-gradient(90deg,
    rgba(229,229,229,.35) 0%,
    rgba(229,229,229,.30) 15%,
    rgba(229,229,229,.23) 30%,
    rgba(229,229,229,.14) 48%,
    rgba(229,229,229,.06) 62%,
    rgba(229,229,229,0) 78%);
  pointer-events: none;
}
.lb-console #dashboardPanel .cc-settings__cover-picker.has-cover::after {
  position: absolute;
  inset: 0;
  content: '';
  background: linear-gradient(180deg,
    rgba(229,229,229,0) 0%,
    rgba(229,229,229,.01) 10%,
    rgba(229,229,229,.025) 20%,
    rgba(229,229,229,.045) 30%,
    rgba(229,229,229,.075) 40%,
    rgba(229,229,229,.115) 50%,
    rgba(229,229,229,.165) 60%,
    rgba(229,229,229,.225) 70%,
    rgba(229,229,229,.295) 80%,
    rgba(229,229,229,.375) 90%,
    rgba(229,229,229,.46) 100%);
  pointer-events: none;
}
.lb-console #dashboardPanel .cc-settings__cover-image {
  position: absolute;
  inset: -2%;
  display: block;
  width: 104%;
  height: 104%;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  opacity: 1;
  transition: opacity 420ms cubic-bezier(.22, 1, .36, 1),
    object-position 320ms cubic-bezier(.22, 1, .36, 1);
}
.lb-console #dashboardPanel .cc-settings__cover-image.is-entering { opacity: 0; }
.lb-console #dashboardPanel .cc-settings__cover-image.is-repositioning {
  max-width: none;
  object-fit: fill;
  transition: none;
  will-change: transform;
}
.lb-console #dashboardPanel .cc-settings__cover-picker.is-dragging .cc-settings__cover-image {
  transition: none;
}
.lb-console #dashboardPanel .cc-settings__cover-image[hidden] { display: none; }
.lb-console #dashboardPanel .cc-settings__photo-wrap,
.lb-console #dashboardPanel .cc-settings__identity { position: relative; z-index: 1; pointer-events: none; }
.lb-console #dashboardPanel .cc-settings__photo,
.lb-console #dashboardPanel .cc-settings__name,
.lb-console #dashboardPanel .cc-settings__birthday-display,
.lb-console #dashboardPanel .cc-settings__dob,
.lb-console #dashboardPanel .cc-settings__save-row { pointer-events: auto; }
.lb-console #dashboardPanel .cc-settings__photo-wrap { flex: 0 0 auto; margin-top: auto; }
.lb-console #dashboardPanel .cc-settings__photo {
  position: relative;
  display: block;
  width: clamp(72px, 6.8vw, 100px);
  height: clamp(72px, 6.8vw, 100px);
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.lb-console #dashboardPanel .cc-settings__photo { touch-action: none; }
.lb-console #dashboardPanel .cc-settings__photo.is-dragging { cursor: grabbing; }
.lb-console #dashboardPanel .cc-settings__photo .avatar-lg {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 50%;
  background: #d9d9d9;
  transition: filter 320ms cubic-bezier(.22, 1, .36, 1),
    opacity 380ms cubic-bezier(.22, 1, .36, 1),
    transform 380ms cubic-bezier(.22, 1, .36, 1);
}
.lb-console #dashboardPanel .cc-settings__photo .avatar-lg img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  transition: opacity 420ms cubic-bezier(.22, 1, .36, 1),
    object-position 320ms cubic-bezier(.22, 1, .36, 1);
}
.lb-console #dashboardPanel .cc-settings__photo .avatar-lg img.is-entering { opacity: 0; }
.lb-console #dashboardPanel .cc-settings__photo.is-dragging .avatar-lg img { transition: none; }
.lb-console #dashboardPanel .cc-settings__photo:hover .avatar-lg { filter: brightness(0.96); }
.lb-console #dashboardPanel .cc-settings__photo:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px #d4d4d4;
}
.lb-console #dashboardPanel .cc-settings__identity { display: flex; flex: 0 0 auto; flex-direction: column; min-width: 0; margin-top: 6px; }
.lb-console #dashboardPanel .cc-settings__name {
  box-sizing: border-box;
  display: block;
  width: min(100%, 620px);
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 3px;
  outline: 0;
  background: transparent;
  color: #000;
  font: 600 clamp(17px, 1.6vw, 24px)/1.2 "Inter Profile", var(--cc-font-ui, var(--font-ui));
  text-overflow: ellipsis;
}
.lb-console #dashboardPanel .cc-settings__name:focus-visible,
.lb-console #dashboardPanel .cc-settings__birthday-display:focus-visible { outline: 2px solid #333; outline-offset: 3px; }
.lb-console #dashboardPanel .cc-settings__birthday { margin-top: 2px; }
.lb-console #dashboardPanel .cc-settings__birthday-display,
.lb-console #dashboardPanel .cc-settings__dob {
  display: block;
  max-width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: #000;
  font: 600 clamp(13px, 1.2vw, 17px)/1.25 "Inter Profile", var(--cc-font-ui, var(--font-ui));
  text-align: left;
  cursor: pointer;
}
.lb-console #dashboardPanel .cc-settings__name,
.lb-console #dashboardPanel .cc-settings__birthday-display {
  background-image: linear-gradient(180deg, #000000 1%, #242424 100%);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  caret-color: #000;
}
.lb-console #dashboardPanel .cc-settings__dob[hidden],
.lb-console #dashboardPanel .cc-settings__birthday-display[hidden] { display: none; }
.lb-console #dashboardPanel .cc-settings__footer {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: 12px;
  width: 100%;
  margin-top: clamp(9px, 1.2vw, 16px);
  color: rgba(0, 0, 0, 0.72);
  font: 700 clamp(11px, .9vw, 13px)/1.3 "Inter Profile", var(--cc-font-ui, var(--font-ui));
}
.lb-console #dashboardPanel .cc-settings__email { display: flex; align-items: center; gap: 5px; min-width: 0; padding-left: 2px; }
.lb-console #dashboardPanel .cc-settings__email svg { flex: 0 0 auto; width: 11.47px; height: 11.47px; color: inherit; }
.lb-console #dashboardPanel .cc-settings__email span { overflow-wrap: anywhere; }
.lb-console #dashboardPanel .cc-settings__save-row {
  margin-top: 12px;
}
.lb-console #dashboardPanel .cc-settings__save-row[hidden] { display: none; }
.lb-console #dashboardPanel .cc-settings__profile-content > .lb-notice { margin: 8px 0 0; }
.lb-console #dashboardPanel .cc-settings__profile-content--guest { justify-content: center; }
.lb-console #dashboardPanel .cc-settings__guest-copy {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  color: #111;
  text-align: center;
}
.lb-console #dashboardPanel .cc-settings__guest-copy span {
  /* The product's Inter mapping serves the Light face for requested 400. */
  font: 400 clamp(18px, 1.8vw, 28px)/1.3 var(--cc-font-ui, var(--font-ui));
}
.lb-console #dashboardPanel .cc-settings__guest-signin {
  min-height: 36px;
  padding: 8px 24px;
  border: 0;
  border-radius: 999px;
  background: #000;
  color: #fff;
  font: 400 clamp(14px, 1.1vw, 17px)/1 "Inter Intro", var(--cc-font-ui, var(--font-ui));
  cursor: pointer;
  transition: background 320ms cubic-bezier(.22, 1, .36, 1), transform 320ms cubic-bezier(.22, 1, .36, 1);
}
.lb-console #dashboardPanel .cc-settings__guest-signin:hover { background: #222; transform: translateY(-1px); }
.lb-console #dashboardPanel .cc-settings__guest-signin:focus-visible { outline: 2px solid #111; outline-offset: 2px; }

/* ── SETTINGS · ACCOUNT — one row per action ─────────────────────────────
   Each route is one full-width row: restrained icon, copy, then chevron. */
.lb-console .cc-acctrow {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 16px;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  margin: 0;
  padding: 11px 0;
  border: 0;
  border-bottom: 1px solid #ececec;
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  appearance: none;
  cursor: default;
}
.lb-console .cc-acctrow:is(button, a) { cursor: pointer; }
.lb-console .cc-acctrow:is(button, a):hover {
  background: var(--token-surface-secondary, #f9fafb);
}
.lb-console .cc-acctrow:focus-visible {
  outline: 2px solid #b7b7b7;
  outline-offset: 2px;
}
.lb-console .cc-acctrow:last-child { border-bottom: 0; }
.lb-console .cc-acctrow__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  background: var(--token-surface-secondary, #f9fafb);
  color: #4b4b4b;
}
.lb-console .cc-acctrow__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.lb-console .cc-acctrow__label {
  margin: 0;
  font-size: 0.86rem;
  font-weight: 500;
  line-height: 1.35;
  color: var(--text);
}
.lb-console .cc-acctrow__hint {
  margin-top: 2px;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--muted);
}
.lb-console .cc-acctrow__chevron { color: #8b8b8b; }

@media (max-width: 700px) {
  .lb-console .cc-settings-card { padding: 18px 16px !important; }
  .lb-console .cc-settings__profile-grid {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 20px;
  }
  .lb-console .cc-settings__photo,
  .lb-console .cc-settings__photo .avatar-lg { width: 88px; height: 88px; }
  .lb-console .cc-settings__save-row { justify-content: stretch; }
  .lb-console .cc-settings__save-row .btn-dark { width: 100%; justify-content: center; }
}

@media (max-width: 1024px) {
  .lb-console #dashboardPanel .cc-settings-card.cc-settings-profile--ready {
    min-height: 210px;
  }
  .lb-console #dashboardPanel .cc-settings__profile-content {
    min-height: 0;
    padding: 14px 28px 13px;
  }
  .lb-console #dashboardPanel .cc-settings__photo { width: 78px; height: 78px; }
}

@media (max-width: 700px) {
  .lb-console #dashboardPanel .cc-settings-card.cc-settings-profile--ready {
    min-height: 235px;
  }
  .lb-console #dashboardPanel .cc-settings__profile-content { min-height: 0; padding: 14px 20px 13px; }
  .lb-console #dashboardPanel .cc-settings__photo { width: 78px; height: 78px; }
}

@media (max-width: 480px) {
  .lb-console #dashboardPanel .cc-settings-card.cc-settings-profile--ready { min-height: 245px; }
  .lb-console #dashboardPanel .cc-settings__profile-content {
    min-height: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .lb-console #dashboardPanel .cc-settings__cover-image,
  .lb-console #dashboardPanel .cc-settings__photo .avatar-lg img,
  .lb-console #dashboardPanel .cc-settings__photo .avatar-lg,
  .lb-console #dashboardPanel .cc-settings__guest-signin { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   WHY TEXT USED TO STACK ONE CHARACTER PER LINE
   ══════════════════════════════════════════════════════════════════════════

   The preview log put a line like

     peak 0.0071 · rms 0.0034 · 12% non-zero · 1.2s @ 24000 Hz (requested, not confirmed)

   into a grid track that had been sized for a timestamp and a sentence. A grid
   or flex child's default min-width is auto, which means "at least as wide as
   my longest unbreakable run" — so the track refused to shrink, took the space
   from its neighbour, and the neighbour's words were left with a column a few
   characters wide. That is what produced the vertical letters, the overlap and
   the text escaping its card.

   THE CAUSE IS THE DEFAULT, NOT THAT ONE BLOCK. Recent previews is gone, but
   any future row of dense text in a grid track would do the same thing, so the
   fix is here rather than in the markup that happened to trigger it:

     · min-width: 0 wherever a grid or flex child holds text, so a track may
       actually shrink to its share;
     · overflow-wrap: anywhere on the text itself, so a long unbroken run
       breaks instead of setting the floor;
     · minmax(0, 1fr) on the tracks, so 1fr means a share rather than a
       minimum.

   Applied to the containers rather than to a class the old block used, so it
   holds for whatever is put in them next. */

.cc-event,
.cc-events,
.cc-metrics,
.cc-langs,
.cc-stats,
.cc-cols { min-width: 0; }

.cc-event > *,
.cc-metric,
.cc-stat,
.cc-col,
.cc-card__body > * { min-width: 0; }

/* The text-bearing leaves. A word longer than its column breaks rather than
   pushing the column open. */
.cc-event__what,
.cc-event__time,
.cc-metric__value,
.cc-metric__label,
.cc-was,
.cc-now,
.lb-row__sub,
.lb-row__title,
.lb-hint,
.lb-notice,
.cc-chip {
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: normal;
}

/* A metric's value is the commonest long unbreakable run on the page. */
.cc-metric__value { white-space: normal; }

/* Nothing escapes its card. A card that would overflow scrolls its own
   contents instead of painting over the one beside it. */
.section-card,
.cc-card__body { overflow-wrap: anywhere; }

/* ══════════════════════════════════════════════════════════════════════════
   THE SEGMENTED CONTROL — AI BRAIN

   TWO OPTIONS, ONE OF WHICH IS TRUE. A pair of buttons in a single bordered
   track rather than a pair of separate buttons: separate buttons read as two
   independent actions, and this is one setting with two positions. The
   pressed one is filled, so which brain is running is answerable at a glance
   from across a desk.

   `aria-pressed` carries the state rather than a class, so the styling and
   what a screen reader announces cannot drift apart — there is only one
   attribute and both read it.
   ══════════════════════════════════════════════════════════════════════════ */

.lb-console .cc-seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--token-surface-secondary, #f9fafb);
  max-width: 100%;
  flex-wrap: wrap;
}

.lb-console .cc-seg__btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  padding: 7px 18px;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.84rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  transition: background-color 120ms ease, color 120ms ease;
}

.lb-console .cc-seg__btn[aria-pressed="true"] {
  background: var(--token-surface, #fff);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

/* NOT MERELY GREYED. A disabled option here means one of two specific things
   — a test is running, or that provider has no key — and both are stated in
   words beneath the control. The cursor says "this is not clickable" so the
   operator looks for the sentence rather than clicking again. */
.lb-console .cc-seg__btn[disabled] {
  cursor: not-allowed;
  opacity: 0.45;
}

@media (hover: hover) {
  .lb-console .cc-seg__btn:not([disabled]):not([aria-pressed="true"]):hover {
    color: var(--text);
    background: var(--token-surface, #fff);
  }
}

.lb-console .cc-seg__btn:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* ── THE TURN-BY-TURN TABLE ───────────────────────────────────────────────
   Eight columns of which four are prose, so it is wide by nature. It scrolls
   inside its own wrapper (.lb-tablewrap already does that) and the text cells
   are capped rather than allowed to set the table's width — a single long
   transcript would otherwise push the four numeric columns off the screen,
   and those are half of what the table is read for. */
.lb-console .cc-diag { font-size: 0.8rem; }

.lb-console .cc-diag__text {
  max-width: 22ch;
  min-width: 12ch;
  white-space: normal;
  overflow-wrap: anywhere;
  vertical-align: top;
}

@media (min-width: 1100px) {
  .lb-console .cc-diag__text { max-width: 30ch; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE CREATE-VOICE DIALOG

   A DIALOG ABOVE THE DASHBOARD, not a screen instead of it.

   The first version filled the viewport edge to edge on the argument that
   choosing recordings deserves the operator's whole attention. It does — but a
   surface that occupies every pixel is not read as a dialog, it is read as
   having navigated somewhere, and the operator loses the thread of where they
   were and what they will come back to. The dashboard stays visible behind,
   dimmed and softly blurred: still there, plainly not the thing being used.

   BOUNDED IN BOTH DIRECTIONS. A max-width so it is a card rather than a page,
   and a max-height so a long file list scrolls inside the dialog instead of
   growing it past the bottom of the screen. At 560px and below it becomes a
   bottom sheet that fills the width, because on a phone a centred card with
   margins is just a smaller phone.

   Above the shared drawer (z-index 2900/2950), because both can never be
   usefully open at once and the one the operator just asked for wins.
   ══════════════════════════════════════════════════════════════════════════ */

/* The page behind cannot scroll while the dialog is open. Only load-bearing
   at or below 720px, where the shared sheet moves the scrolling from .main to
   the body; above it the shell already sets html,body { overflow: hidden }. */
body.cc-modal-open { overflow: hidden; }

.cc-modal {
  position: fixed; inset: 0; z-index: 3000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.cc-modal[hidden] { display: none; }

/* The scrim carries the SHARED .lb-drawer-backdrop, which already owns the
   colour, the blur and the fade. All that is added here is the stacking
   context this dialog needs — the shared class pins itself at z-index 2900,
   which is below the dialog's own panel. */
.cc-modal__scrim { z-index: 0; }

.cc-modal__panel {
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  /* A CARD, not a page. Wide enough for the drop zone to breathe, short
     enough that the dashboard is visibly still there above and below it. */
  width: 100%;
  max-width: 640px;
  max-height: min(760px, calc(100vh - 48px));
  background: var(--card, #fff);
  border-radius: var(--radius-xl, 16px);
  border: 1px solid var(--line, #e5e7eb);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 2px 8px rgba(0, 0, 0, 0.06);
  /* The panel scrolls, never the page behind it. */
  overflow: hidden;
}

.cc-modal__head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line, #e5e7eb);
  flex: none;
}
.cc-modal__title {
  font-size: 1.05rem; font-weight: 500; letter-spacing: -0.02em;
  color: var(--text, #111827);
  min-width: 0;
}
.cc-modal__x { flex: none; }

/* THE PANEL IS THE COLUMN. A second max-width in here would narrow an
   already-narrow card twice and leave the drop zone stranded in the middle of
   its own dialog. What this owns is the scroll: a long file list scrolls
   HERE, inside the card, so the head and the actions stay put. */
.cc-modal__body {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 14px;
  padding: 22px 22px 24px;
}

.cc-modal__foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  padding: 16px 24px;
  border-top: 1px solid var(--line, #e5e7eb);
  flex: none;
}

.cc-modal__warn {
  font-size: 0.85rem; line-height: 1.55;
  color: var(--muted, #6b7280);
  text-align: center;
}

.cc-modal__status {
  font-size: 0.86rem; color: var(--muted, #6b7280);
  min-height: 1.2em; text-align: center;
}
.cc-modal__status[data-tone="error"] { color: #b91c1c; }
/* The busy mark is the SHARED one (busyOrb(), .cc-spin), in the same 0.85em
   slot — this only keeps the status line's own 8px beside the words. */
.cc-modal__status .cc-spin {
  margin-right: calc(8px + (0.85em - 20px) / 2);
  vertical-align: -1px;
}

/* ── THE DROP ZONE ─────────────────────────────────────────────────────────
   Replaces .lb-upload here. The shared component is a 30px-padded dashed box
   built to sit inside a card among other controls; this is the only thing on
   its screen, so it is sized and weighted as a destination rather than as a
   field. Left alone in the shared sheet, because the imports screen still uses
   it exactly as it was built to be used.

   THE FILE INPUT IS HIDDEN HERE, which it never was before: the raw
   <input type="file"> was rendering its own grey "Choose files / no file
   chosen" widget in the middle of the styled zone. Hidden the accessible way —
   it keeps its size and stays focusable, so the label's keyboard behaviour and
   the focus ring below still work. */
.cc-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 20px;
  min-height: 300px;
  padding: 48px 28px;
  text-align: center;
  border-radius: 18px;
  border: 1px solid transparent;
  background: var(--token-surface-secondary, #f6f6f7);
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
@media (hover: hover) {
  .cc-drop:hover { background: rgba(17, 24, 39, 0.05); }
}
.cc-drop[data-drag="true"] {
  border-color: rgba(17, 24, 39, 0.35);
  background: rgba(17, 24, 39, 0.05);
}
.cc-drop:focus-within {
  border-color: rgba(17, 24, 39, 0.35);
  box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.18);
}

.cc-drop input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The white disc. A raised circle on a recessed field is the whole visual
   idea — it is what makes the zone read as a surface you can put something
   onto rather than a border you must click inside. */
.cc-drop__badge {
  width: 84px; height: 84px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--card, #fff);
  box-shadow: 0 2px 10px rgba(17, 24, 39, 0.07), 0 0 0 1px rgba(17, 24, 39, 0.03);
  color: var(--text, #111827);
}
.cc-drop__badge .cc-i { width: 26px; height: 26px; stroke-width: 1.7; }

.cc-drop__title {
  font-size: 1.02rem; font-weight: 500; letter-spacing: -0.01em;
  color: var(--text, #111827);
}

/* Not a <button>. It is inside the <label>, so a real button would need its
   own click handler to do what the label already does — and would be a second
   control for one action. Styled as one, announced by the label. */
.cc-drop__btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 11px 20px;
  border-radius: 12px;
  background: var(--card, #fff);
  border: 1px solid rgba(17, 24, 39, 0.08);
  box-shadow: 0 1px 3px rgba(17, 24, 39, 0.06);
  font-size: 0.93rem; font-weight: 600;
  color: var(--text, #111827);
}

.cc-drop__limit {
  font-size: 0.8rem; color: var(--muted, #6b7280); text-align: center;
}

/* ── WHAT WAS CHOSEN ────────────────────────────────────────────────────── */
.cc-files { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.cc-files:empty { display: none; }

.cc-file {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 10px);
  background: var(--card, #fff);
}
.cc-file > * { min-width: 0; }
.cc-file__icon { color: var(--muted, #6b7280); }
.cc-file__name { font-size: 0.87rem; color: var(--text, #111827); overflow-wrap: anywhere; }
.cc-file__size { font-size: 0.8rem; color: var(--muted, #6b7280); flex: none; }
.cc-file__x {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border: 0; border-radius: 8px;
  background: transparent; color: var(--muted, #6b7280);
  cursor: pointer;
}
@media (hover: hover) {
  .cc-file__x:hover { background: rgba(17, 24, 39, 0.05); color: var(--text, #111827); }
}

/* IN FLIGHT. `pointer-events: none` is the load-bearing half, for the reason
   the shared .lb-upload gives: without it a file can be dropped onto a dialog
   that is already uploading, and the two responses race. */
.cc-modal[data-busy="true"] .cc-drop,
.cc-modal[data-busy="true"] .cc-file__x {
  opacity: 0.55; pointer-events: none;
}

/* Tables and other genuinely wide content scroll inside their wrapper rather
   than widening the page. */
.lb-tablewrap { max-width: 100%; overflow-x: auto; }

/* ── Narrow screens ──────────────────────────────────────────────────────
   Below the point where a fixed 84px timestamp track plus dense text stops
   fitting, the event row stops being a row. Stacking is what keeps the words
   readable; squeezing is what produced the original fault. */
@media (max-width: 560px) {
  .cc-event {
    grid-template-columns: 7px minmax(0, 1fr);
    row-gap: 2px;
  }
  .cc-event__time { grid-column: 2; }
  .cc-event__what { grid-column: 2; }
  .cc-toasts { right: 12px; left: 12px; bottom: 12px; max-width: none; }

  /* A SHEET. A centred card with margins on a 380px screen is just a smaller
     screen with wasted edges, so below this the dialog takes the width and
     sits against the bottom — where a thumb is. */
  .cc-modal { padding: 0; align-items: flex-end; }
  .cc-modal__panel {
    max-width: none;
    max-height: 92vh;
    border-radius: var(--radius-xl, 16px) var(--radius-xl, 16px) 0 0;
    border-bottom: 0;
  }
  .cc-modal__head,
  .cc-modal__foot { padding: 14px 16px; }
  .cc-modal__body { padding: 18px 16px 20px; gap: 12px; }
  .cc-drop { min-height: 200px; padding: 28px 18px; gap: 14px; }
  .cc-drop__badge { width: 64px; height: 64px; }
  .cc-modal__foot .btn-dark,
  .cc-modal__foot .btn-ghost { flex: 1; justify-content: center; }
}

/* ══════════════════════════════════════════════════════════════════════════
   AGENT BRAIN

   Three product-local additions. The checkbox itself, its box and its text
   come from the SHARED component sheet (.lb-check) and are not restated here
   — this is only the arrangement and the two controls that sheet has no
   opinion about.
   ══════════════════════════════════════════════════════════════════════════ */

/* A list of options that wraps. Not a grid with a fixed column count: three
   languages and six personality presets are different lengths and both should
   read as one list rather than as a shape. */
.lb-console .cc-optionlist {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
  margin-bottom: 10px;
}

/* THE RADIO VARIANT. The shared sheet paints one box, square, because every
   existing use of it is a checkbox. A round one is how a person tells "pick
   one" from "pick any", and getting that wrong makes a five-way choice look
   like five independent switches. */
.lb-console .cc-check--radio .lb-check__box { border-radius: 50%; }
.lb-console .cc-check--radio .lb-check__box::after {
  width: 7px; height: 7px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  transform: scale(0.4);
}
.lb-console .cc-check--radio input:checked + .lb-check__box::after {
  transform: scale(1);
}


/* ══════════════════════════════════════════════════════════════════════
   THE BUSINESS BRAIN

   ── EVERY RULE IS SCOPED TO .lb-console ──────────────────────────────
   This product wears the main dashboard's design system through three
   shared sheets it does not own. An unprefixed `.cc-mem` here would leak
   into every page that loads them, so nothing below stands on its own —
   the same rule the rest of this file follows.
   ══════════════════════════════════════════════════════════════════════ */

/* ── THE TABS ─────────────────────────────────────────────────────────
   Six groups an operator thinks in — Business Knowledge, Sales Guidance,
   Offers, Customers, Campaigns, Learned Experience — and never the six
   scope identifiers underneath them. */
.lb-console .cc-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 14px;
}
.lb-console .cc-tab {
  padding: 7px 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 999px;
  background: var(--card, #fff);
  color: var(--muted, #6b7280);
  font-size: 0.82rem;
  cursor: pointer;
}
.lb-console .cc-tab:hover { color: var(--token-text, #111827); }
.lb-console .cc-tab.is-on {
  background: var(--accent, #111827);
  border-color: var(--accent, #111827);
  color: #fff;
}
.lb-console .cc-tab:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
}

/* ── SEARCH AND THE OLDER-VERSIONS SWITCH ─────────────────────────── */
.lb-console .cc-mem-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
}
.lb-console .cc-mem-search { max-width: 340px; }

/* ── THE CARDS ────────────────────────────────────────────────────────
   A stack rather than a table. A memory is a sentence somebody wrote, and
   a table cell is the wrong shape for one. */
.lb-console .cc-mem-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.lb-console .cc-mem {
  padding: 14px 16px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  background: var(--card, #fff);
}
.lb-console .cc-mem__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.lb-console .cc-mem__subject {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--token-text, #111827);
}
.lb-console .cc-mem__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.lb-console .cc-mem__body {
  margin: 0 0 8px;
  line-height: 1.55;
  color: var(--token-text, #111827);
  /* A memory may be a paragraph somebody pasted. It wraps rather than
     stretching the card, and long unbroken input cannot push the layout. */
  overflow-wrap: anywhere;
}
.lb-console .cc-mem__meta { margin: 0; }
.lb-console .cc-mem__origins,
.lb-console .cc-mem__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

/* ── WHAT WAS WORKED OUT, RATHER THAN SAID ───────────────────────────
   The most important distinction on this screen, so it is carried by the
   pill's WEIGHT rather than by a hue: a dashed edge reads as provisional
   in monochrome and stays legible to a reader who cannot separate colours.
   The same reasoning as the status dots in the shared sheet. */
.lb-console .cc-pill--soft {
  border-style: dashed;
  color: var(--muted, #6b7280);
}

/* ── AND WHAT THE CUSTOMER ACTUALLY SAID ─────────────────────────────
   The other half of the same distinction, for the screen where approving
   a card writes something permanent. A dashed edge on its own leaves
   "they said this" as the ABSENCE of a marking, which is no marking at
   all on a card read by itself. Filled against dashed — still no hue. */
.lb-console .cc-pill--said {
  border-color: var(--token-text, #111827);
  background: var(--token-text, #111827);
  color: var(--card, #fff);
}

/* ── AND WHAT IS NO LONGER THERE ─────────────────────────────────────
   A deleted memory keeps a card, because "there used to be one and it was
   removed on the 3rd" is the thing the shell exists to answer. It is
   visibly emptied rather than visibly broken. */
.lb-console .cc-pill--gone {
  border-style: dotted;
  color: var(--muted, #6b7280);
}
.lb-console .cc-mem--gone {
  border-style: dashed;
  background: transparent;
}
.lb-console .cc-mem__gone { font-style: italic; }
.lb-console .cc-mem__was {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-left: 2px solid var(--line, #e5e7eb);
  overflow-wrap: anywhere;
}

/* ── TWO ANSWERS, SIDE BY SIDE ────────────────────────────────────────
   Deliberately NOT styled as an error or a warning. Two things a customer
   said at different times are not a fault, and a red banner would tell an
   operator to go and fix something that may be perfectly true. */
.lb-console .cc-mem-conflict { margin-bottom: 14px; }

.lb-console .cc-mem-input {
  width: 100%;
  min-height: 110px;
  resize: vertical;
  font: inherit;
  line-height: 1.55;
}

/* ══════════════════════════════════════════════════════════════════════════
   TEACH LABBIE

   One screen, two panels, and one element that carries the whole safety of
   the feature: the quotation beside each suggestion. Everything else here is
   the shared dashboard components — the upload zone, the tabs, the cards —
   because a second visual language for a second way into the same Business
   Brain would be a second thing to keep in step.

   Scoped to .lb-console like everything else in this file, so nothing leaks
   into the shared Labbie sheets this console borrows.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE EVIDENCE ─────────────────────────────────────────────────────
   A QUOTATION, and it looks like one. An operator is deciding whether a
   proposed fact is really what the document said, so the sentence it came
   from has to read as the document's words rather than as a caption under a
   verdict somebody else reached.

   It is never truncated in CSS. An excerpt is already bounded to 500
   characters by the server, and hiding part of the little that is kept would
   be hiding the evidence to save a few pixels. */
.lb-console .cc-teach-excerpt {
  margin: 0 0 10px;
  padding: 8px 12px;
  border-left: 3px solid var(--line, #e5e7eb);
  color: var(--muted, #6b7280);
  font-size: 0.88rem;
  line-height: 1.55;
  /* A document may contain a very long unbroken string. It wraps rather than
     stretching the card, exactly as a memory body does. */
  overflow-wrap: anywhere;
}

/* ── THE PASTE BOX ────────────────────────────────────────────────────
   Below the drop zone rather than beside it: pasting is the fallback for the
   formats this does not read, and a layout that gave the two equal weight
   would suggest they were equal choices. */
.lb-console .cc-teach-paste {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line, #e5e7eb);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.lb-console .cc-teach-paste textarea {
  width: 100%;
  resize: vertical;
  font: inherit;
  line-height: 1.55;
}
.lb-console .cc-teach-paste .btn-dark { align-self: flex-start; }

/* ══════════════════════════════════════════════════════════════════════════
   THE REVIEW

   ── WHY THE DECISIONS ARE VISIBLY A GROUP ────────────────────────────────
   Approving is one act with several parts: what it should say, where it
   belongs, and what becomes of what is already recorded. Laid out as loose
   controls they would read as a form to fill in and then a button to press.
   Boxed together under the quotation they read as what they are — the
   answers being given about the thing quoted above them.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── WHAT WOULD BE RECORDED, WHICH IS EDITABLE ────────────────────────
   Taller than a label field, because what goes in it is a sentence somebody
   may rewrite rather than a value they may correct. */
.lb-console .cc-teach-edit {
  width: 100%;
  resize: vertical;
  font: inherit;
  line-height: 1.55;
}

.lb-console .cc-teach-decide {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 14px 0;
  padding: 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 10px;
  background: var(--surface-2, #f9fafb);
}

/* A choice and its words on one line, with the whole line clickable. A radio
   an operator has to hit exactly is a radio they mis-click. */
.lb-console .cc-teach-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text, #111827);
}
.lb-console .cc-teach-check input {
  margin: 2px 0 0;
  flex: 0 0 auto;
  cursor: pointer;
}

/* ── AND WHAT IS ALREADY THERE ────────────────────────────────────────
   Set apart with a warm edge rather than a red one. Two records disagreeing
   is not an error: it is the ordinary case, and "keep both" is as legitimate
   an answer as the other one. A danger colour here would push every operator
   towards replacing, which is the choice that loses something. */
.lb-console .cc-teach-conflict {
  padding: 12px;
  border: 1px solid var(--warn-line, #fde68a);
  border-radius: 8px;
  background: var(--warn-bg, #fffbeb);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lb-console .cc-teach-conflict .lb-label { margin: 0; }
.lb-console .cc-teach-conflict .cc-teach-excerpt {
  margin: 0;
  border-left-color: var(--warn-line, #fde68a);
}

/* ── WHERE THE QUOTATION CAME FROM ────────────────────────────────────
   Directly under the excerpt and set quieter than it, because it is the
   caption to the evidence and not evidence itself. A filename an operator
   chose can be long, and it wraps rather than widening the drawer. */
.lb-console .cc-teach-from {
  margin: -6px 0 10px;
  overflow-wrap: anywhere;
}

/* ── AND WHAT THE SERVER LAST REFUSED ─────────────────────────────────
   Above the controls rather than below them: it is the answer to something
   the operator has just done, and a refusal they have to scroll past their
   own choices to reach is one they will read after changing the wrong
   thing. The .lb-notice it sits on carries the colour; this only places it. */
.lb-console .cc-teach-refused {
  margin: 0 0 12px;
}

/* ── HOW MANY ARE WAITING ─────────────────────────────────────────────
   On the tab, because "waiting for you" with no number is a tab an operator
   has to open to find out whether it is worth opening. Not a red dot: a
   count is information and a dot is only pressure. */
.lb-console .cc-count {
  display: inline-block;
  min-width: 18px;
  margin-left: 6px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--line, #e5e7eb);
  color: var(--token-text, #111827);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.lb-console .cc-tab.is-on .cc-count {
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
}

/* A table cell that opens something. A <button>, not an <a>, because it goes
   nowhere a browser could follow — there is no URL for a campaign screen in
   this console, and an anchor with href="#" is a link that lies to a keyboard
   user about what Enter will do. Styled to read as the row's own title so the
   table does not sprout a column of blue.

   NO SECOND PALETTE: it inherits --text and is distinguished by weight and by
   its underline on hover, not by a colour this file does not have. */
.cc-linkish {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--text);
  font-weight: 550;
  text-align: left;
  cursor: pointer;
  min-width: 0;
  overflow-wrap: anywhere;
}
.cc-linkish:hover { text-decoration: underline; }
.cc-linkish:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
  border-radius: 3px;
}


/* ══════════════════════════════════════════════════════════════════════════
   THE AGENTS PAGE, THE ONBOARDING SHELL, AND THE LABBIE AGENT CHARACTER

   Malaysia V1 B1. Everything below is scoped to .lb-console, which is on
   <body> in coldcalling/public/index.html and nowhere else in the repository
   — so none of it can reach the Labbie user dashboard, which links the same
   shared sheets this file sits after but never links this file.

   Nothing here restyles a shared primitive. The inputs are .lb-input and
   .lb-select, the checkboxes are .lb-check, the buttons are .btn-dark and
   .btn-quiet, and every one of them is used exactly as it already is. What
   this block adds is the CONTAINER those primitives sit in, which did not
   exist before.
   ══════════════════════════════════════════════════════════════════════════ */


/* ── THE LABBIE AGENT CHARACTER ───────────────────────────────────────────
   The wrapper, not the artwork — the shape itself is #i-labbie-agent in the
   sprite, defined once and used by every state.

   ONE SIZE KNOB. --cc-labbie-size is set inline by the caller because the
   character appears at different sizes on different surfaces, and a class per
   size would be a class per surface.

   [data-state] is where a later state is expressed — analysing, listening,
   speaking, completed — so none of them needs a second character drawn for
   it. Only 'neutral' exists today, and it is styled by having nothing added
   to it. */
.lb-console .cc-labbie {
  display: block;
  flex: none;
  width: var(--cc-labbie-size, 56px);
  height: var(--cc-labbie-size, 56px);
  /* The one black the product already uses for its primary buttons, so the
     character and the Continue button beside it are the same colour rather
     than two blacks that nearly match. */
  color: var(--accent, #111827);
}
.lb-console .cc-labbie > svg { display: block; width: 100%; height: 100%; }


/* ── THE AGENTS LANDING CARD ──────────────────────────────────────────────
   Deliberately NOT the onboarding shell. The empty state and the draft
   summary are a compact card, as the approved landing reference shows; the
   fixed 900x650 shell belongs to the numbered steps, and stretching this card
   to it would be 650px of white around four lines of text. */
.lb-console .cc-agents-card {
  width: 440px;
  max-width: calc(100% - 32px);
  margin: 0 auto;
}
.lb-console .cc-agents-card__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 44px 28px 40px;
}
.lb-console .cc-agents-card__title {
  margin: 22px 0 0;
  font-size: 1.05rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  color: var(--text);
  overflow-wrap: anywhere;
}
/* "Setup in progress" — a state, said in words. Not a colour and not a badge,
   because it has to be readable to somebody who can see neither. */
.lb-console .cc-agents-card__status {
  margin: 8px 0 0;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.lb-console .cc-agents-card__text {
  margin: 8px 0 0;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 34ch;
}
/* The card mirrors the operator's Step 1 avatar — same asset, same circular
   crop the Step 1 selector uses, only sized to sit above the card title. */
.lb-console .cc-agents-card__avatar {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  overflow: hidden;
}
.lb-console .cc-agents-card__avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
.lb-console .cc-agents-card__action { margin-top: 22px; }
.lb-console .cc-agents-card .lb-notice { margin-top: 16px; text-align: left; }


/* ══════════════════════════════════════════════════════════════════════════
   THE STANDARD ONBOARDING CARD

   ── ONE SHELL, EVERY STEP, EVERY STATE ──────────────────────────────────
   Exactly 900 x 650 on a desktop, and the SAME 900 x 650 for a loading
   state, a validation message, an error, short content, long content, and
   every step built later. A card that resized when a red line appeared under
   a field would move the buttons out from under somebody's cursor at the
   exact moment they were being told they had made a mistake.

   ── HOW THE SIZE IS HELD ────────────────────────────────────────────────
   A THREE-ROW GRID whose middle row is minmax(0, 1fr). The zero minimum is
   the load-bearing part: 1fr alone resolves to minmax(auto, 1fr), whose auto
   minimum is the CONTENT's height — so a long form would push the grid past
   650px and the card would grow, silently, exactly as content arrived. With a
   zero minimum the middle row takes what is left and its own overflow handles
   the rest.

   Which is also why the middle row is the only scroller: the top row carries
   the step number and the five segments, the bottom row carries the actions,
   and neither may scroll out of reach of somebody halfway down a form.

   overflow: hidden on the card is not decoration either — it is what keeps
   the scrolling child inside the 16px corners. */
.lb-console .cc-onb {
  box-sizing: border-box;
  width: 100%;
  /* 24px of breathing room each side once 900 no longer fits. A max-width
     rather than a media query, because the width at which 900 stops fitting
     depends on the sidebar, and a hard-coded breakpoint would be a second
     answer to a question the layout already answers. */
  max-width: none;
  /* ── 650 IS THE CARD'S SIZE, NOT ITS CEILING ────────────────────────
     A fixed `height` here is what made the card clip its own contents, and
     the clipping was hidden behind the body's own scrollbar — see
     .cc-onb__body. Step 1 fits in 650px and always did; step 2 holds a drop
     zone AND a textarea AND a footer, and it does not.

     min-height keeps every state that fits at exactly the size the design
     was approved at — nothing about step 1 moves — and lets the one state
     that does not fit grow downward onto a page that scrolls. What is
     removed is the clipping, not the proportions. */
  min-height: max(650px, calc(100vh - 150px));
  margin: 0;
  display: grid;
  /* `1fr`, not `minmax(0, 1fr)`. The difference is the whole fix: 1fr means
     minmax(AUTO, 1fr), so the row can never be shorter than what is in it,
     while minmax(0, 1fr) explicitly permits it to be — which is what let the
     card stay 650px while its contents did not fit inside it.

     It still absorbs the slack, so a short step keeps its footer on the
     bottom edge exactly as before. Step 1 does not move. */
  grid-template-rows: auto 1fr auto;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 16px;
  box-shadow: var(--shadow, 0 1px 6px rgba(0, 0, 0, 0.05));
  overflow: hidden;
}

/* ── THE FIXED TOP ─────────────────────────────────────────────────────── */
.lb-console .cc-onb__top {
  padding: 34px 40px 22px;
  text-align: center;
  position: relative;
}

/* ── BACK TOP LEFT, CONTINUE TOP RIGHT ──────────────────────────────────
   Steps 1 to 4 (setupNavMarkup). OUT OF FLOW, so the step number, the
   segments and the heading stay centred on the card rather than on the space
   left between two buttons — and so nothing below the header, however long,
   can move either control. Anchored to the header's own top padding: on the
   step count's line, without being part of it.

   One row for both, so the two share a centre line. The row itself takes no
   pointer: it passes over the step count, which stays ordinary text. */
.lb-console .cc-onb__nav {
  position: absolute;
  top: 20px;
  left: 18px;
  right: 27px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  pointer-events: none;
}
.lb-console .cc-onb__nav > button { pointer-events: auto; }

/* BACK: a chevron and a word, in ink, with no surface in any state. The quiet
   button's hover pill is exactly what the reference leaves out. */
.lb-console .cc-onb__back {
  gap: 4px;
  padding: 8px 11px;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text);
}
.lb-console .cc-onb__back:hover {
  background: none;
  color: var(--text);
  opacity: 0.8;
}

/* CONTINUE: the platform's black pill, proportioned to the reference — a
   little more height and a lighter right side, where the arrow's own drawing
   already carries space. !important for .cc-ll-addfile's reason: glass.css
   sets .btn-dark's padding and weight with !important. */
.lb-console .cc-onb__next {
  gap: 7px;
  padding: 9px 9px 9px 21px !important;
  font-weight: 500 !important;
}

/* The console's 18px icon box. Both drawings sit inside its middle, so the
   arrow gives back 2px above and below rather than making the pill taller. */
.lb-console .cc-onb__navicon { stroke-width: 2.25; }
/* The shared chevron points right; this one is a Back control. */
.lb-console .cc-onb__navicon--back { transform: rotate(180deg); }
.lb-console .cc-onb__navicon--next { margin-block: -2px; }
/* A press that setBusy() is carrying shows its spinner and its own word; an
   arrow beside them would promise a step the press has not reached yet. */
.lb-console .cc-onb__next[aria-busy="true"] .cc-onb__navicon--next { display: none; }

.lb-console .cc-onb__stepno {
  margin: 0 0 14px;
  font-size: 0.78rem;
  color: var(--muted);
}
.lb-console .cc-onb__title {
  margin: 0 0 6px;
  font-size: 1.55rem;
  font-weight: 500;
  letter-spacing: -0.025em;
  color: var(--text);
}
.lb-console .cc-onb--step-two .cc-onb__title { font-weight: 600; }
.lb-console .cc-onb__desc {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--muted);
}

/* ── THE FIVE SEGMENTS ────────────────────────────────────────────────────
   flex: 1 1 0 inside a container of fixed width, so all five are equal and
   sit in the same place on every step — an indicator that moved or rescaled
   as somebody advanced would stop reading as one object.

   The dark segment is the only visual difference between a step that is
   current and one that is not, so each segment also carries its number, its
   name and its condition as text for a reader who cannot see which is dark
   (WCAG 1.4.1). */
.lb-console .cc-onb__progress {
  display: flex;
  gap: 8px;
  width: min(320px, 100%);
  margin: 0 auto 20px;
  padding: 0;
  list-style: none;
}
.lb-console .cc-onb__seg {
  flex: 1 1 0;
  height: 3px;
  border-radius: 999px;
  background: var(--line, #e5e7eb);
}
.lb-console .cc-onb__seg[data-state="current"],
.lb-console .cc-onb__seg[data-state="done"] {
  background: var(--accent, #111827);
}

/* ── THE MIDDLE, WHICH NO LONGER SCROLLS ON ITS OWN ────────────────────
   This was the card's own scroller: `overflow-y: auto` inside a row of a
   grid whose card was exactly 650px tall. On step 1 that was invisible,
   because step 1 fits. On step 2 it was the defect — a scrollbar INSIDE a
   card that was itself on a scrolling page, a drop zone and a textarea
   sharing a viewport that could not show both, and a footer sitting on the
   boundary looking as though it were on top of the box above it.

   Two scrollbars for one document is a bug wherever it appears. The card now
   grows (see .cc-onb's min-height) and the PAGE scrolls, which is the one
   scrollbar a person already knows how to use.

   ── AND WHY overflow-x HAD TO GO WITH IT ────────────────────────────
   Removing `overflow-y: auto` and leaving `overflow-x: hidden` did NOT stop
   the scrolling, and the reason is a corner of the box model that is easy to
   miss: when one axis is anything other than `visible`, the other axis
   cannot be `visible` either — the browser computes it as `auto`. So the
   element went on being a scroll container with nothing in the stylesheet
   saying so, the footer still sat on the fold, and the textarea still ran
   underneath it. Measured in the browser, which is the only place that fact
   is visible.

   Neither axis is declared now. Horizontal containment is unaffected: the
   card is `overflow: hidden`, and that is the rule which has always kept a
   long unbroken string inside the rounded corners. */
.lb-console .cc-onb__body {
  min-height: 0;
  padding: 4px 40px 28px;
}
.lb-console .cc-onb > #stepOneForm { display: contents; }
.lb-console .cc-onb__field {
  margin: 0 auto 18px;
  min-width: 0;
  max-width: 420px;
}
.lb-console .cc-onb__field > .lb-label { display: block; margin-bottom: 6px; }
/* A fieldset carries the group's name to a screen reader; it must carry none
   of a fieldset's default box to the eye. */
.lb-console .cc-onb__fieldset {
  border: 0;
  padding: 0;
  margin: 0 auto 18px;
  min-width: 0;
  max-width: 420px;
}
.lb-console .cc-onb__fieldset > .lb-label { padding: 0; margin-bottom: 6px; }
.lb-console .cc-onb__error { margin: 6px 0 0; }

/* The character beside the name it is being given, as the approved reference
   has it: the thing being named, next to where it is named. */
.lb-console .cc-onb__idrow {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 24px;
  max-width: 560px;
  margin: 0 auto 26px;
}
.lb-console .cc-onb__idrow > .cc-onb__field { margin: 0; max-width: none; }
.lb-console .cc-onb__avatar-field { min-width: 96px; }
.lb-console .cc-onb__avatar {
  display: block;
  width: 96px;
  height: 96px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}
.lb-console .cc-onb__avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
.lb-console .cc-onb__avatar:focus-visible {
  outline: 3px solid var(--accent, #111827);
  outline-offset: 4px;
}
.lb-console #stepOneForm .cc-onb__fieldset { max-width: 560px; }

.lb-console .cc-onb__langs {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
}
.lb-console #stepOneForm .cc-onb__langs .lb-check {
  min-height: 44px;
  width: 100%;
  align-items: center;
}
.lb-console #stepOneForm .cc-onb__langs .lb-check__box { border-radius: 50%; }

/* ── THE FIXED BOTTOM ───────────────────────────────────────────────────
   Back on the left, the step's own actions on the right, exactly as the
   approved reference has them. It never scrolls, in any state. */
.lb-console .cc-onb__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px 40px 20px;
}
.lb-console .cc-onb__foot-main {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}
.lb-console .cc-onb__notice { margin: 0 40px 16px; }

/* ── A FAILURE, WHERE IT HAPPENED ───────────────────────────────────────
   Three slots, one type: red TEXT on the card's own surface. No panel, no
   border, no icon and no corner of the window. A workflow error is a
   sentence about the thing beside it, and a coloured box around it makes an
   ordinary refusal — "complete this item before saving" — look like a fault
   in the product.

   The footer's takes the whole first row so the primary action stays on the
   right where it is in every state, rather than being pushed by the length
   of a message. */
.lb-console .cc-onb__footerror {
  flex: 1 1 100%;
  margin: 0;
  order: -1;
}
.lb-console .cc-biz__panelerror { margin: 14px 0 0; }
.lb-console .cc-biz__rowerror { margin: 8px 0 0; }

/* ── THE HONEST UNBUILT STEP ────────────────────────────────────────────
   The same card, at the same size, saying what happened. */
.lb-console .cc-onb__done {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 26px 0 0;
}
.lb-console .cc-onb__done-title {
  margin: 20px 0 0;
  font-size: 0.98rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-onb__done-text {
  margin: 8px 0 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted);
  max-width: 46ch;
}



/* ══════════════════════════════════════════════════════════════════════════
   STEP 2 OF 5 — BUSINESS INFORMATION

   Inside the same .cc-onb card as step 1, at the same width and the same
   height, scrolling in the same middle region. Nothing below restyles the
   shell: a step that changed the card it lives in would stop reading as the
   same flow, and step 1 must look on the day step 2 shipped exactly as it
   looked the day before.

   ── ONE COLUMN, NOT TWO ──────────────────────────────────────────────────
   A proposed fact and the fact it might replace are read one after the other,
   not side by side. Two columns inside 900px would put a 40-character measure
   next to another 40-character measure, and the sentence is the thing being
   reviewed — it gets the width.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-biz {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 560px;
  margin: 0 auto;
  padding-top: 6px;
}

/* ── THE SENTENCE AT THE TOP OF EACH STATE ────────────────────────────────
   What happened, and what to do.

   LEFT, with everything else in the column. It was centred, which read as
   deliberate while a character sat above it and as a mistake the moment the
   character went: three blocks of identical width, one of them centred, is
   exactly the "inconsistent horizontal alignment" a reader notices without
   being able to name it.

   The completion state keeps its centring. It is a different composition —
   something finished, rather than a form waiting to be filled in. */
.lb-console .cc-biz__lead {
  text-align: left;
}
.lb-console .cc-biz__lead--done {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.lb-console .cc-biz__leadtitle {
  /* Was 10px, to clear the character above it. There is no character now. */
  margin: 0;
  font-size: 0.98rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-biz__leadtext {
  /* `auto` side margins centred this paragraph inside a 46ch measure, which
     put it several characters in from the title above it and the drop zone
     below it — three blocks that are meant to share one left edge, and one
     of them starting somewhere else. The measure is kept, because a line of
     46 characters is easier to read than one of 70; only the centring goes,
     so the column has ONE left edge. */
  margin: 8px 0 0;
  max-width: 46ch;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted);
}
/* The completion state is still a centred composition, so its paragraph
   stays centred inside it. */
.lb-console .cc-biz__lead--done > .cc-biz__leadtext { margin-inline: auto; }

/* ── A REPLACEMENT THAT IS WAITING ───────────────────────────────────────
   Set apart from the lead above it, because it is a different KIND of
   statement: everything else on this screen describes the business, and this
   describes an unfinished decision about it. A tinted panel and a rule, in
   the tokens the console already uses — no new colour is introduced, and
   nothing else on the page moves.

   The destructive control is .btn-danger, which this stylesheet already
   defines and which every other destructive action in the product uses. A
   discard that looked like every other button would be the one place in the
   console where losing work is one indistinguishable press away. */
.lb-console .cc-biz__pending {
  /* The panel geometry this stylesheet already uses for a bordered block —
     same border, same radius, same surface token and the same fallback. A
     second panel shape would be a page-specific component for something that
     is not a new kind of thing. */
  margin: 14px 0 0;
  padding: 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 10px;
  background: var(--surface-2, #f9fafb);
}
.lb-console .cc-biz__pendingtitle {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-biz__pendingtext {
  margin: 6px 0 0;
  max-width: 52ch;
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--muted);
}
.lb-console .cc-biz__pendingacts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

/* ── MORE THAN ONE BUSINESS ─────────────────────────────────────────────
   The same panel geometry as the replacement notice above, deliberately: it
   is the same KIND of thing — a state of this step, explained where it is
   true — and giving it its own shape would say it is a different sort of
   event. No red, no icon and no border colour: nothing has gone wrong, the
   product has read two documents correctly and is asking which business
   this agent is. */
.lb-console .cc-biz__identity {
  margin: 14px 0 0;
  padding: 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 10px;
  background: var(--surface-2, #f9fafb);
}
.lb-console .cc-biz__identitytitle {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-biz__identitytext {
  margin: 6px 0 0;
  max-width: 52ch;
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--muted);
}
.lb-console .cc-biz__ids {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
}
/* minmax(0) so a long filename wraps inside its own column instead of
   pushing the panel wider than the card. */
.lb-console .cc-biz__id {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  padding: 9px 11px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  background: var(--token-surface-primary, #fff);
}
.lb-console .cc-biz__idname {
  font-size: 0.86rem;
  font-weight: 550;
  color: var(--text);
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__idsource {
  font-size: 0.78rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}
/* Narrow screens stack them, so neither control is clipped and both stay a
   comfortable target — the same 44px floor .cc-actions uses. */
@media (max-width: 560px) {
  .lb-console .cc-biz__pendingacts > .btn-dark,
  .lb-console .cc-biz__pendingacts > .btn-danger {
    flex: 1 1 100%;
    min-height: 44px;
    justify-content: center;
  }
}

/* The withdrawn-document panel that lived here — .cc-biz__decide,
   .cc-biz__decidetext, .cc-biz__decideacts and their mobile stacking — is
   removed with the markup it styled. Under §B a document going takes what
   it said with it, so no row is ever asked what it should be now. */

.lb-console .cc-biz__heading {
  margin: 0;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.lb-console .cc-biz__hint {
  margin: 0;
  font-size: 0.83rem;
  line-height: 1.55;
  color: var(--muted);
}

.lb-console .cc-biz__facts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.lb-console .cc-biz__add { margin-top: 7px; font-size: 0.78rem; }

/* ── ONE FACT ─────────────────────────────────────────────────────────────
   24px, which is the radius the rest of this product's components carry. The
   shell around it is 16 and stays 16 — it was built before this and changing
   it would be restyling step 1. */
.lb-console .cc-biz__fact {
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 16px;
  background: var(--card, #fff);
  padding: 12px 13px;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  overflow: hidden;
}
.lb-console .cc-biz__body {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}
/* The editable sentence. It looks like what it is — a field somebody may
   correct — rather than like text with an edit affordance hidden behind a
   hover, which on a touch screen is no affordance at all. */
.lb-console .cc-biz__edit {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  resize: none;
  overflow: hidden;
  font: inherit;
  font-size: 0.9rem;
  line-height: 1.6;
}

.lb-console .cc-biz__faq {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  min-width: 0;
}
.lb-console .cc-biz__faq > .lb-label { margin-top: 4px; }
.lb-console .cc-biz__faq > .lb-label:first-child { margin-top: 0; }
.lb-console .cc-biz__faq > .lb-input { width: 100%; min-width: 0; box-sizing: border-box; }
.lb-console .cc-biz__faqread { margin: 0; }
.lb-console .cc-biz__faqread dt {
  margin: 8px 0 2px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
}
.lb-console .cc-biz__faqread dt:first-child { margin-top: 0; }
.lb-console .cc-biz__faqread dd {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.55;
  overflow-wrap: anywhere;
}

/* Evidence is a secondary disclosure. It occupies no space until the row's
   View source control is used, and the route has already reduced it to the
   smallest supporting sentence or bounded fragment. */
.lb-console .cc-biz__sourcebtn { font-size: 0.76rem; }
.lb-console .cc-biz__quote {
  margin: 10px 0 0;
  padding: 0 0 0 12px;
  border-left: 2px solid var(--line, #e5e7eb);
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__quote[hidden] { display: none; }

/* The rules that styled the collision card — .cc-biz__identity, its
   title, the .cc-biz__quote--live copy of the fact already in force and
   the .cc-biz__choice radio fieldset — went with the card itself. Step 2
   publishes one resolved conclusion and has nothing to compare. */
.lb-console .cc-biz__factfoot {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.lb-console .cc-biz__drop { margin-left: auto; }

/* ── WHAT IS ALREADY IN USE ───────────────────────────────────────────────
   Set apart by a rule and a heading rather than by a colour, so it reads as
   a different REGION on a monochrome screen too — and so nothing here needs a
   colour the palette does not have. */
.lb-console .cc-biz__already {
  margin-top: 4px;
  padding-top: 20px;
  border-top: 1px solid var(--line, #e5e7eb);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ── WHERE INFORMATION IS HANDED OVER ─────────────────────────────────── */
.lb-console .cc-biz__input {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.lb-console .cc-biz__paste {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lb-console .cc-biz__paste textarea {
  width: 100%;
  min-height: 168px;
  box-sizing: border-box;
  resize: vertical;
  font: inherit;
  line-height: 1.55;
  padding: 12px 14px;
  border: 1px solid #b6b6b6;
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.lb-console .cc-biz__paste textarea::placeholder {
  color: #989898;
  opacity: 1;
}
.lb-console .cc-biz__pastefoot {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}
.lb-console .cc-biz__title { flex: 1 1 200px; min-width: 0; }
.lb-console .cc-biz__title > .lb-label { display: block; margin-bottom: 6px; }
.lb-console .cc-biz__pastefoot .btn-dark { flex: 0 0 auto; }
.lb-console .cc-biz__queued,
.lb-console .cc-biz__sources ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.lb-console .cc-biz__queued:empty { display: none; }
.lb-console .cc-biz__queued li {
  display: flex;
  min-width: 0;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  color: var(--text);
  font-size: 0.8rem;
}
.lb-console .cc-biz__queued li + li { margin-top: 6px; }
.lb-console .cc-biz__queued li > span { min-width: 0; overflow-wrap: anywhere; }
.lb-console .cc-biz__sources {
  padding: 10px 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  background: var(--token-surface-secondary, #f9fafb);
}
.lb-console .cc-biz__sourceslabel {
  display: block;
  margin-bottom: 5px;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 500;
}
.lb-console .cc-biz__sources li {
  color: var(--text);
  font-size: 0.78rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__sourcerow {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
}
.lb-console .cc-biz__sourcerow + .cc-biz__sourcerow { border-top: 1px solid var(--line, #e5e7eb); }
.lb-console .cc-biz__fileicon {
  display: inline-flex;
  width: 32px;
  height: 34px;
  align-items: center;
  justify-content: center;
  color: #080808;
}
.lb-console .cc-biz__fileicon--pdf { color: #e00004; }
.lb-console .cc-biz__fileicon--txt { color: #0867f9; }
.lb-console .cc-biz__typeicon { display: block; width: 30px; height: 34px; flex: none; }
.lb-console .cc-biz__typepage { fill: #fff; stroke: currentColor; stroke-width: 3.8; stroke-linejoin: round; }
.lb-console .cc-biz__typefold { fill: none; stroke: currentColor; stroke-width: 3.8; stroke-linejoin: round; }
.lb-console .cc-biz__typelines { fill: none; stroke: #515151; stroke-width: 1.8; stroke-linecap: round; }
.lb-console .cc-biz__typebadge { fill: currentColor; }
.lb-console .cc-biz__typelabel { fill: #fff; font-family: var(--cc-font-ui, var(--font-ui)); font-size: 14px; font-weight: 700; }
.lb-console .cc-biz__filecopy { min-width: 0; display: flex; flex-direction: column; }
.lb-console .cc-biz__filename { color: #111827; font-weight: 500; overflow-wrap: anywhere; }
.lb-console .cc-biz__filemeta { margin-top: 2px; color: #6b7280; font-size: 0.72rem; }
.lb-console .cc-biz__fileactions { display: flex; align-items: center; gap: 4px; }
.lb-console .cc-biz__fileactions .btn-quiet { min-height: 44px; display: inline-flex; align-items: center; }

/* The section guide list that used to sit here is gone — see
   agentStepTwoMarkup() for why. Its rules went with it rather than being
   left behind for a class nothing renders: dead CSS is what a later reader
   mistakes for a component that exists. */

/* Adding more, once the step is done. A <details> because it is genuinely
   secondary at that point — the approved information is what the screen is
   for — and because a native disclosure is keyboard- and screen-reader
   complete without a line of script. */
.lb-console .cc-biz__more {
  padding-top: 20px;
  border-top: 1px solid var(--line, #e5e7eb);
}
.lb-console .cc-biz__moretop {
  cursor: pointer;
  font-size: 0.86rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-biz__more > .cc-biz__hint { margin: 8px 0 16px; }

/* ══════════════════════════════════════════════════════════════════════════
   STEP 2 V3: ONE WORKSPACE, TWO TABS

   ── WHAT REPLACED WHAT ──────────────────────────────────────────────────
   The review used to be a single bordered accordion with every category
   collapsed. It was compact, and it was compact in the way a filing cabinet
   is: eleven closed drawers, each headed with a summary, and no way to check
   a price without opening one. Nobody opens eleven drawers before pressing
   Approve — so the facts are on the page now, in a card per category.

   Everything here is the console's existing design language: white surfaces,
   one hairline, one radius, one grey scale. No shadow that is doing work a
   border could do, no colour that is not either the text, the line or the one
   black primary action.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE TAB STRIP ──────────────────────────────────────────────────────
   A segmented control, because the two views are peers. It sits inside the
   step's card, centred under the heading — it is part of the step, not
   chrome above it.

   ── AND IT WEARS THE WEBSITE BUILDER'S SWITCH ──────────────────────────
   The Preview / Manage / Payments pill of the workspace (#wsModeSwitch, as
   it renders at 901px and wider), value for value: the track, the selected
   segment, the type, the hover, the focus ring and the 140ms fade. Its
   segments are as wide as their labels, so the strip is as wide as its two
   tabs rather than a fixed 360px split in half. The same treatment is on
   Step 3's input switch (.cc-v3__modes), and on nothing else.

   Two things are not copied. The workspace's frosted blur, which this
   console does not use (coldcalling-ui-check.js) and which has nothing to
   blur on a white card. And the face: the segments inherit the console's
   font like every other control here.

   One value is stated rather than copied because of that: the label's line
   height. The workspace's `normal` is a 17px line in its face — a 27px
   segment in a 35px pill — and Inter's `normal` is 15px, so 17px is set to
   keep those two heights.

   Choosing a tab re-renders the strip, so the selected segment is drawn
   already selected — the fade shows on hover, not on the switch. */
.lb-console .cc-biz__tabs {
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 22px;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.90);
  box-shadow: 0 2px 8px -4px rgba(28, 24, 16, 0.16);
}
.lb-console .cc-biz__tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 5px 13px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted, #6b7280);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 17px;
  letter-spacing: -0.005em;
  cursor: pointer;
  transition: background 140ms, color 140ms, box-shadow 140ms;
}
.lb-console .cc-biz__tab[aria-selected="true"] {
  background: rgba(255, 255, 255, 0.98);
  color: var(--text, #111827);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04);
}
/* The workspace has no disabled segment. Review is disabled until there is a
   review, and keeps the grey it always had. */
.lb-console .cc-biz__tab:disabled {
  color: #b6bcc5;
  cursor: default;
}
.lb-console .cc-biz__tab:not(:disabled):hover { color: var(--text, #111827); }
/* The workspace draws the browser's own focus ring, not this console's. */
.lb-console .cc-biz__tab:focus-visible {
  outline: revert;
  outline-offset: revert;
}
/* ── AND THE REVIEW TAB CARRIES NO MARK ───────────────────────────────
   A dot lived here, shown while the server said something had not been
   analysed. It asked the reader a question it could not answer — the only
   action is Analyse changes, which is a named button on the Sources tab —
   and Step 2 shows business information, not its own state. Nothing
   replaces it: a badge, a count or a colour in its place would be the same
   question drawn differently. */
.lb-console .cc-biz__panel { min-width: 0; }
.lb-console .cc-biz__panel:focus { outline: none; }

/* ── THE SOURCES TAB ──────────────────────────────────────────────────────
   ── THE FILE INPUT IS NOT PART OF THE DESIGN ──────────────────────────
   A fault this redesign inherited rather than introduced: the drop zone
   wraps a bare <input type="file">, nothing ever hid it, and the browser's
   own "Choose Files / No file chosen" widget has been sitting in the middle
   of the zone directly under the sentence inviting somebody to choose a
   file. Two controls for one act, one of them unstyled.

   Hidden visually and NOT with display:none, because the label carries
   tabindex="-1" — the input is the only thing on the zone a keyboard can
   reach, and taking it out of the tree would make uploading a mouse-only
   act. The zone shows the focus in its place. */
.lb-console .cc-biz__input .lb-upload {
  position: relative;
  box-sizing: border-box;
  justify-content: center;
  min-height: 230px;
  padding: 28px 20px;
  gap: 0;
  border: 1px solid #b6b6b6;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14);
}
@media (hover: hover) {
  .lb-console .cc-biz__input .lb-upload:hover { border-color: #777; }
}
.lb-console .cc-biz__input .lb-upload[data-drag="true"] {
  border-color: #777;
  background: rgba(0, 0, 0, 0.02);
}
.lb-console .cc-biz__input .cc-biz__upload-art {
  display: block;
  width: 58px;
  height: 58px;
  margin-bottom: 14px;
  color: #333;
  flex: none;
}
.lb-console .cc-biz__input .lb-upload__title {
  color: #111;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
}
.lb-console .cc-biz__input .cc-biz__uploadsize {
  margin-top: 4px;
  color: #999;
  font-size: 0.875rem;
  line-height: 1.35;
}
.lb-console .cc-biz__input .lb-upload input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  white-space: nowrap;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}
.lb-console .cc-biz__input .lb-upload:focus-within {
  border-color: rgba(17, 24, 39, 0.45);
  box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.07);
}

.lb-console .cc-biz__srcstate {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 10px;
  background: var(--token-surface-secondary, #f9fafb);
}
/* ── WAITING IS NOT A WARNING ──────────────────────────────────────────
   Amber for one draft, and wrong twice over: this console's palette has no
   amber in it, and "you have changes that haven't been analysed" is not a
   fault to be corrected — it is a fact about what the operator has just
   done. The surface stays the neutral one the state shares with "up to
   date"; what separates them is the missing tick and the sentence, which is
   all a difference of state needs. The border is drawn a shade firmer so the
   line still reads as the one asking for a press. */
.lb-console .cc-biz__srcstate--wait {
  border-color: rgba(17, 24, 39, 0.16);
}
.lb-console .cc-biz__srctick {
  display: inline-flex;
  color: #15803d;
}
.lb-console .cc-biz__srctick .cc-i { width: 16px; height: 16px; }
.lb-console .cc-biz__srctext {
  margin: 0;
  color: var(--text, #111827);
  font-size: 0.82rem;
  line-height: 1.45;
}
/* The two states are told apart by SHAPE and by words, not by hue: the
   up-to-date line carries a green check, the unanalysed one carries no icon
   at all and a sentence saying so. Nothing here restates the difference in
   colour, which is what makes it readable to somebody who cannot separate
   two hues — the rule the console's one green is already written to. */

/* ══════════════════════════════════════════════════════════════════════════
   THE WAIT

   Alive, and true. Everything that moves here is either a genuine state
   change from the server or an indeterminate animation that claims nothing —
   see bizAnalysingPanel() in coldcalling.js, and analysisProgress() in
   routes/business-information.js, for why there is no percentage on offer
   unless the run has one.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-biz__analysing {
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
  min-width: 0;
}
.lb-console .cc-biz__analysinghead {
  display: flex;
  align-items: center;
  gap: 14px;
}
.lb-console .cc-biz__analysingtitle {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 600;
  line-height: 1.35;
  /* ── THE SHIMMER ────────────────────────────────────────────────────
     A soft grey band sweeping through the heading while the reading is
     active — the same "this is still working" role the character's breathe
     and the stage ring's spin already play on this panel, spent on the one
     line an operator actually reads for news. No glow, no blur: a gradient
     clipped to the letterforms themselves, so the text stays exactly as
     sharp as it would be in a flat colour. Stopped under reduced motion,
     below, back to that flat colour. */
  background-image: linear-gradient(100deg,
    var(--text, #111827) 40%, #d5d9df 50%, var(--text, #111827) 60%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: cc-biz-sheen 2.4s linear infinite;
}
@keyframes cc-biz-sheen {
  from { background-position: 100% 0; }
  to   { background-position: -120% 0; }
}
.lb-console .cc-biz__analysingtext {
  margin: 3px 0 0;
  color: var(--muted, #6b7280);
  font-size: 0.82rem;
  line-height: 1.45;
}
/* The character is the one thing on this screen allowed to be decorative,
   and it earns it by moving: a still mascot over a still bar is the screen
   that read as stopped. */
.lb-console .cc-labbie[data-state="working"] {
  animation: cc-biz-breathe 2.4s ease-in-out infinite;
}
@keyframes cc-biz-breathe {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

.lb-console .cc-biz__progress {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  background: #fff;
}
.lb-console .cc-biz__barrow {
  display: flex;
  align-items: center;
  gap: 12px;
}
.lb-console .cc-biz__bar {
  position: relative;
  flex: 1 1 auto;
  height: 6px;
  border-radius: 999px;
  /* The sheet's own line token rather than a hex written here, so the track
     under the fill is the same grey as every other rule on the step. */
  background: var(--line, #e5e7eb);
  overflow: hidden;
}
.lb-console .cc-biz__barfill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--text, #111827);
  transition: width 420ms ease;
}
.lb-console .cc-biz__barpct {
  flex: 0 0 auto;
  min-width: 34px;
  text-align: right;
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
/* ── THE INDETERMINATE ONE ──────────────────────────────────────────────
   Drawn when the server has no fraction to give. It sweeps rather than
   fills, so it cannot be mistaken for a measurement. */
.lb-console .cc-biz__bar--idle .cc-biz__barfill {
  width: 38%;
  transition: none;
  animation: cc-biz-sweep 1.5s ease-in-out infinite;
}
@keyframes cc-biz-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(265%); }
}

.lb-console .cc-biz__stages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lb-console .cc-biz__stage {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
  font-size: 0.83rem;
}
.lb-console .cc-biz__stagemark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  color: #15803d;
}
.lb-console .cc-biz__stagemark .cc-i { width: 16px; height: 16px; }
.lb-console .cc-biz__stagering {
  width: 14px;
  height: 14px;
  border: 1.5px solid #d5d9df;
  border-radius: 50%;
}
/* The one stage that is happening spins its ring. Not a claim about how far
   through it is — a claim that it is running, which is true. */
.lb-console .cc-biz__stage[data-state="active"] .cc-biz__stagering {
  border-color: #d5d9df;
  border-top-color: var(--text, #111827);
  animation: cc-biz-spin 720ms linear infinite;
}
@keyframes cc-biz-spin { to { transform: rotate(360deg); } }
.lb-console .cc-biz__stagename {
  min-width: 0;
  color: var(--muted, #6b7280);
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__stage[data-state="active"] .cc-biz__stagename {
  color: var(--text, #111827);
  font-weight: 600;
}
.lb-console .cc-biz__stage[data-state="done"] .cc-biz__stagename {
  color: var(--text, #111827);
}
.lb-console .cc-biz__stagestate {
  color: var(--muted, #6b7280);
  font-size: 0.76rem;
}

.lb-console .cc-biz__progfiles {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
}
.lb-console .cc-biz__progfile {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  font-size: 0.82rem;
}
.lb-console .cc-biz__progfile + .cc-biz__progfile {
  border-top: 1px solid var(--line, #e5e7eb);
}
.lb-console .cc-biz__progfile .cc-biz__filename { font-weight: 500; }
.lb-console .cc-biz__progfile .cc-biz__filemeta { margin: 0; }
.lb-console .cc-biz__filestate {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  white-space: nowrap;
}
.lb-console .cc-biz__progfile[data-state="read"] .cc-biz__filestate { color: #15803d; }
.lb-console .cc-biz__filetick { display: inline-flex; }
.lb-console .cc-biz__filetick .cc-i { width: 15px; height: 15px; }

.lb-console .cc-biz__counts {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  background: var(--token-surface-secondary, #f9fafb);
}
.lb-console .cc-biz__count {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.lb-console .cc-biz__count + .cc-biz__count {
  padding-left: 10px;
  border-left: 1px solid var(--line, #e5e7eb);
}
.lb-console .cc-biz__counticon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  background: #fff;
  color: #4b5563;
}
.lb-console .cc-biz__counticon .cc-i { width: 15px; height: 15px; }
.lb-console .cc-biz__countnum {
  color: var(--text, #111827);
  font-size: 0.9rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-biz__countlabel {
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
}

/* ── AND FOR SOMEBODY WHO HAS ASKED FOR LESS OF IT ──────────────────────
   Four things on this panel move forever: the character breathes, the
   indeterminate bar sweeps, the active stage's ring spins, and the heading
   shimmers. All four exist to say "this is still working", and none of them
   carries a fact — which is exactly the kind of motion a reduced-motion
   setting is asking to be spared, and exactly the kind this stylesheet
   already stops everywhere else it animates (see .cc-read__track and
   .cc-speed__dot).

   ── STOPPED, NOT HIDDEN ────────────────────────────────────────────────
   The bar keeps its fill and the ring keeps its shape, so the panel still
   reads as a wait with a stage in progress; it simply holds still. Hiding
   them would take away the state as well as the movement, and the state is
   the honest half. */
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-labbie[data-state="working"],
  .lb-console .cc-biz__bar--idle .cc-biz__barfill,
  .lb-console .cc-biz__stage[data-state="active"] .cc-biz__stagering,
  .lb-console .cc-biz__analysingtitle {
    animation: none;
  }
  /* The sweep is a 38%-wide fill translating across the track, so simply
     stopping it parks that fill at the left edge — an indeterminate wait
     that now looks exactly like a measured 38%, which is the one thing this
     panel may never say. Filled instead: an unmoving full-width band claims
     no fraction at all. */
  .lb-console .cc-biz__bar--idle .cc-biz__barfill { width: 100%; }
  /* Stopping the gradient's animation alone would freeze it on whichever
     slice of the band it happened to be on — sometimes solid, sometimes
     half washed out. Removed outright instead: the same flat heading colour
     every other state on this step already uses. */
  .lb-console .cc-biz__analysingtitle {
    background-image: none;
    -webkit-background-clip: initial;
    background-clip: initial;
    color: var(--text, #111827);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE REVIEW — A CARD PER CATEGORY, OPEN
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-biz--review {
  width: 100%;
  max-width: 760px;
  gap: 0;
}
.lb-console .cc-biz--review button { font-weight: 600; }
.lb-console [data-biz-review] { min-width: 0; }
.lb-console .cc-biz--review .cc-biz__srcstate { margin-bottom: 14px; }
.lb-console .cc-biz__cards {
  width: 100%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.lb-console .cc-biz__card {
  min-width: 0;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
}
.lb-console .cc-biz__cardhead {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid #f0f1f3;
}
.lb-console .cc-biz__cardicon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  background: var(--token-surface-secondary, #f9fafb);
  color: #4b5563;
}
.lb-console .cc-biz__cardicon .cc-i { width: 16px; height: 16px; }
/* Weight 500 and 16px, which is what the accordion's category label used and
   what coldcalling-step-two-merge-check.js pins: a heading in this console is
   medium, never bold. The base .cc-biz__heading rule is a small uppercase
   eyebrow — what a section label was when the review was an accordion — and a
   card heading is a heading: "Opening hours", not "OPENING HOURS". */
.lb-console .cc-biz__card .cc-biz__heading {
  margin: 0;
  min-width: 0;
  color: #111827;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-transform: none;
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__optional {
  margin-left: auto;
  color: var(--muted, #6b7280);
  font-size: 0.74rem;
}
.lb-console .cc-biz__updated {
  margin-left: auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--token-surface-secondary, #f3f4f6);
  color: #4b5563;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.lb-console .cc-biz__card > .cc-biz__facts {
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 0;
}
.lb-console .cc-biz__card .cc-biz__fact {
  display: block;
  min-width: 0;
  padding: 12px 16px;
  /* The card carries the border and the radius. A row that kept its own
     would draw a box inside a box on every fact. */
  border: 0;
  border-radius: 0;
  overflow: visible;
}
.lb-console .cc-biz__card .cc-biz__fact + .cc-biz__fact {
  border-top: 1px solid #f0f1f3;
}
.lb-console .cc-biz__cardfoot {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px 10px 10px;
}
.lb-console .cc-biz__cardfoot:empty { display: none; }
.lb-console .cc-biz__card .cc-biz__add {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 500;
}

/* ── A FACT, AS A FIELD AND A VALUE ─────────────────────────────────────
   The label on the left in the muted grey, the value on the right in the
   text colour. That ordering is the point of the redesign: a wrong price is
   visible when it is a value in a column of values and buried when it is a
   clause in a sentence. */
.lb-console .cc-biz__pair {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.3fr);
  align-items: baseline;
  gap: 4px 16px;
  min-width: 0;
}
.lb-console .cc-biz__pair > span,
.lb-console .cc-biz__pair > strong { min-width: 0; overflow-wrap: anywhere; }
.lb-console .cc-biz__pair > span,
.lb-console .cc-biz__rowlabel {
  color: var(--muted, #6b7280);
  font-size: 0.8rem;
  font-weight: 400;
}
.lb-console .cc-biz__pair > strong {
  color: var(--text, #111827);
  font-size: 0.85rem;
  font-weight: 500;
}
.lb-console .cc-biz__body {
  margin: 0;
  color: var(--text, #111827);
  font-size: 0.85rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__faqread { margin: 0; }
.lb-console .cc-biz__faqread dt {
  margin-top: 8px;
  color: var(--muted, #6b7280);
  font-size: 0.74rem;
}
.lb-console .cc-biz__faqread dt:first-child { margin-top: 0; }
.lb-console .cc-biz__faqread dd {
  margin: 2px 0 0;
  color: var(--text, #111827);
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
/* ── THE ROW'S OWN CONTROLS ─────────────────────────────────────────────
   Quiet until they are wanted. On a pointer they appear when the row is
   hovered or something inside it has focus; on touch, and for anybody
   moving by keyboard, they are simply always there — a control that only
   exists on hover is a control a phone does not have. */
.lb-console .cc-biz__factfoot {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
}
.lb-console .cc-biz__factfoot .btn-quiet {
  font-size: 0.76rem;
  font-weight: 500;
}
.lb-console .cc-biz__drop { margin-left: auto; }

/* ── THE CONTROLS SIT BESIDE THE FACT, NOT UNDER IT ─────────────────────
   A row is one line of information, and it should be one line tall. Stacking
   View source / Edit / Remove underneath every value made each fact three
   lines high and a card of four services as tall as the viewport — the exact
   "giant tall cards, excessive whitespace" the redesign was asked to avoid.

   They move into a second column instead. The column is reserved whether or
   not they are visible, which is what lets the hover reveal below cost
   nothing: opacity changes, layout does not, and no row moves under the
   pointer as it crosses the card. */
.lb-console .cc-biz__card .cc-biz__factread {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
}
.lb-console .cc-biz__card .cc-biz__factread > .cc-biz__pair,
.lb-console .cc-biz__card .cc-biz__factread > .cc-biz__body,
.lb-console .cc-biz__card .cc-biz__factread > .cc-biz__faqread { grid-column: 1; }
.lb-console .cc-biz__card .cc-biz__factread > .cc-biz__factfoot {
  grid-column: 2;
  grid-row: 1;
  margin-top: 0;
  justify-self: end;
}
/* Quiet until wanted — on a pointer. On touch, and for anybody moving by
   keyboard, they are simply always there: a control that exists only on
   hover is a control a phone does not have. */
@media (hover: hover) and (pointer: fine) {
  .lb-console .cc-biz__card .cc-biz__factfoot { opacity: 0; transition: opacity 120ms ease; }
  .lb-console .cc-biz__card .cc-biz__fact:hover .cc-biz__factfoot,
  .lb-console .cc-biz__card .cc-biz__fact:focus-within .cc-biz__factfoot { opacity: 1; }
}
.lb-console .cc-biz__sourcebtn { font-size: 0.76rem; }
.lb-console .cc-biz__quote {
  margin: 8px 0 0;
  padding: 8px 12px;
  border-left: 2px solid var(--line, #e5e7eb);
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.lb-console .cc-biz__quote[hidden],
.lb-console .cc-biz__factread[hidden],
.lb-console .cc-biz__editor[hidden] { display: none; }

.lb-console .cc-biz__editor { min-width: 0; }
.lb-console .cc-biz__pairform,
.lb-console .cc-biz__faq {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 8px 12px;
  min-width: 0;
}
.lb-console .cc-biz__pairform .lb-input,
.lb-console .cc-biz__faq .lb-input { min-width: 0; width: 100%; box-sizing: border-box; }
.lb-console .cc-biz__faq textarea { grid-column: 1 / -1; }
.lb-console .cc-biz__faq > .lb-label { margin-top: 4px; }
.lb-console .cc-biz__faq > .lb-label:first-child { margin-top: 0; }
.lb-console .cc-biz__editorfoot {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}
.lb-console .cc-biz__editorfoot .btn-dark,
.lb-console .cc-biz__editorfoot .btn-quiet { min-height: 34px; }

/* ── MISSING INFORMATION ────────────────────────────────────────────────
   Compact rows, no hierarchy of alarm, and an Add on each. It is a list of
   things somebody MAY want to say, not a list of things they got wrong. */
.lb-console .cc-biz__missinglist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.lb-console .cc-biz__missingitem {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
}
.lb-console .cc-biz__missingitem + .cc-biz__missingitem {
  border-top: 1px solid #f0f1f3;
}
.lb-console .cc-biz__missingname,
.lb-console .cc-biz__missinghint { display: block; }
.lb-console .cc-biz__missingname {
  color: var(--text, #111827);
  font-size: 0.83rem;
  font-weight: 500;
}
.lb-console .cc-biz__missinghint {
  margin-top: 1px;
  color: var(--muted, #6b7280);
  font-size: 0.75rem;
  line-height: 1.4;
}
.lb-console .cc-biz__missingitem > .cc-biz__facts {
  grid-column: 1 / -1;
  width: 100%;
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
}

/* .cc-biz__suggestion, .cc-biz__comparelabel, .cc-biz__comparevalue and
   the .cc-biz__pendingaction row were the second half of the same card.
   Removed with it rather than left as rules no selector can reach. */

/* ── A FAILURE, INSIDE THE STEP ─────────────────────────────────────────
   Not a screen of its own and not a toast that disappears. It is a panel at
   the top of the Sources tab, and everything underneath it — the drop zone,
   the box, the files already handed over — is untouched, because a failed
   reading changed none of them. */
/* ── IN THE PRODUCT'S ONE RED, AND NOTHING ELSE ────────────────────────
   The shared .btn-danger's exact treatment: the card colour underneath, a
   hairline of the one red at low alpha, and the red itself carried by the
   words. It was a pink wash with two invented off-reds for one draft, which
   is three colours this console does not have — and a filled red panel is
   also louder than the event deserves, since a failed reading changed
   nothing and the sentence under the title says so. */
.lb-console .cc-biz__failed {
  padding: 14px 16px;
  border: 1px solid rgba(185, 28, 28, 0.28);
  border-radius: 12px;
  background: var(--card, #fff);
}
.lb-console .cc-biz__failedtitle {
  margin: 0;
  color: #b91c1c;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.35;
}
.lb-console .cc-biz__failedtext {
  margin: 5px 0 0;
  color: var(--muted, #6b7280);
  font-size: 0.82rem;
  line-height: 1.5;
}
.lb-console .cc-biz__retry { margin-top: 12px; }
.lb-console .cc-biz__failed .cc-biz__input { margin-top: 26px; text-align: left; }

/* ══════════════════════════════════════════════════════════════════════════
   WHILE A DOCUMENT IS BEING READ

   ── WHAT THIS BLOCK IS TRYING TO BE ────────────────────────────────────
   A person who has just handed over their price list waits. What they are
   shown is their OWN text moving past — evidence that something has hold of
   the thing they sent, rather than a spinner that would look identical if
   nothing were happening at all.

   So the column is deliberately not a document viewer. It is short, it has
   no scrollbar and it cannot be scrolled: it is not for reading through, it
   is for RECOGNISING. One band of it is sharp and everything above and below
   softens away, which is what stops the eye trying to read four lines at
   once and lets it rest on the one passing through the middle.

   ── AND WHY IT IS ALL GREY ─────────────────────────────────────────────
   Black, white and grey, nothing else. Every other colour in this console
   carries a meaning — an error is red, an action is dark, a state is a
   badge — and a wait means none of them. Colour here would be the interface
   claiming something about text it has not yet understood.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE ONE GREY THIS BLOCK ADDS, NAMED ──────────────────────────────────
   Declared rather than written inline for the reason every other value in
   this file is: a colour that appears twice as a literal is a colour that
   will one day appear twice as two different literals. It is neutral — equal
   parts red, green and blue to within a point — which is the whole of
   "black, white and grey only" said in numbers.

   --cc-read-surface  the column's ground. A shade off the card behind it, so
                      it reads as a well rather than as a bordered rectangle.

   The heading's light needs no grey of its own: it is white at an alpha, laid
   OVER the ink rather than substituted for it. */
.lb-console .cc-read {
  --cc-read-surface: #f7f7f8;
  width: 100%;
  text-align: left;
  padding-top: 4px;
}

/* ── THE CHARACTER, THE SENTENCE AND THE CLOCK ────────────────────────────
   One row, baseline-aligned, so the seconds sit ON the sentence rather than
   under it. */
.lb-console .cc-read__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
}

/* The animated Labbie, at its own size and in nothing. NOT inside a circle,
   a tile or a coloured well: the artwork is already a shape, and putting a
   shape inside a second one is the mark of a character that was drawn for
   somewhere else. */
.lb-console .cc-read__avatar {
  display: block;
  flex: none;
  width: 48px;
  height: 48px;
  /* The file is a 250×250 square with the character filling it. No radius,
     no background, no border, no shadow. */
  border-radius: 0;
  background: none;
}

/* ── THE HEADING, AND THE LIGHT THAT CROSSES IT ───────────────────────────
   The sentence is painted once, in the ordinary ink, and never touched
   again. Everything that moves is a SECOND copy stacked over it, carrying
   nothing but a band of light.

   ── WHY IT IS NOT ONE LAYER WITH A GRADIENT IN IT ──────────────────────
   Because a gradient clipped to the letterforms IS the letters: the pale
   stop is not a highlight lying over the word, it is the colour of the word
   while the band is on it. The heading read "Labbie is analy" for a third of
   every cycle. Two layers makes the requirement — the whole sentence legible
   in every frame — a property of the structure, rather than a hope about how
   light a grey can get before it stops being there. */
.lb-console .cc-read__title {
  position: relative;
  display: inline-block;
  margin: 0;
  font-size: 0.98rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  color: var(--text);
  /* The two copies have to break identically, or the light lands between the
     letters instead of on them. Neither wraps. */
  white-space: nowrap;
}

/* THE PERMANENT LAYER. No animation, no clip, no mask, no opacity — nothing
   that could take a letter away. */
.lb-console .cc-read__titletext {
  position: relative;
  z-index: 0;
  color: var(--text);
}

/* THE MOVING LAYER. Off unless the browser can clip a background to text:
   otherwise its gradient would paint as a rectangle across the heading, so
   not drawing it at all is the safe default. */
.lb-console .cc-read__sheen { display: none; }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .lb-console .cc-read__sheen {
    display: block;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
    pointer-events: none;
    white-space: nowrap;
    /* Transparent everywhere but a narrow band, so most of the sentence is
       untouched at any instant — and where the band IS, it adds white to
       what is already painted. It cannot darken and it cannot erase.

       White at zero alpha (#ffffff00) and white at 60% (#ffffff99, since
       0x99 is 153 of 255). Written as 8-digit hex rather than rgba() because
       `rgba(255, 255, 255, …)` reads as a blue to the no-blue guard in
       coldcalling-agent-profile-check.js, which tests the blue channel
       without asking whether red and green are just as high. */
    background-image: linear-gradient(100deg,
      #ffffff00 42%,
      #ffffff99 50%,
      #ffffff00 58%);
    background-size: 300% 100%;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: cc-read-sheen 2.8s linear infinite;
  }
}

@keyframes cc-read-sheen {
  from { background-position: 140% 0; }
  to   { background-position: -140% 0; }
}

/* The seconds. Quiet, and tabular so the row does not twitch sideways as the
   digits change. */
.lb-console .cc-read__elapsed {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ── THE COLUMN ───────────────────────────────────────────────────────────
   Compact on purpose. Tall enough for the fade at each end to have somewhere
   to happen and for two or three lines to be legible between them; short
   enough that it reads as a status, not as a card of content. */
.lb-console .cc-read__box {
  position: relative;
  height: 188px;
  overflow: hidden;
  /* The inset is on the LAYERS, not here. An absolutely positioned child
     resolves left/right against its containing block's padding box, so a
     padding on both would be applied twice. */
  box-sizing: border-box;
  background: var(--cc-read-surface);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-xl, 16px);
  /* The duration wireStepTwo() measures. The fallback is only what is used
     for the frame before it measures. */
  --cc-read-dur: 18s;
}

/* ── ONE SURFACE, TWO LAYERS OF IT ────────────────────────────────────────
   Identical content, identical animation, complementary masks: the sharp one
   is revealed across the middle and the blurred one at the two ends, so the
   text softens as it approaches either edge and comes back into focus as it
   returns to the centre.

   They cannot drift apart. Both are written in the same innerHTML, so both
   animations begin on the same frame, and both take their duration from the
   same custom property on the box above. */
/* ── AND WHY EACH LAYER IS EXACTLY THE WINDOW ─────────────────────────────
   A mask gradient's percentages are read against the masked ELEMENT, not
   against what you can see of it. A layer left to size itself would be as
   tall as the whole track — a thousand-odd pixels of text — and "sharp from
   37% to 63%" would then describe a band four hundred pixels below the
   bottom edge of the box. Everything visible would sit in the first fifth of
   the gradient, which is the transparent end, and the entire column would be
   faint. It was, before this line was added.

   So the layer is pinned to all four sides of the box and clips: the mask
   now measures the 188 pixels somebody is actually looking at, and the track
   scrolls THROUGH it. */
.lb-console .cc-read__layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  padding: 0 22px;
  box-sizing: border-box;
}

.lb-console .cc-read__layer--sharp {
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, transparent 20%,
    #000 37%, #000 63%,
    transparent 80%, transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0%, transparent 20%,
    #000 37%, #000 63%,
    transparent 80%, transparent 100%);
}

.lb-console .cc-read__layer--soft {
  filter: blur(2.6px);
  -webkit-mask-image: linear-gradient(to bottom,
    rgba(0, 0, 0, 0.28) 0%, rgba(0, 0, 0, 0.72) 15%,
    transparent 39%, transparent 61%,
    rgba(0, 0, 0, 0.72) 85%, rgba(0, 0, 0, 0.3) 100%);
  mask-image: linear-gradient(to bottom,
    rgba(0, 0, 0, 0.28) 0%, rgba(0, 0, 0, 0.72) 15%,
    transparent 39%, transparent 61%,
    rgba(0, 0, 0, 0.72) 85%, rgba(0, 0, 0, 0.3) 100%);
}

/* ── THE LOOP WITH NO SEAM ────────────────────────────────────────────────
   The track holds the lines twice and travels up by exactly half its height.
   At -50% the pixels under the box are the pixels that were there at 0%, so
   the restart is invisible.

   ── WHICH IS WHY THE SPACING IS PADDING AND NOT MARGIN ─────────────────
   Adjacent margins collapse and a trailing margin can escape its parent, so
   a track spaced with margins is not reliably twice the height of one copy —
   and -50% of the wrong height is a jump, once per loop, forever. Padding
   does neither. */
.lb-console .cc-read__track {
  animation: cc-read-scroll var(--cc-read-dur, 18s) linear infinite;
  will-change: transform;
}

.lb-console .cc-read__line {
  margin: 0;
  padding: 0 0 15px;
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--text);
  /* A pasted URL or an unbroken product code must not widen the box. */
  overflow-wrap: anywhere;
}

@keyframes cc-read-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -50%, 0); }
}

/* A breath at the wrap, so the last line does not run straight into the
   first. It has to be the SAME on both copies or the two halves stop being
   the same height and the loop grows a seam — which is the whole reason this
   is one rule and not two. It sits inside the top fade, so it costs no
   visible space. */
.lb-console .cc-read__copy { padding-top: 26px; }

/* ── SOMEBODY WHO HAS ASKED FOR LESS MOVEMENT ─────────────────────────────
   Gets the same text and none of the motion. The column stops, the second
   copy is removed rather than left stacked under the first, the blur and the
   masks come off, and what is left is the opening of their document sitting
   still — which is the same evidence, delivered without anything travelling
   across the screen.

   The character keeps its own animation: it is a 48px face blinking in
   place, it is what the brief asks to loop for as long as the analysis runs,
   and it moves nothing across the field of view. */
@media (prefers-reduced-motion: reduce) {
  /* The moving layer goes entirely. Nothing has to be undone on the heading
     itself, because nothing was ever done to it — which is the second reason
     the sheen is its own element. */
  .lb-console .cc-read__sheen { display: none; }
  .lb-console .cc-read__track { animation: none; }
  .lb-console .cc-read__copy + .cc-read__copy { display: none; }
  .lb-console .cc-read__copy { padding-top: 18px; }
  .lb-console .cc-read__layer--soft { display: none; }
  /* The band comes off — with nothing moving there is no reason to soften
     anything the eye is not chasing — but the BOTTOM fade stays. A fixed
     window with no scrollbar ends mid-sentence whatever it does, and a line
     sliced in half by a border reads as a rendering fault where the same line
     fading out reads as "there is more of this". A gradient is not motion. */
  .lb-console .cc-read__layer--sharp {
    -webkit-mask-image: linear-gradient(to bottom,
      #000 0%, #000 74%, transparent 100%);
    mask-image: linear-gradient(to bottom,
      #000 0%, #000 74%, transparent 100%);
  }
  .lb-console .cc-swap__ghost { animation: none; opacity: 0; }
  .lb-console .cc-swap__in { animation: none; }
}

/* ── THE SWAP OUT OF THE WAIT ─────────────────────────────────────────────
   The outgoing markup is held over the panel for the length of one fade
   while the review draws underneath it. Absolutely positioned so the two do
   not stack and push the card to twice its height for a fifth of a second.
   Nothing is clickable on the way out. */
.lb-console .cc-swap { position: relative; }
.lb-console .cc-swap__ghost {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  animation: cc-swap-out 240ms ease forwards;
}
.lb-console .cc-swap__in { animation: cc-swap-in 260ms ease both; }

@keyframes cc-swap-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes cc-swap-in  { from { opacity: 0; } to { opacity: 1; } }

/* ── WHY CONTINUE IS DISABLED ─────────────────────────────────────────────
   Beside the button rather than inside a tooltip. A disabled control with no
   stated reason is the interface refusing without saying why, and a reason
   that only appears on hover is not there at all for somebody on a phone. */
.lb-console .cc-biz__whynot {
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--muted);
  max-width: 30ch;
  text-align: right;
}

/* ══════════════════════════════════════════════════════════════════════
   CAMPAIGNS PAGE — Gate 5B final UI (Master Plan v4 §19.10, Appendix G)

   Composed from the shell the rest of this console uses: .stat-card,
   .section-card, the underline .cc-tab, .stat-badge, the drawer and the
   shared buttons. Every rule is scoped to .lb-console and prefixed
   cc-camp-, and every colour, radius and shadow reads a token already
   declared by labbie-dashboard-shell.css. The Trash Bin tab reuses the
   Trash screen's own .cc-trash-* cards unchanged.

   Icon colours are Platform Design System V1 §10's semantic mapping, read
   from frontend/design-system/tokens/tokens.css (--lds-icon-color-*). This
   console does not load that stylesheet yet, so each token carries its own
   value as the fallback — the same pattern this file already uses for
   --lds-color-green — rather than a second definition of it.
   ══════════════════════════════════════════════════════════════════════ */

/* Every card spans three of the summary's rows: a label row, a number row
   and a note row. The cards on one line SHARE those rows (subgrid, below).
   The space between lines of cards is each card's bottom margin, not a row
   gap, because a row gap would also open inside the cards. The 12px card
   margin plus this 4px make the 16px the summary always had below it. */
.lb-console .cc-camp-summary {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  column-gap: 12px;
  row-gap: 0;
  margin: 0 0 4px;
}

/* ONE GRID FOR ALL SIX CARDS, WHATEVER EACH ONE SAYS. The label is row 1, the
   number row 2, and the icon is centred across those two rows. A card whose
   count could not be read adds its note as row 3, underneath. The three rows
   are the summary's own (`subgrid`), so on a line of cards every label row
   is as tall as the tallest label on it. A label that wraps to two lines
   therefore moves no number, icon or note out of line with its neighbours.
   Before, the card's content was centred as a whole: a summary row stretches
   every card to its tallest one, so a two-line card centred in a three-line
   height sat lower than the card next to it. The body is `display: contents`,
   so its lines are the grid's own items. It is a plain div with no role, so a
   screen reader loses nothing. A browser without subgrid keeps the three rows
   as the card's own, packed to its top by `align-content: start`. */
.lb-console .cc-camp-stat {
  grid-row: span 3;
  display: grid;
  grid-template-rows: subgrid;
  grid-template-columns: 24px minmax(0, 1fr);
  align-content: start;
  align-items: center;
  column-gap: 12px;
  min-width: 0;
  margin: 0 0 12px;
}
.lb-console .cc-camp-stat__body { display: contents; }
/* Every label starts at the top of its row, so a one-line label lines up with
   the first line of a two-line label beside it. */
.lb-console .cc-camp-stat .stat-label {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  margin-bottom: 2px;
  white-space: normal;
}
.lb-console .cc-camp-stat .stat-value {
  grid-column: 2;
  grid-row: 2;
  font-size: 1.5rem !important;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-camp-stat .stat-value[data-empty="true"] { color: var(--muted, #6b7280); }
.lb-console .cc-camp-stat__note {
  grid-column: 2;
  grid-row: 3;
  margin-top: 2px;
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--muted, #6b7280);
}

/* The icon stands on the card by itself, in its colour — the approved
   reference draws no pale rounded square behind it, at any width. This slot
   is exactly the icon, so nothing here can paint one back. */
.lb-console .cc-camp-stat__icon {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--lds-icon-color-inactive, #8E8E93);
}
/* The universal 24px interface icon. */
.lb-console .cc-camp-stat__icon .cc-i { width: 24px; height: 24px; }
.lb-console .cc-camp-stat__icon[data-tone="information"] {
  color: var(--lds-icon-color-information, #007AFF);
}
.lb-console .cc-camp-stat__icon[data-tone="success"] {
  color: var(--lds-icon-color-success, #34C759);
}
.lb-console .cc-camp-stat__icon[data-tone="communication"] {
  color: var(--lds-icon-color-communication, #30B0C7);
}
.lb-console .cc-camp-stat__icon[data-tone="warning"] {
  color: var(--lds-icon-color-warning, #FF9500);
}
.lb-console .cc-camp-stat__icon[data-tone="inactive"] {
  color: var(--lds-icon-color-inactive, #8E8E93);
}

/* ── The first-run hero: "No campaigns yet" ──────────────────────────────
   One centred group — the owner's megaphone, the heading, one sentence and
   Add Campaign — in the approved reference's proportions, centred across
   AND down its own large card (owner, 26 Sep — this replaces the 25 Sep
   placement near the panel's top, which left the group high in a tall
   card). On a desktop the panel grows to the foot of the page and centres
   the group in that height (the responsive block below); a phone centres it
   in a panel of sensible height. No pixel position is fixed and no script
   measures anything. The heading and sentence are sized here, for this hero
   only — every other empty state keeps the shared ones. This padding is a
   desktop's, even above and below so the group's middle is its content's
   middle; a phone sets its own below. */
.lb-console .cc-camp-empty { padding: 26px 24px; }
/* Until its megaphone can be drawn, the group is laid out but not shown,
   so the picture arrives WITH its heading and never after it (owner,
   26 Sep). See holdHeroForArt in coldcalling.js. */
.lb-console .cc-camp-empty--waiting { visibility: hidden; }
.lb-console .cc-camp-empty .cc-camp-empty__art { margin-bottom: 20px; }
/* ── ONE EMPTY-STATE HEADING AND SENTENCE FOR THE THREE BIG EMPTY STATES ──
   "No campaigns yet" is the authority (owner, 1 Oct): Needs Attention's
   "No unanswered leads waiting" and the Leads Library's "No leads yet" are
   the same kind of screen and are set by THIS rule, not by a copy of its
   values — font, size, weight, line-height, letter-spacing and colour for
   the heading, and for the sentence under it. Each screen keeps only its
   own spacing and measure. */
.lb-console .cc-attn-empty .lb-empty__title,
.lb-console .cc-ll-empty .lb-empty__title,
.lb-console .cc-camp-empty .lb-empty__title {
  margin: 0 0 6px;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text, #111827);
  /* A heading that wraps on a phone wraps evenly ("No unanswered leads /
     waiting" left one word alone). One line is unaffected. */
  text-wrap: balance;
}
/* The sentence's colour is the shared empty-state sentence's own, stated so
   the Leads Library's navy no longer differs from it. 25rem is "No
   campaigns yet"'s measure: it breaks that sentence where the approved
   reference does, "…organizing your / outbound calls.", instead of leaving
   "calls." alone on a line. Needs Attention and the Leads Library restate
   only their own measure, in their own sections below. */
.lb-console .cc-attn-empty .lb-empty__text,
.lb-console .cc-ll-empty .lb-empty__text,
.lb-console .cc-camp-empty .lb-empty__text {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--muted, #6b7280);
  max-width: 25rem;
}
.lb-console .cc-camp-empty .lb-empty__action { margin-top: 24px; }

/* ── The card ────────────────────────────────────────────────────────── */
.lb-console .cc-camp-section { padding: 8px 20px 20px !important; }

/* ── The control row: the lifecycle switch, and Add Campaign ─────────────
   Above the card, on one line (owner's reference, 6 Oct): the switch on the
   left, Add Campaign on the right, and the same top and bottom edge for
   both — the switch's track is as tall as the button, by the button's own
   token.

   THE SWITCH IS NEEDS ATTENTION'S, EXACTLY. The markup carries
   .cc-attn-switch, so the track, the raised selected segment, the font, its
   size and weight, the segment's padding, the two greys and both shadows
   are that one rule, not a copy of it (owner, 6 Oct: the same pill, the
   same font, the same font size). What is set here is only what five tabs
   with counts beside a button need and two tabs alone do not: the track's
   height, the count, and a way to wrap. */
.lb-console .cc-camp-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 16px;
  margin: 12px 0 20px;
}
/* On a line of its own, the button keeps the right edge. */
.lb-console .cc-camp-bar > .cc-camp-cta {
  flex: none;
  margin-left: auto;
}

.lb-console .cc-tabs.cc-camp-tabs {
  /* WRAP, not a hidden horizontal scroller. Browser acceptance at ~800px
     showed a scroller with no visible scrollbar hides the Trash Bin tab from
     a pointer user entirely; a second row keeps every lifecycle tab in view
     and never widens the page. Half the track's height is a full round end
     on one row and a rounded corner, not a bite out of a tab, on two. */
  box-sizing: border-box;
  flex: 0 1 auto;
  flex-wrap: wrap;
  align-items: center;
  min-width: 0;
  min-height: var(--lds-button-height, 48px);
  border-radius: calc(var(--lds-button-height, 48px) / 2);
}
/* Outranks .cc-attn-switch .cc-tab, which is later in this file. The 8px is
   that rule's own 3px of track padding and 1px of border, twice. */
.lb-console .cc-attn-switch.cc-camp-tabs .cc-tab {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: calc(var(--lds-button-height, 48px) - 8px);
}

/* ── The count: a white disc beside its label ────────────────────────────
   The owner's drawing, to its own proportions: a disc 95 units across
   holding a 60-unit Inter Medium number, under a -2 / 5 / 10-unit shadow at
   25% black. --cc-camp-u is one of those units, set so the number is the
   0.72rem every count in this console already is. A circle for one or two
   figures; a third lengthens it into a capsule rather than spilling out of
   it. The number is centred by the box, not by a line-height chosen to look
   centred. A tab that is not selected shows its number in its label's grey,
   as drawn; the selected one in black. */
.lb-console .cc-camp-tabs .cc-count {
  --cc-camp-u: calc(0.72rem / 60);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: calc(95 * var(--cc-camp-u));
  height: calc(95 * var(--cc-camp-u));
  margin-left: calc(33 * var(--cc-camp-u));
  padding: 0 calc(8 * var(--cc-camp-u));
  border-radius: 999px;
  background: var(--card, #fff);
  box-shadow: calc(-2 * var(--cc-camp-u)) calc(5 * var(--cc-camp-u)) calc(10 * var(--cc-camp-u)) rgba(0, 0, 0, 0.25);
  color: inherit;
  font-size: calc(60 * var(--cc-camp-u));
  font-weight: 500;
  font-variant-numeric: normal;
  line-height: 1;
}
.lb-console .cc-camp-tabs .cc-tab.is-on .cc-count {
  background: var(--card, #fff);
  color: #000;
}

/* The Trash Bin's retention sentence, at the top of the card it describes.
   Its 12px above is what the card's own padding is short of 20px. */
.lb-console .cc-camp-note {
  margin: 12px 0;
  font-size: 0.82rem;
  color: var(--muted, #6b7280);
  text-align: right;
}

.lb-console .cc-camp-panel:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 4px;
  border-radius: 4px;
}

/* ── The cards ───────────────────────────────────────────────────────── */
.lb-console .cc-camp-grid {
  /* Flex, not a fractional grid track, for the reason .cc-trash-grid gives:
     one long unbreakable name must not set a track's floor. */
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ── AN ITEM, AND THE TILE INSIDE IT ──────────────────────────────────
   The approved composition writes a Waiting Campaign's place in the queue
   UNDER its dark tile — "Next", "After Campaign 2" — on the page's own
   ground rather than inside the tile. So the list item is a transparent
   column holding two things: the tile, and the caption beneath it. The
   item is what the flex row sizes and reflows; the tile is what is drawn. */
.lb-console .cc-camp-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 240px;
  max-width: 320px;
  min-width: 0;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
}
.lb-console .cc-camp-card__tile {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  padding: 14px 16px;
  border-radius: var(--radius-lg, 14px);
}

.lb-console .cc-camp-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.lb-console .cc-camp-card__title {
  min-width: 0;
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-card__title .cc-linkish {
  font: inherit;
  color: var(--text, #111827);
  text-align: left;
}
/* ── TWO LINES, THEN AN ELLIPSIS ──────────────────────────────────────
   These cards sit in a flex row that equalises their heights, so ONE long
   name makes every card in the row as tall as the longest — which is the
   card growing absurdly tall by proxy. Two lines is the ceiling.

   Clamping is VISUAL ONLY. The text stays whole in the DOM, so the button's
   accessible name is still the full Campaign name, `title` gives a sighted
   reader the same thing on hover, and the Campaign's own screen shows it in
   full. Nothing is truncated anywhere it would be the only copy. */
.lb-console .cc-camp-card__title .cc-linkish {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.lb-console .cc-camp-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
}

.lb-console .cc-camp-card__sub {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--muted, #6b7280);
  overflow-wrap: anywhere;
}

.lb-console .cc-camp-progress__text {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin: 0 0 6px;
  font-size: 0.82rem;
  color: var(--muted, #6b7280);
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-camp-progress__track {
  height: 6px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--token-surface-secondary, #f3f4f6);
  box-shadow: inset 0 0 0 1px var(--line, #e5e7eb);
}
.lb-console .cc-camp-progress__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent, #111827);
  transition: width 200ms ease;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE CAMPAIGN CARD IS DARK

   The approved composition draws a Campaign as a near-black tile — the same
   in every lifecycle tab, which is what makes a Campaign recognisable as one
   object wherever it has got to. That is a GROUND, and everything below is
   the consequence of changing it: each rule here re-states a colour that was
   readable on white and would not be on black.

   Nothing else changes. The card still carries the server's state badge, the
   server's progress and the server's reason for dialling nothing — a colour
   is not a reason to show less — and the title is still the same button that
   opens the Campaign.

   Contrast: white on #101012 is about 18:1, and the 70% and 60% whites used
   for secondary text are about 11:1 and 8:1. All well past 4.5:1.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-camp-card__tile {
  /* Compact, as drawn. A Campaign tile is an index entry, not a panel. */
  background: #101012;
  border: 1px solid #101012;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.16);
  color: #fff;
}
/* The second line — "Sarah • Lead 1" — on one line, ellipsed, never wrapping
   the tile taller; the goal beneath it where the server sent one. */
.lb-console .cc-camp-card__ctx {
  margin: 0;
  min-width: 0;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-console .cc-camp-card__goal {
  margin: 0;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.6);
  overflow-wrap: anywhere;
}
/* The queue caption, under the tile, on the page's ground: the reference's
   "Next" and "After Campaign 2", printed only from the server's position. */
.lb-console .cc-camp-card__queue {
  margin: 0;
  padding: 0 6px;
  text-align: center;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted, #6b7280);
  overflow-wrap: anywhere;
}
/* `inherit`, not a white of its own. .cc-linkish carries no hue anywhere in
   this product — it takes the ink of whatever it sits on, and here that is
   the card's. */
.lb-console .cc-camp-card__title .cc-linkish { color: inherit; }
/* ── THE WHOLE TILE OPENS THE CAMPAIGN (owner, 4 October 2026) ─────────
   Not a second way in. The name is still the card's one button, and its
   ::after is stretched over the tile, so a click anywhere on the tile —
   the name, the project line, the goal, the status, the progress or the
   space between them — lands on that button and opens the Campaign it
   names through the existing data-open-campaign handler. Nothing is
   nested inside the button, and a keyboard still reaches one stop for it.

   The ⋮ button sits ABOVE the stretch, a sibling of the name rather than
   inside it, so a click there opens the menu and never reaches the name's
   handler. Only the button is lifted, never .cc-camp-menu: a z-index there
   would trap its list's own z-index under the NEXT card's ⋮.

   The name reads as the card's title, not as a link of its own: no
   underline and no dimming on hover, which would otherwise follow the
   pointer anywhere on the tile. */
.lb-console .cc-camp-card__tile { position: relative; }
/* -1px and the tile's own radius: the target is exactly the tile as drawn,
   its 1px border and rounded corners included. */
.lb-console .cc-camp-card__title .cc-linkish::after {
  content: '';
  position: absolute;
  inset: -1px;
  z-index: 1;
  border-radius: var(--radius-lg, 14px);
}
.lb-console .cc-camp-card__title .cc-linkish:hover { text-decoration: none; }
.lb-console .cc-camp-card .cc-camp-menu__btn { position: relative; z-index: 2; }
.lb-console .cc-camp-card__title .cc-linkish:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
  border-radius: 4px;
}
.lb-console .cc-camp-card__sub,
.lb-console .cc-camp-card .cc-camp-progress__text { color: rgba(255, 255, 255, 0.72); }
/* The blocked reason is the one line on the card that is a WARNING rather
   than a reading, so it stays distinguishable from the progress figures. */
.lb-console .cc-camp-card__sub--why { color: rgba(255, 255, 255, 0.9); }

.lb-console .cc-camp-card .cc-camp-progress__track {
  background: rgba(255, 255, 255, 0.16);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
.lb-console .cc-camp-card .cc-camp-progress__fill { background: #fff; }

.lb-console .cc-camp-card .cc-camp-menu__btn { color: rgba(255, 255, 255, 0.72); }
.lb-console .cc-camp-card .cc-camp-menu__btn:hover,
.lb-console .cc-camp-card .cc-camp-menu__btn[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}
.lb-console .cc-camp-card .cc-camp-menu__btn:focus-visible { outline-color: #fff; }

/* The state badge keeps its SHAPE and its word; only the ground it sits on
   is restated, because a pale tint drawn for a white card disappears here. */
.lb-console .cc-camp-card .stat-badge {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.22);
  color: #fff;
}

/* ══════════════════════════════════════════════════════════════════════════
   WAITING IN QUEUE, PUT IN ORDER BY HAND (owner, 9 October 2026)

   A card at rest is drawn exactly as it was: nothing here changes how a
   Campaign card looks until one is lifted. coldcalling.js marks the cards
   that can move (data-queue-card) and does the moving; this is only what a
   move looks like.

   ── A CARD THAT CAN MOVE ────────────────────────────────────────────────
   Its words are not selected by a press that becomes a drag, and a finger
   held on it calls up no callout. The transition is what a card slides by
   when the order changes around it: the script puts it back where it was
   with a transform, and lets go.

   ── THE LIFTED CARD ─────────────────────────────────────────────────────
   A picture of the card, carried by a transform the script writes. Its
   tile is raised a little, on the console's own card shadow, and edged
   with the white the card already draws its badge with, because a black
   tile carried over black tiles has no edge of its own. Its caption is not
   drawn: the slot's says where the card would land.

   ── IT NEVER LEAVES THE WHITE PANEL ─────────────────────────────────────
   The picture lives in a layer INSIDE the panel the waiting cards are
   drawn in: laid over the panel edge to edge, above its cards, and clipped
   to its rounded shape. Nothing of the lifted card — its shadow, the
   little it grows when raised, its way down to its slot — can be drawn
   outside the panel, whatever the script asks for, and the layer scrolls
   with the panel by itself. The panel is positioned only while a card is
   in the air, for the layer to be laid out against; nothing about how the
   panel is drawn changes.

   ── THE SLOT ────────────────────────────────────────────────────────────
   The card itself stays in the grid as the place it will drop into, its
   tile emptied to the quiet surface a progress track is drawn on.

   Motion runs on the design system's duration and curve, and a card that
   crosses the grid is given half as long again. Someone who asked for less
   motion gets none: every card is simply where it belongs.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-camp-card[data-queue-card] {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  transition: transform calc(var(--lds-duration, 180ms) * 1.5) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1));
}

.lb-console .cc-camp-card--placeholder .cc-camp-card__tile {
  background: var(--token-surface-secondary, #f3f4f6);
  border-color: var(--line, #e5e7eb);
  box-shadow: none;
}
.lb-console .cc-camp-card--placeholder .cc-camp-card__tile > * { opacity: 0; }

.lb-console.cc-queue-dragging .cc-camp-section { position: relative; }
.lb-console .cc-queue-layer {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}
.lb-console .cc-camp-card--lifted {
  position: absolute;
  top: 0;
  left: 0;
  max-width: none;
  pointer-events: none;
  transition: none;
  will-change: transform;
}
.lb-console .cc-camp-card--lifted .cc-camp-card__queue { visibility: hidden; }
.lb-console .cc-camp-card--lifted .cc-camp-card__tile {
  transition:
    transform var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1)),
    border-color var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1)),
    box-shadow var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1));
}
.lb-console .cc-camp-card--aloft .cc-camp-card__tile {
  transform: scale(1.03);
  border-color: rgba(255, 255, 255, 0.22);
  box-shadow: var(--cc-card-shadow);
}
/* Coming down: the picture travels to its slot and takes the slot's size. */
.lb-console .cc-camp-card--settling {
  transition:
    transform var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1)),
    width var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1)),
    height var(--lds-duration, 180ms) var(--lds-ease, cubic-bezier(0.25, 0.1, 0.25, 1));
}

/* While a card is in the air the whole page shows the hand that holds it,
   and nothing on it can be selected. Nor does the browser hold a card still
   by scrolling the page under it (scroll anchoring): the slot moving up the
   queue would otherwise push the page back down each time, against the
   scrolling the drag is doing. */
.lb-console.cc-queue-dragging,
.lb-console.cc-queue-dragging * {
  cursor: grabbing !important;
  -webkit-user-select: none;
  user-select: none;
  overflow-anchor: none;
}

/* ── The card menu ───────────────────────────────────────────────────── */
.lb-console .cc-camp-menu { position: relative; flex: 0 0 auto; }

.lb-console .cc-camp-menu__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: -10px -12px -10px 0;
  border: 0;
  border-radius: var(--radius-md, 10px);
  background: none;
  color: var(--muted, #6b7280);
  cursor: pointer;
}
.lb-console .cc-camp-menu__btn .cc-i { width: 24px; height: 24px; }
.lb-console .cc-camp-menu__btn:hover,
.lb-console .cc-camp-menu__btn[aria-expanded="true"] {
  background: rgba(0, 0, 0, 0.05);
  color: var(--text, #111827);
}
.lb-console .cc-camp-menu__btn:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
}

.lb-console .cc-camp-menu__list {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  min-width: 190px;
  padding: 6px;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 10px);
  box-shadow: var(--shadow, 0 1px 6px rgba(0, 0, 0, 0.05));
}
.lb-console .cc-camp-menu__list[hidden] { display: none; }

.lb-console .cc-camp-menu__item {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: 8px 12px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text, #111827);
  font: inherit;
  font-size: 0.88rem;
  text-align: left;
  cursor: pointer;
}
.lb-console .cc-camp-menu__item:hover { background: var(--token-surface-secondary, #f3f4f6); }
.lb-console .cc-camp-menu__item:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: -2px;
  background: var(--token-surface-secondary, #f3f4f6);
}
.lb-console .cc-camp-menu__item--danger { color: #b91c1c; }

/* ── Responsive ──────────────────────────────────────────────────────── */
/* The first-run panel takes the rest of the page, down to the line the
   sidebar ends on — the same grow-never-shrink rule the one-panel pages use
   (`flex: 1 0 auto` on #viewPanel's flex column, near the top of this file).
   That rule tests :only-of-type, which this page cannot pass: its six
   counters are a <section> too. So the hero's panel opts in by name. */
@media (min-width: 901px) {
  .lb-console #viewPanel > .cc-camp-section--empty {
    flex: 1 0 auto;
    margin-bottom: 0;
    display: flex;
    flex-direction: column;
  }
  /* The panel takes the card's height and is a column that centres the
     group in it, by the one-panel pages' own rule (margin-block: auto on
     the empty state, near the top of this file): however tall the window,
     the group sits in the middle of its card (owner, 26 Sep). */
  .lb-console .cc-camp-section--empty > .cc-camp-panel {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
  }
  .lb-console .cc-camp-section--empty > .cc-camp-panel > .cc-camp-empty { margin-block: auto; }
  /* The card's padding is 8px above its panel and 20px below it, so the
     panel takes back the 12px difference and the group's middle is the
     card's middle, exactly as a phone does below. The tab row is above the
     card now, so the panel leads every list card, not only the first run's:
     the same 12px gives a list of Campaigns 20px above it as well. */
  .lb-console .cc-camp-section > .cc-camp-panel:first-child { padding-top: 12px; }
  /* A desktop's hero is compact, so Add Campaign stays clear of Simulation
     Mode even in a short window (owner, 25 Sep): a smaller megaphone and
     tighter gaps. The heading and sentence keep their sizes and Add
     Campaign its full 48px. A phone keeps the larger hero, centred. */
  .lb-console .cc-camp-empty .cc-camp-empty__art { width: 120px; margin-bottom: 8px; }
  .lb-console .cc-camp-empty .lb-empty__title { margin-bottom: 4px; }
  .lb-console .cc-camp-empty .lb-empty__action { margin-top: 16px; }
}
@media (max-width: 900px) {
  .lb-console .cc-camp-empty {
    min-height: 360px;
    justify-content: center;
    padding: 32px 16px;
  }
  /* The card's padding is 8px above its panel and 20px below it, and the
     panel is the card's first child. Where the group is centred, the panel
     takes back the 12px difference so the group sits in the card's middle,
     not 6px above it. */
  .lb-console .cc-camp-section > .cc-camp-panel:first-child { padding-top: 12px; }
}

/* A finger gets the 44px these tabs always gave it. A pointer gets the
   reference's proportions, and the track the button's height. */
@media (max-width: 900px), (pointer: coarse) {
  .lb-console .cc-attn-switch.cc-camp-tabs .cc-tab { min-height: 44px; }
}

@media (max-width: 1180px) {
  .lb-console .cc-camp-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  /* 10px between cards across and down; 10 + 6 is still 16px below. */
  .lb-console .cc-camp-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 10px;
    margin-bottom: 6px;
  }
  .lb-console .cc-camp-stat { margin-bottom: 10px; }
  .lb-console .cc-tabs.cc-camp-tabs { flex: 1 1 100%; }
  .lb-console .cc-camp-stat .stat-value { font-size: 1.3rem !important; }
}

@media (max-width: 560px) {
  .lb-console .cc-camp-section { padding: 6px 14px 16px !important; }
  /* 6px above and 16px below here, so the panel takes back 10, and so does
     the Trash Bin's sentence when it leads the card instead. */
  .lb-console .cc-camp-section > .cc-camp-panel:first-child { padding-top: 10px; }
  .lb-console .cc-camp-note { margin-top: 10px; }
  .lb-console .cc-camp-card { flex-basis: 100%; max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-camp-progress__fill { transition: none; }
  .lb-console .cc-camp-card[data-queue-card],
  .lb-console .cc-camp-card--settling,
  .lb-console .cc-camp-card--lifted .cc-camp-card__tile { transition: none; }
  .lb-console .cc-camp-card--aloft .cc-camp-card__tile { transform: none; }
}

@media (forced-colors: active) {
  .lb-console .cc-camp-card__tile,
  .lb-console .cc-camp-menu__list { border: 1px solid; }
  /* The slot is told from a card by its edge, where no ground is drawn. */
  .lb-console .cc-camp-card--placeholder .cc-camp-card__tile { border-style: dashed; }
  .lb-console .cc-camp-progress__fill { background: Highlight; }
  /* A shadow is not drawn here, and the disc is nothing else. */
  .lb-console .cc-camp-tabs .cc-count { border: 1px solid; }
}

/* ══════════════════════════════════════════════════════════════════════════
   STEP 3 OF 5 — CLONE YOUR VOICE

   Inside the same .cc-onb card as steps 1 and 2, at the same width, with the
   same fixed top and fixed bottom. Nothing below restyles the shell: a step
   that changed the card it lives in would stop reading as one flow.

   ── THE PASSAGE IS A TELEPROMPTER, SO IT GETS THE CARD ───────────────────
   It was set in a 620px column, which is a fine measure for prose somebody
   reads silently and the wrong one for a passage somebody reads ALOUD off a
   screen while a microphone is open. At 620px this script runs to fourteen
   lines, which pushed the recording controls off the bottom of a 1080p
   viewport: the operator had to scroll away from the words to find Start
   recording, and scroll back to read them.

   The fix is WIDTH, not smaller type. The same passage at the card's own
   measure is six lines, the type is untouched, the leading is untouched, and
   the controls sit under it where they can be seen while it is being read.
   The cap is there for a 4K monitor, where the card itself is wider than any
   measure is worth reading.

   ── AND THE HEADER DOES NOT FOLLOW IT ────────────────────────────────────
   The step number, the title, the sentence under it and the language chips
   stay centred and narrow. They are one short line each; stretching them
   across a 1500px card would leave three words marooned at either end.

   ── NO SECOND VISUAL LANGUAGE ────────────────────────────────────────────
   Hairline borders, the shared radius, the shared ink. No gradient, no
   frosted surface, no shadow of its own, no colour that is not already in
   the dashboard's palette — this is an onboarding step, not an instrument.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── THE STEP'S OWN RHYTHM ────────────────────────────────────────────────
   18px between every part made the step read as one dense column: the
   language track, the passage, the regenerate button, the mode switch and the
   capsule all sat the same distance apart, so nothing grouped and the eye had
   no resting points. 26px is enough that the three things somebody actually
   moves between — where am I, what do I read, how do I record it — separate,
   while the parts INSIDE each of those keep their own tighter spacing. */
.lb-console .cc-v3 {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  width: 100%;
  min-width: 0;
  margin: 0 auto;
  max-width: 1680px;
}
/* The track sits under the card's own subtitle, not against it. */
.lb-console .cc-v3 > .cc-v3__lang:first-child { margin-top: 6px; }

/* Each part of the step: a small heading and its content, separated by space
   rather than by a box. Nested cards inside a card is the crowding the
   approved design rules out. */
.lb-console .cc-v3__block {
  width: 100%;
  min-width: 0;
}
.lb-console .cc-v3__h {
  margin: 0 0 12px;
  font-size: 0.82rem;
  font-weight: 550;
  letter-spacing: -0.005em;
  color: var(--text);
}

/* ══════════════════════════════════════════════════════════════════════════
   WHICH LANGUAGE — A SELECTOR, NOT A TRACK

   This replaced a segmented control — one tab per language, centred over the
   passage. Tabs read as "pick one", and every language the agent speaks has to
   be recorded, so people recorded one and looked for the way on.

   ── AND THEN IT WENT TOO FAR THE OTHER WAY ────────────────────────────────
   What replaced the tabs was a progress TRACK: numbered circles joined by
   rules, with "Language 2 of 3" underneath. That is the shape of a wizard, and
   it says three things that are not true here — that there is an order, that
   the second circle is further along than the third, and that a circle you
   have not reached is not yet available.

   None of it holds. Any language may be recorded at any time, in any order,
   and the server files each one under its own language either way. So the
   rules between the circles are gone, the count is gone, and what is left is a
   row of round buttons that say how many languages there are, which are saved,
   and which one is showing.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-v3__lang {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  min-width: 0;
}
/* ── THE GAP DOES WHAT THE CONNECTORS USED TO ─────────────────────────────
   A connector is the one thing that turns a row of dots into a sequence, so it
   is the one thing removed. The circles keep their spacing from `gap`, which
   scales with the viewport the same way the old rules did and holds three of
   them comfortably on the narrowest phone this supports. */
.lb-console .cc-v3__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 8px;
  padding: 0;
  list-style: none;
  max-width: 100%;
}
.lb-console .cc-v3__dot {
  display: flex;
  align-items: center;
  min-width: 0;
}
/* ── THE DEFAULT CIRCLE ───────────────────────────────────────────────────
   A number on the page's own ground inside a hairline. Every circle starts
   here and only a SAVE moves it off. The numeral is the page's text colour
   rather than the muted grey it used to be: it is the label of a control
   somebody is meant to click, not a caption. */
.lb-console .cc-v3__dot-btn {
  appearance: none;
  cursor: pointer;
  border: 1.5px solid var(--line, #e5e7eb);
  background: var(--card, #fff);
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  font-size: 0.8rem;
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}
/* ── 44px OF TARGET AROUND A 30px CIRCLE ──────────────────────────────────
   The circle stays 30px: that is the size its numeral is readable at, and the
   size at which it reads as a marker rather than as a button. What was wrong
   was the TARGET. 30px is under every touch minimum there is, and these are
   the step's primary navigation — the control somebody presses to go back and
   redo a language.

   So the hit area is extended 7px on every side by a transparent overlay,
   which leaves the ring, the green fill and the focus outline exactly where
   they were. The `gap` above is 16px so that two 44px targets clear each
   other by 2px rather than overlapping — an expanded target that eats its
   neighbour's is worse than a small one. */
.lb-console .cc-v3__dot-btn::before {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
}
/* ── WHERE YOU ARE: A BORDER, AND NOTHING ELSE ────────────────────────────
   This used to be a solid black fill. Filled is the heaviest thing this
   control can say, and it was saying it about the circle that had achieved
   the LEAST — the one still being recorded — while the finished ones sat in
   outline. The step read as though selecting a language completed it.

   So being current is now a darker, thicker ring on the same white ground.
   It reads as focus, which is what it is. FILL IS RESERVED FOR SAVED. */
.lb-console .cc-v3__dot[data-state="current"] > .cc-v3__dot-btn {
  border-color: var(--accent, #111827);
  border-width: 2px;
  color: var(--text);
}

/* ── SAVED: GREEN, WITH THE NUMBER STILL IN IT ────────────────────────────
   THE NUMERAL STAYS. A tick would remove the only thing that identifies the
   circle, and these are targets somebody clicks: "go back to 2" has to stay
   sayable after 2 is done. There is no checkmark here and no rule joining one
   circle to the next.

   Keyed on data-done, never on data-state, so selecting a saved language
   cannot take its green away. That is the whole point of the two attributes
   being separate.

   ── THE GREEN ITSELF, AND WHERE IT COMES FROM ────────────────────────────
   #15803d — the console's dark forest green, borrowed from the billing and
   import screens, where it carries TEXT — read as a badge at 30px: dense,
   flat, closer to black than to a colour.

   This is the Labbie platform's own success green instead:
   `--lds-color-green` / #34C759 in frontend/design-system/tokens/tokens.css,
   the value the written specification names for "success, completion,
   confirmation, positive status", which is precisely what a saved language
   is. The token is named first and the literal is its fallback, because the
   console mounts the dashboard sheets rather than the token sheet — so the
   value is stated once, sourced, and picks the token up wherever it is
   present. It is not a hue invented for this control.

   The depth is a shadow rather than a second colour: a soft drop under the
   disc and one hairline of a darker tone inside its lower edge. Nothing joins
   one circle to the next, and nothing carries a checkmark.

   THE CONTRAST TRADE IS REAL AND IS STATED HERE. White on #34C759 measures
   about 2.2:1, where white on the old forest green measured 5.0:1. The
   numeral is aria-hidden and every circle states its number, its language and
   its condition in text beside it (WCAG 1.4.1), so no fact depends on reading
   the figure — but it is lighter against its ground than it was, and that is
   a deliberate move onto the platform's specified green, not an oversight.
   The weight below is raised to hold the figure together at this size. */
.lb-console .cc-v3__dot[data-done="true"] > .cc-v3__dot-btn {
  background: var(--lds-color-green, #34c759);
  border-color: var(--lds-color-green, #34c759);
  color: #fff;
  font-weight: 650;
  box-shadow:
    inset 0 -1.5px 0 rgba(36, 138, 61, 0.32),
    0 1px 2px rgba(36, 138, 61, 0.24);
}
/* ── SAVED AND SELECTED: THE SAME DISC, RINGED IN ITS OWN DARKER TONE ─────
   The ring used to be the page's black --accent, which put a second colour
   around a circle that already had one and made the selected saved circle the
   heaviest object in the row. It is deeper green on green now — Apple's own
   darker green, not a third hue — so "saved" and "you are here" stack in one
   colour instead of competing in two. */
.lb-console .cc-v3__dot[data-done="true"][data-state="current"] > .cc-v3__dot-btn {
  border-color: #248a3d;
  border-width: 2px;
  box-shadow:
    inset 0 -1.5px 0 rgba(36, 138, 61, 0.32),
    0 0 0 3px rgba(52, 199, 89, 0.24),
    0 1px 2px rgba(36, 138, 61, 0.24);
}
.lb-console button.cc-v3__dot-btn:hover {
  border-color: var(--text);
}
.lb-console .cc-v3__dot[data-done="true"] > button.cc-v3__dot-btn:hover {
  background: #2fb35a;
  border-color: #2fb35a;
}
.lb-console .cc-v3__dot[data-done="true"][data-state="current"] > button.cc-v3__dot-btn:hover {
  border-color: #248a3d;
}
.lb-console .cc-v3__dot-btn:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
}
.lb-console .cc-v3__dot-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.lb-console .cc-v3__lang-name {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  color: var(--text);
}
/* The gate's copy of the same indicator, on the step's own rhythm. */
.lb-console .cc-v3__gate { margin: 0 0 18px; }

/* ── THE PASSAGE ──────────────────────────────────────────────────────────
   The one surface on the step, and it is a page rather than a card: white,
   a hairline, generous padding, and text set at the size somebody can read
   from a phone propped against a laptop. */
.lb-console .cc-v3__passage {
  margin: 0;
  padding: 22px 28px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 12px);
  background: var(--card, #fff);
  color: var(--text);
  font-size: 1.02rem;
  line-height: 1.9;
  /* A long project name is broken rather than allowed to set the floor of
     the column and push the card wide. */
  overflow-wrap: anywhere;
  transition: opacity 140ms ease;
}

/* ── REGENERATING ─────────────────────────────────────────────────────────
   The passage dims and stays exactly where it is. It is NOT emptied, NOT
   replaced by a spinner and NOT replaced by an error if the request fails:
   what is on screen is a passage the operator can read, and taking it away
   because a request for a SECOND one is in flight costs them the thing they
   came for. The button beside it carries the spinner and the word. */
.lb-console .cc-v3__passage[data-busy="true"] { opacity: 0.45; }

.lb-console .cc-v3__scriptfoot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 14px;
}
.lb-console .cc-v3__scriptnote {
  margin: 0;
  flex: 1 1 200px;
  min-width: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--muted);
}
/* Nothing to say means nothing on the page, rather than an empty paragraph
   holding a line of space open under the button. */
.lb-console .cc-v3__scriptnote:empty { display: none; }

.lb-console .cc-v3__hint {
  margin: 0 0 10px;
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--muted);
}
/* ── MICROPHONE / UPLOAD AUDIO ──────────────────────────────────────────
   The Website Builder's switch, exactly as Step 2's tab strip wears it — see
   "AND IT WEARS THE WEBSITE BUILDER'S SWITCH" there. Scoped to this one
   .cc-seg: billing and the brain picker keep the shared segmented control.
   Every value is restated rather than inherited from .cc-seg, so a change to
   the shared control cannot move this one away from the workspace's. */
.lb-console .cc-v3__modes {
  margin-bottom: 16px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.90);
  box-shadow: 0 2px 8px -4px rgba(28, 24, 16, 0.16);
}
.lb-console .cc-v3__modes .cc-seg__btn {
  padding: 5px 13px;
  border-radius: 999px;
  background: transparent;
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 17px;
  letter-spacing: -0.005em;
  transition: background 140ms, color 140ms, box-shadow 140ms;
}
.lb-console .cc-v3__modes .cc-seg__btn[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.98);
  color: var(--text, #111827);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04);
}
/* Hover darkens the label and nothing else: the shared control's white hover
   fill is not the workspace's. */
.lb-console .cc-v3__modes .cc-seg__btn:not([disabled]):not([aria-pressed="true"]):hover {
  background: transparent;
  color: var(--text, #111827);
}
.lb-console .cc-v3__modes .cc-seg__btn:focus-visible {
  outline: revert;
  outline-offset: revert;
}
.lb-console .cc-v3__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  width: 100%;
  min-width: 0;
}

/* The record flow has one primary action at a time, and it is the width of
   the step. Once a take is ready the quiet text action sits above it, right
   aligned, rather than beside it competing for the same weight. */
/* ── SAVE ─────────────────────────────────────────────────────────────────
   The step's one primary action, the width of the take above it.

   ── WHY IT NEEDS A HEIGHT AT ALL ─────────────────────────────────────────
   .btn-dark sets padding and line-height and no min-height, which is right
   for a button sitting in a row of controls — it comes out around 30px. Under
   a 68px black capsule, at the full width of the card, 30px reads as a strip
   rather than a button, and it was the one control on the step somebody has
   to hit on every language.

   46px is the same decision the 720px rules below already made for this exact
   control (they set 44px, the touch-target floor, which is why only the
   desktop looked wrong). Nothing else is touched: radius, colour, weight and
   type all stay .btn-dark's, so this is the platform button at a comfortable
   size and not a second button geometry. */
.lb-console .cc-v3__record-primary {
  width: 100%;
  min-width: 0;
  flex: 1 1 100%;
  min-height: 46px;
  padding-block: 0;
}
/* ── AND ITS OWN ROW, BECAUSE IT IS ALWAYS THERE ──────────────────────────
   Save used to be rendered only once a take existed, inside whichever input
   flow produced it, so it appeared and vanished and moved between the two
   modes. It sits below both now, in a fixed place, in every state — grey and
   disabled until there is something to save. A control that is always in the
   same place is one somebody stops having to look for. */
.lb-console .cc-v3__save {
  display: flex;
  width: 100%;
  min-width: 0;
  margin-top: 12px;
}
/* ── STEP 3'S CONTINUE, WHILE THAT PRESS IS WORKING ───────────────────────
   The button IS the loading indicator, for the whole of the press — the clone
   being built and the approval that publishes it afterwards. It keeps its
   geometry exactly — same width, height, radius, weight and colour — and
   nothing is inserted beside it, so the header does not shift when the press
   starts or ends. No spinner, and the surface itself never pulses or flashes:
   all of the motion is in the three dots.

   Non-clickable while it runs. `aria-disabled` carries the meaning and this
   rule carries the pointer half of the behaviour; the handler's own guard is
   what stops a keyboard press, which is why a real `disabled` attribute is not
   needed here (see the markup for why it would cost focus). Kept at full
   opacity rather than dimmed: it is working, not unavailable, and a greyed
   control would read as the step having stopped. */
.lb-console .cc-v3__continue--saving {
  pointer-events: none;
  /* ── AND IT DOES NOT SHRINK TO FIT THE SHORTER WORD ─────────────────
     The button is anchored by its RIGHT edge in the top corner, so a label
     narrower than "Continue" would pull its left edge inward the moment a
     press started. The floor is the resting button's own width — the label's
     4.1em, measured in the browser, plus the pill's fixed 55px of padding, gap
     and arrow — so it tracks the font rather than pinning a pixel count, and
     it is declared on THIS state only: the resting button gains nothing. */
  min-width: calc(4.1em + 55px);
}
/* ── AND THE ELLIPSIS, WHICH IS THE WHOLE OF THE MOTION ───────────────────
   Three dots fading in turn, a sixth of a second apart. Reserved inline-block
   width per dot so the label cannot reflow as they fade, and the dots never
   leave the layout — they change opacity only, between a resting quarter and
   full, which is what keeps it a breath rather than a blink.

   The floor is 0.25 rather than 0: a dot that disappears entirely reads as
   text being deleted, and the eye follows the gap. */
.lb-console .cc-ellipsis {
  display: inline-block;
  letter-spacing: 0.06em;
}
.lb-console .cc-ellipsis i {
  font-style: normal;
  opacity: 0.25;
  animation: cc-ellipsis-fade 1.4s ease-in-out infinite;
}
.lb-console .cc-ellipsis i:nth-child(2) { animation-delay: 0.16s; }
.lb-console .cc-ellipsis i:nth-child(3) { animation-delay: 0.32s; }
@keyframes cc-ellipsis-fade {
  0%, 70%, 100% { opacity: 0.25; }
  35% { opacity: 1; }
}
/* Reduced motion keeps the WORD and the dots — they are what says the press is
   working — and takes away only the fading. A static "Saving..." still says
   the true thing. */
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-ellipsis i {
    animation: none;
    opacity: 1;
  }
}

/* ── THE LENGTH RULE, CENTRED ON SAVE ─────────────────────────────────────
   Directly under the button and centred on it, so it reads as that control's
   own note rather than as another left-ranged line of body copy. The step's
   text is ranged left throughout, which is exactly why this one is not: it is
   attached to a control, and the centring is what attaches it. */
.lb-console .cc-v3__save-hint {
  margin: 8px 0 0;
  text-align: center;
}
/* ── AND WHAT DISABLED LOOKS LIKE ─────────────────────────────────────────
   The platform button's own disabled treatment, stated here only because
   .btn-dark is a solid black surface and the shared rule has to be visible
   against it: the same geometry, the same radius, no shadow, and a weight
   that plainly cannot be pressed. No new colour is introduced — it is the
   console's own line and muted tokens. */
.lb-console .cc-v3__record-primary[disabled] {
  background: var(--line, #e5e7eb);
  border-color: var(--line, #e5e7eb);
  color: var(--muted);
  box-shadow: none;
  cursor: default;
}
/* ── THE STEP'S ONE STATUS LINE ───────────────────────────────────────────
   Where three blocks used to be — "Hear your voice", "Your voice", and a
   failure card. Every one of them was saying where the voice had got to, which
   is a sentence rather than a section. Rendered only when there is something
   to say, so an ordinary recording pass has nothing here at all. */
.lb-console .cc-v3__state {
  margin: 14px 0 0;
  text-align: center;
}
/* ── THE IDLE CAPSULE ─────────────────────────────────────────────────────
   The SAME object as the live one and the finished one, in its first state:
   black, full width, a round control on the left, one line across it. It was
   a .btn-dark pill with an inline icon, which meant pressing it replaced one
   kind of object with another and the eye had to find its place again.

   Three columns rather than the capsule's own auto/1fr/auto, because the
   label is centred on the CAPSULE and not on the space left beside the
   button. The trailing column is the button's width, empty, holding the
   symmetry. */
.lb-console .cc-v3__cap--start {
  appearance: none;
  border: 0;
  cursor: pointer;
  grid-template-columns: auto minmax(0, 1fr) 52px;
  color: #fff;
  text-align: center;
  transition: background 150ms ease;
}
.lb-console .cc-v3__cap--start:hover { background: #24242a; }
.lb-console .cc-v3__cap--start:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
}
.lb-console .cc-v3__cap--start[disabled] { cursor: default; opacity: 0.72; }
/* Not a button itself — the whole capsule is the button — so it is a span
   wearing the transport's shape. */
.lb-console .cc-v3__cap-btn--mic { pointer-events: none; }
.lb-console .cc-v3__cap-say {
  min-width: 0;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
/* ── A SAVED RECORDING THAT IS NOT PLAYABLE *YET* ────────────────────────
   NOT the ordinary state of a saved language: that one is the real transport,
   over bytes fetched back from
   GET /voice/samples/:language/audio, and it wears .cc-v3__cap--player like
   every other take. This is the capsule for the two moments when there is
   nothing to press — while those bytes are still arriving, and when they
   could not be fetched at all — so a saved language is never a hole in the
   step where the other states have an object. The round control keeps the
   shape the other states put in that column, and gives up the affordances it
   has no use for. */
.lb-console .cc-v3__cap--saved { color: #fff; }
.lb-console .cc-v3__cap--saved .cc-v3__cap-btn {
  cursor: default;
  pointer-events: none;
}

/* ── A TAKE AND THE WAY TO DISCARD IT, ON ONE ROW ─────────────────────────
   The capsule takes the space, the discard sits to its right at the size of a
   touch target. It used to be a text button on its own line underneath, which
   read as a second decision of equal weight to the pill below it. */
.lb-console .cc-v3__take {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-width: 0;
  flex: 1 1 100%;
}
.lb-console .cc-v3__again {
  appearance: none;
  flex: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 50%;
  background: var(--card, #fff);
  color: var(--text);
  transition: background 150ms ease, border-color 150ms ease;
}
.lb-console .cc-v3__again:hover {
  background: var(--surface-2, #f3f4f6);
  border-color: var(--text);
}
.lb-console .cc-v3__again:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE AUDIO CAPSULE

   One object in six states, and the same object whether the audio came from
   the microphone or from a file. Black, because it is the step's primary
   surface while it exists; pill-shaped, because that is this system's shape
   for a primary control and the capsule replaces one.

   Three parts on one row: the transport, the timeline, the measurement. The
   grid is minmax(0, 1fr) for the middle column ON PURPOSE — `1fr` alone means
   minmax(auto, 1fr), so fifty-six bars would set a floor under the column and
   push the timer off a narrow screen instead of compressing.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-v3__cap {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-width: 0;
  flex: 1 1 100%;
  box-sizing: border-box;
  min-height: 68px;
  padding: 8px 20px 8px 8px;
  border-radius: 999px;
  background: var(--token-btn-action-bg, #101012);
  box-shadow: var(--shadow, 0 1px 6px rgba(0, 0, 0, 0.05));
}

/* THE TRANSPORT. One circular control, 44px, which is the touch target the
   rest of this product uses and large enough to hit while reading. */
.lb-console .cc-v3__cap-btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  transition: background 150ms ease;
}
.lb-console .cc-v3__cap-btn:hover { background: rgba(255, 255, 255, 0.22); }
.lb-console .cc-v3__cap-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ── THE ONE RED THING ON THE STEP ────────────────────────────────────────
   Stopping is the only move available while the microphone is open, and this
   is the system's own red — the same #b91c1c the destructive controls use —
   rather than a second one introduced for a recorder.

   It breathes rather than blinks: a two-second ease, a little opacity and a
   little scale. A flash would read as an alarm, and nothing is wrong. */
.lb-console .cc-v3__cap-btn--stop {
  background: #b91c1c;
  box-shadow: 0 0 0 6px rgba(185, 28, 28, 0.22);
  animation: cc-v3-live-breathe 2s ease-in-out infinite;
}
.lb-console .cc-v3__cap-btn--stop:hover { background: #b91c1c; }
.lb-console .cc-v3__cap-square {
  width: 17px;
  height: 17px;
  border-radius: 4px;
  background: #fff;
}
@keyframes cc-v3-live-breathe {
  0%, 100% { opacity: 0.88; transform: scale(0.94); }
  50% { opacity: 1; transform: scale(1); }
}

/* Recording never borrows the dimmed disabled treatment. */
.lb-console .cc-v3__record-stop,
.lb-console .cc-v3__record-stop:hover { opacity: 1 !important; }

/* ── THE TIMELINE ─────────────────────────────────────────────────────────
   Two identical stacks of bars, one dim and one bright, the bright one
   clipped to the fraction that has played. That is what makes a frame of
   playback ONE custom-property write rather than fifty-six height writes,
   and it is why the progress can be smoother than the bars are wide. */
.lb-console .cc-v3__wave {
  position: relative;
  display: block;
  height: 34px;
  min-width: 0;
  border: 0;
  padding: 0;
  background: none;
}
.lb-console .cc-v3__wave[role="slider"] { cursor: pointer; }
.lb-console .cc-v3__wave[role="slider"]:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
  border-radius: 4px;
}
.lb-console .cc-v3__wave-layer {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2px;
  pointer-events: none;
}
.lb-console .cc-v3__wave-layer--played {
  clip-path: inset(0 calc(100% - var(--cc-played, 0%)) 0 0);
}
/* Slim, and capped so the capsule getting wider adds SPACE between the bars
   rather than fattening each one into a block. `flex: 1 1 0` with min-width 0
   is what lets the same 128 bars survive a 390px screen as hairlines instead
   of overflowing it. */
.lb-console .cc-v3__bar {
  flex: 1 1 0;
  min-width: 0;
  max-width: 4px;
  height: 12%;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.30);
  transition: height 90ms linear;
}
.lb-console .cc-v3__wave-layer--played .cc-v3__bar { background: #fff; }

/* Live capture is red for as long as it is live, and only then. */
.lb-console .cc-v3__wave--live .cc-v3__wave-layer--played .cc-v3__bar {
  background: #b91c1c;
}

/* ── THE MEASUREMENT ──────────────────────────────────────────────────────
   Tabular figures, so a timer counting 0:08 → 0:09 → 0:10 does not shift the
   digits beside it and make the whole capsule appear to twitch. */
.lb-console .cc-v3__cap-time {
  font-size: 0.92rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: #fff;
  white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════════
   CHOOSING A FILE

   The whole area is the control, exactly as the upload dialog's drop zone is:
   a <label> wrapping the input, which keeps it a real target for a pointer
   AND reachable from the keyboard without a handler pretending to be one.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-v3__pick {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-width: 0;
  flex: 1 1 100%;
  box-sizing: border-box;
  min-height: 68px;
  padding: 8px 20px 8px 8px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 999px;
  background: var(--token-surface-secondary, #f9fafb);
  color: var(--text);
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease;
}
.lb-console .cc-v3__pick:hover { border-color: var(--text); }
.lb-console .cc-v3__pick:focus-within {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}
.lb-console .cc-v3__pick-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 50%;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e5e7eb);
  color: var(--text);
}
.lb-console .cc-v3__pick-label {
  font-size: 0.92rem;
  font-weight: 500;
}
/* Visually hidden, still focusable — the same treatment the dialog's own
   picker gets, for the same reason. */
.lb-console .cc-v3__pick input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* Once the take is ready the picker is re-opened by the quiet text action
   above it, so the label itself carries nothing and occupies nothing. */
.lb-console .cc-v3__pick--hidden {
  position: absolute;
  width: 1px; height: 1px;
  min-height: 0;
  padding: 0; margin: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ── CHOSEN, AND BEING READ ───────────────────────────────────────────────
   The file's real name and a level timeline, on the light surface, because
   nothing is playable yet and a black transport with no transport in it
   would be a control that cannot be used. */
.lb-console .cc-v3__picked {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-width: 0;
  flex: 1 1 100%;
  box-sizing: border-box;
  min-height: 68px;
  padding: 8px 20px 8px 8px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 999px;
  background: var(--card, #fff);
}
.lb-console .cc-v3__picked .cc-v3__bar { background: rgba(17, 24, 39, 0.16); }
.lb-console .cc-v3__picked-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  min-width: 0;
}
.lb-console .cc-v3__picked-name {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text);
}
.lb-console .cc-v3__pick--inline {
  position: static;
  width: auto;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  flex: 0 0 auto;
}
.lb-console .cc-v3__pick-replace {
  font-size: 0.8rem;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.lb-console .cc-v3__pick--inline:hover .cc-v3__pick-replace { color: var(--text); }

/* ── WHAT THE ACCOUNT ALREADY HAS ─────────────────────────────────────────
   One hairline above it, because it is a report about state rather than
   another thing to do, and the rule is the quietest way to say so. */
.lb-console .cc-v3__block--voice {
  padding-top: 22px;
  border-top: 1px solid var(--line, #e5e7eb);
}

/* ── AND THE SENTENCE BESIDE THE FOOTER IS GONE ───────────────────────────
   `.cc-v3__whynot` used to stand permanently beside Save & continue,
   explaining why it could not be pressed. Save & continue can always be
   pressed now — pressing it is how somebody asks what is left — and what it
   answers with lands in the step's own notice and in `.cc-v3__state` above the
   recorder. One sentence, in one place, and only when there is one to say. */

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   The dim is information, not decoration, so it survives — only the movement
   between the two states goes. The same rule decides the recorder: the red
   stop control stays red, stays ringed and stays the only red thing on the
   step; it simply stops breathing. And the bars stop EASING between heights
   rather than stopping updating — the waveform is the evidence that the
   microphone is working, so it is the last thing that may be taken away. */
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-v3__passage { transition: none; }
  .lb-console .cc-v3__cap-btn--stop {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .lb-console .cc-v3__bar { transition: none; }
  /* The circles' STATE is the information — filled, ticked, outline — and it
     survives untouched. Only the ease between those states goes. */
  .lb-console .cc-v3__dot-btn,
  .lb-console .cc-v3__again,
  .lb-console .cc-v3__cap--start { transition: none; }
}

/* ── NARROW SCREENS ───────────────────────────────────────────────────────
   720px, the card's own breakpoint, so the step and the shell it sits in
   change shape at the same width instead of one reflowing inside the other.

   The passage keeps its leading and loses its inset: 26px each side of a
   320px screen is a sixth of the width spent on nothing, and the measure is
   what makes it readable. Every control becomes a full 44px target, and the
   footer's reason moves under its button and reads left — a right-aligned
   fragment beside a full-width button is a ragged edge mid-screen. */
@media (max-width: 720px) {
  .lb-console .cc-v3 { gap: 20px; max-width: 100%; }
  .lb-console .cc-v3__passage {
    padding: 18px 16px;
    font-size: 0.98rem;
    line-height: 1.8;
  }
  /* ── THE SELECTOR AT 390px ─────────────────────────────────────────────
     Nothing to reflow. There are at most three circles, each 30px with a 44px
     target and 16px between them — 138px of visible control inside a viewport
     that is 390px wide minus the card's own inset. The rules that used to
     compress here are gone with the track they belonged to.

     The circles keep their size at every width: they are targets somebody
     presses, and shrinking a 30px marker on the device where fingers are
     doing the pressing is the wrong direction. */
  .lb-console .cc-v3 .cc-seg__btn {
    min-height: 44px;
    flex: 1 1 auto;
  }
  .lb-console .cc-v3__actions > .btn-dark,
  .lb-console .cc-v3__actions > .btn-ghost {
    min-height: 44px;
    flex: 1 1 150px;
  }
  .lb-console .cc-v3__actions > .cc-v3__record-primary { flex: 1 1 100%; }
  .lb-console .cc-v3__scriptfoot > .btn-ghost { min-height: 44px; }

  /* ── THE CAPSULE AT 390px ──────────────────────────────────────────────
     It keeps all three parts on one row — a transport that wrapped onto its
     own line would stop reading as one object — and gives up the space it
     does not need: the inner padding, the gaps, and the bars' own width. The
     transport stays 48px, comfortably over the 44px target, and the timer
     keeps `white-space: nowrap` and an `auto` column so it can never be
     clipped; the timeline is the part that compresses, because it is the
     only part with room to give. */
  .lb-console .cc-v3__cap,
  .lb-console .cc-v3__pick,
  .lb-console .cc-v3__picked {
    min-height: 60px;
    padding: 6px 14px 6px 6px;
    gap: 10px;
  }
  /* 50px, not 48: the stop control BREATHES, and at the bottom of that cycle
     it is scaled to 0.94 — which is also its hit area. 48 would spend part of
     every two seconds at 45px, and the floor for a touch target is 44. */
  .lb-console .cc-v3__cap-btn,
  .lb-console .cc-v3__pick-badge { width: 50px; height: 50px; }
  .lb-console .cc-v3__cap-btn--stop { box-shadow: 0 0 0 4px rgba(185, 28, 28, 0.22); }
  .lb-console .cc-v3__wave { height: 28px; }
  /* ── THE GAPS GO BEFORE THE BARS DO ─────────────────────────────────────
     128 bars and a 1px gap need 127px of gap alone, out of a track that is
     about 200px wide here: the bars are squeezed to half a pixel and the
     waveform becomes a flat grey band. Closing the gap gives the same 128
     real measurements 1.6px each and draws them as a continuous silhouette.
     Every bar is still present and still its own peak — what is given up is
     the space between them, not any of the audio. */
  .lb-console .cc-v3__wave-layer { gap: 0; }
  .lb-console .cc-v3__bar { border-radius: 1px; }
  .lb-console .cc-v3__cap-time { font-size: 0.85rem; }
  /* The idle capsule is the same object as the other two here as well, so it
     keeps their height rather than shrinking to a button's. Its trailing
     column matches the transport's phone width. */
  .lb-console .cc-v3__cap--start { grid-template-columns: auto minmax(0, 1fr) 50px; }
  .lb-console .cc-v3__cap-say { font-size: 0.92rem; }
  /* Over the 44px floor, and it keeps its width when the capsule beside it
     compresses — a discard that shrinks with the row is the control somebody
     misses on a phone. */
  .lb-console .cc-v3__take { gap: 10px; }
  .lb-console .cc-v3__again { width: 46px; height: 46px; }

  /* ── THE CHOSEN FILE, BEFORE IT IS PLAYABLE ────────────────────────────
     Two columns here rather than three. The timeline in this state is a
     placeholder carrying no measurement yet, and 130px of placeholder between
     a badge and a filename is the widest thing on the row saying the least.
     The name gets that width instead, which is the part somebody is reading
     to check they picked the right file. */
  .lb-console .cc-v3__picked { grid-template-columns: auto minmax(0, 1fr); }
  .lb-console .cc-v3__picked > .cc-v3__wave { display: none; }
  .lb-console .cc-v3__picked-meta {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
  }
  .lb-console .cc-v3__picked-name { max-width: 100%; }
  /* The step has no footer any more: Back and Continue are the header's, and
     their 44px targets on a phone are the shared .cc-onb__nav rules. */
}

/* ══════════════════════════════════════════════════════════════════════════
   STEP 4 — ADD YOUR PHONE NUMBER

   Step 3's column, at step 3's measure, because they are two states of one
   card and a step that changed width would read as a different screen. No
   nested boxes: each part is a heading and its content, separated by space,
   which is the crowding the approved design rules out.

   ── THE ONE THING THE COLOURS MUST NOT DO ────────────────────────────────
   Make the provider boundary look like a warning to be dismissed, or make a
   recorded number look confirmed. So there is NO green anywhere on this step
   and no amber — the Labbie palette has neither — and the boundary is set on
   the shared .lb-notice surface, which is the product's neutral one. The only
   coloured state on the step is a genuine refusal, and that is .lb-notice
   --error, which already exists.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-v4 {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  width: 100%;
  min-width: 0;
  margin: 0 auto;
  max-width: 620px;
}
.lb-console .cc-v4__block {
  width: 100%;
  min-width: 0;
}
.lb-console .cc-v4__h {
  margin: 0 0 10px;
  font-size: 0.82rem;
  font-weight: 550;
  letter-spacing: -0.005em;
  color: var(--text);
}

/* ── THE COUNTRY CODE, BESIDE THE BOX AND NOT INSIDE IT ───────────────────
   A prefix a person can see and cannot type into. It is not a picker: this
   product calls Malaysian numbers only, and a country selector with one
   entry is a control that cannot be used.

   It is aria-hidden and the LABEL and hint carry the same fact in words, so
   the country is not something only a sighted user is told. */
.lb-console .cc-v4__inputrow {
  display: flex;
  align-items: stretch;
  gap: 8px;
  width: 100%;
  min-width: 0;
}
.lb-console .cc-v4__cc {
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 12px);
  background: var(--surface, #f7f7f8);
  color: var(--muted, #6b7280);
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.lb-console .cc-v4__input {
  flex: 1 1 auto;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-v4__hint { margin: 8px 0 0; }

/* ── A NUMBER THAT IS RECORDED ────────────────────────────────────────────
   Set large, because it is a number somebody has to CHECK digit by digit,
   and tabular so the digits line up under each other rather than drifting.

   Deliberately NOT on a coloured surface and with no tick beside it. It is a
   stored answer, not a confirmation, and every visual grammar this product
   uses for "confirmed" is reserved for things that were. */
.lb-console .cc-v4__number {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 560;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  overflow-wrap: anywhere;
}
.lb-console .cc-v4__meta {
  margin: 6px 0 0;
  font-size: 0.86rem;
  color: var(--muted, #6b7280);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.lb-console .cc-v4__raw { margin: 4px 0 0; }

/* ── THE PROVIDER BOUNDARY ────────────────────────────────────────────────
   The most important thing on the step, and it is styled to be READ rather
   than to be noticed and dismissed. It sits on .lb-notice — the shared
   neutral surface — because it is not an error, not a warning and not a
   success: it is the true state of the product, stated once, in full.

   Left-aligned inside a centred column, because it is prose. */
.lb-console .cc-v4__boundary {
  width: 100%;
  min-width: 0;
  text-align: left;
  padding: 18px 20px;
  /* ── IT BORROWS .lb-notice's SURFACE, NOT ITS LAYOUT ──────────────────
     The shared .lb-notice is `display: flex` because it is built for one
     line beside an icon. This is four blocks of prose, and the inherited
     flex laid each of them out as a COLUMN — the heading wrapping one
     character per line down a 40px strip, the lead and the two roles in
     narrow bands beside it. What was wanted from .lb-notice is its SURFACE:
     the border, the radius, the secondary background and the muted colour,
     which are what mark this as the product speaking rather than the form.
     The row is not, so the display is restated here. */
  display: block;
}
.lb-console .cc-v4__boundary .cc-v4__h { margin-bottom: 8px; }
.lb-console .cc-v4__boundary-lead {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.65;
}
.lb-console .cc-v4__boundary-foot {
  margin: 14px 0 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted, #6b7280);
}

/* ── THE TWO ROLES, DRAWN AS TWO ──────────────────────────────────────────
   §15.2 states them as separate rules and they have separate answers, so
   they are two rows of a definition list rather than one status line. The
   rule between them is what stops the second being read as a continuation of
   the first.

   No colour distinguishes the states. `data-state` is on the element for a
   later release to style if the palette ever gains a token for it, and today
   both rows look identical — which is correct, because neither is available
   and the difference between them is what the WORDS say. */
.lb-console .cc-v4__roles {
  margin: 14px 0 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.lb-console .cc-v4__role {
  padding-top: 12px;
  border-top: 1px solid var(--line, #e5e7eb);
}
.lb-console .cc-v4__role-name {
  margin: 0 0 3px;
  font-size: 0.86rem;
  font-weight: 550;
  color: var(--text);
}
.lb-console .cc-v4__role-state {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted, #6b7280);
}

/* The campaign lock (§15.5). A refusal about the account's state, so it is a
   banner across the step rather than a message beside a field. */
.lb-console .cc-v4__lock {
  width: 100%;
  min-width: 0;
  text-align: left;
}

.lb-console .cc-v4__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}

/* ── NARROW SCREENS ───────────────────────────────────────────────────────
   Step 3's breakpoint and step 3's rules, because they are two states of one
   card: the column loses its inset, and every control becomes a full 44px
   target rather than the 30px the desktop footer uses.

   The country code and the box stay on ONE row. Stacking them would put a
   read-only "+60" on a line of its own above an empty field, which reads as
   two questions where there is one. */
@media (max-width: 720px) {
  .lb-console .cc-v4 { gap: 22px; max-width: 100%; }
  .lb-console .cc-v4__boundary { padding: 16px 14px; }
  .lb-console .cc-v4__number { font-size: 1.25rem; }
  .lb-console .cc-v4__input { min-height: 44px; }
  .lb-console .cc-v4__actions > .btn-dark,
  .lb-console .cc-v4__actions > .btn-ghost {
    min-height: 44px;
    flex: 1 1 150px;
  }
  /* THE STEP'S OWN FOOTER, scoped to the modifier so steps 1 to 3 keep the
     footers they shipped with. Back, Change number, Remove number and Save
     are the whole of what this step can do, and on a phone they were the
     smallest targets on the screen at 30px. */
  .lb-console .cc-onb--step-four .cc-onb__foot .btn-dark,
  .lb-console .cc-onb--step-four .cc-onb__foot .btn-ghost,
  .lb-console .cc-onb--step-four .cc-onb__foot .btn-quiet { min-height: 44px; }
  .lb-console .cc-onb--step-four .cc-onb__foot-main > .btn-dark,
  .lb-console .cc-onb--step-four .cc-onb__foot-main > .btn-quiet { flex: 1 1 150px; }
  /* The declaration is a 44px row too: a checkbox is the easiest control on
     a step to miss with a thumb, and this one gates the whole save. */
  .lb-console .cc-onb--step-four .lb-check { min-height: 44px; align-items: center; }
}

/* ══════════════════════════════════════════════════════════════════════════
   STEP 5 OF 5 — THE AGENT'S PHONE NUMBER

   One centred column under the step's heading, as the approved references
   draw it: the handset, the small tracked label, and directly under the
   label exactly one thing — the Get phone number button, the number and
   Start, or a sentence. The card, header, segments and footer are the shell
   every step uses and nothing below restyles them; the buttons are the
   platform's own .btn-dark, whose height, radius, padding and type are set
   centrally and are NOT restated here.

   The label and the number are the product's own greys and ink; a failure is
   .lb-error's one red, carried by the words. The one colour on the step is the
   handset itself — the owner's approved violet→blue illustration, whose
   gradient lives in the icon sprite and nowhere in this stylesheet.

   ── THE COLUMN IS CENTRED IN THE BODY, NOT PINNED TO ITS TOP ──────────────
   The header carries the step count, the title and the one-line description;
   everything below sits in the card's body, and the body centres it in the
   space the header and footer leave. That is what gives the block the balance
   the approved reference has rather than a stack crowded under the heading.
   Scoped to this step's modifier, so no other step's body moves. */
.lb-console .cc-onb--step-five .cc-onb__body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.lb-console .cc-v5 {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-width: 0;
  max-width: 460px;
  margin: 0 auto;
  text-align: center;
}
/* The approved illustration at picture size — its own box, not an interface
   icon, so .cc-i's 18px stroke rules do not apply. The silhouette fills the
   24px grid edge to edge, so the box IS the handset's size, and the gradient
   fill comes from the sprite (#i-phone-handset), not from `color`. */
.lb-console .cc-v5__icon {
  display: block;
  flex: 0 0 auto;
  width: 76px;
  height: 76px;
}
.lb-console .cc-v5__label {
  margin: 32px 0 0;
  font-size: 0.66rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
}
.lb-console .cc-v5__note { margin: 14px 0 0; max-width: 40ch; text-wrap: balance; }
/* Directly under the label, as the reference has it. 120px is the written
   design system's button minimum (§8). */
.lb-console .cc-v5__action { margin-top: 24px; min-width: 120px; }
.lb-console .cc-v5__note + .cc-v5__action { margin-top: 18px; }

/* ── NARROW SCREENS ───────────────────────────────────────────────────────
   Step 4's breakpoint and Step 4's rule: the step's own action becomes a
   full 44px thumb target. The column is already centred and shorter than
   the narrowest phone, so nothing else changes shape. */
@media (max-width: 720px) {
  .lb-console .cc-v5__action { min-height: 44px; }
}

/* ── STEP 5's ENTRY/EDIT FORM, VERIFY AND REVIEW ──────────────────────────
   The step's body is a centred column; a form and a review read as a left
   column of fields, so this widens the box and turns off the centring for
   those states only — down as well as across: the column starts under the
   header, as every other step's does, instead of floating in the middle of
   a card sized for its tallest state (the auto bottom margin takes the
   space the body would otherwise centre it in). Everything inside is a
   shared primitive (.lb-field, .lb-input, .lb-check, .lb-notice) — nothing
   here restyles one, it only lays them out. */
.lb-console .cc-v5--form { align-items: stretch; text-align: left; max-width: 480px; margin-bottom: auto; }
.lb-console .cc-v5form { display: flex; flex-direction: column; gap: 16px; width: 100%; }
.lb-console .cc-v5form .lb-field { margin: 0; }
.lb-console .cc-v5__edit { align-self: flex-start; margin-top: 4px; }
/* A connection's actions are one row, the console's own .cc-actions: Edit on
   the left, Re-verify and Disconnect pushed right (.cc-v5__push) — or
   Disconnect's Cancel and Disconnect. Set well apart from the facts above
   them. On a phone the shared row stretches its buttons across the width,
   which leaves the push nothing to take. */
.lb-console .cc-v5form > .cc-actions { margin-top: 36px; }
.lb-console .cc-actions > .cc-v5__push { margin-left: auto; }

/* ── THE VERIFIED SUMMARY (.cc-v5--summary) ──────────────────────────────
   The quiet screen after verification succeeds, drawn to the owner's
   reference: the facts, then one row — Edit on the left, Re-verify and
   Disconnect together on the right, the pair reaching past the facts as the
   reference has it. Its buttons are the reference's size, and Re-verify's
   grey and Disconnect's solid red belong to THIS screen only: the shared
   .btn-ghost and .btn-danger are untouched everywhere else. !important only
   where glass.css sizes a button with it. The red is the product's one red
   (#b91c1c, as .cc-v3__cap-btn--stop fills with it) — solid here, with a
   hover that darkens rather than a second red. On a phone the row keeps its
   buttons compact, one line, with the 44px press .cc-onb__next uses. */
.lb-console .cc-v5--summary .cc-v5__facts { gap: 12px; margin-top: 7px; }
.lb-console .cc-v5__row { display: flex; align-items: center; gap: 9px; margin-top: 34px; }
.lb-console .cc-v5__row > .cc-v5__push { margin-left: auto; }
.lb-console .cc-v5__row > button {
  min-width: 98px;
  padding: 7px 24px !important;
  border-width: 0 !important;
  font-size: 0.875rem !important;
  font-weight: 500 !important;
}
.lb-console .cc-v5--summary .btn-ghost { background: #d5d6da; border-color: #d5d6da; color: #000; }
.lb-console .cc-v5--summary .btn-ghost:hover { background: #c8c9ce; border-color: #c8c9ce; }
.lb-console .cc-v5--summary .btn-danger { background: #b91c1c; border-color: #b91c1c; color: #fff; }
.lb-console .cc-v5--summary .btn-danger:hover { background: #b91c1c; border-color: #b91c1c; opacity: 0.85; }
@media (min-width: 721px) {
  .lb-console .cc-v5--summary .cc-v5__row { margin-right: -55px; }
}
@media (max-width: 560px) {
  .lb-console .cc-v5__row > button { min-width: 0; padding: 7px 16px !important; position: relative; }
  .lb-console .cc-v5__row > button::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }
}

/* The connection's stored facts — a plain description list, using the
   console's own type and muted colour rather than any new token. */
.lb-console .cc-v5__facts { display: flex; flex-direction: column; gap: 10px; margin: 0; }
.lb-console .cc-v5__facts > div {
  display: grid; grid-template-columns: minmax(0, 120px) minmax(0, 1fr); gap: 12px;
}
.lb-console .cc-v5__facts dt { margin: 0; color: var(--muted, #6b7280); font-size: 0.82rem; }
.lb-console .cc-v5__facts dd { margin: 0; word-break: break-word; }

@media (max-width: 720px) {
  .lb-console .cc-v5__facts > div { grid-template-columns: minmax(0, 1fr); gap: 3px; }
}

/* ── NARROW SCREENS ───────────────────────────────────────────────────────
   The card's own breakpoint is 720px and this matches it, so the step and the
   shell it sits in change shape at the same width rather than one reflowing
   inside the other.

   The two-column guide becomes one: two 12-word hints inside 280-odd pixels
   is four words a line. The footer's reason moves under its button and reads
   left, because a right-aligned fragment beside a full-width button is a
   ragged edge in the middle of the screen. */
@media (max-width: 720px) {
  .lb-console .cc-biz { gap: 18px; width: 100%; min-width: 0; max-width: 100%; }
  .lb-console .cc-biz--review { gap: 0; }
  .lb-console .cc-biz__cards { max-width: 100%; }
  /* ── ONE COLUMN, EVERYWHERE ────────────────────────────────────────
     A label and its value side by side inside 280-odd pixels leaves four
     words a line for each. Stacked, the value keeps the whole width and the
     label sits above it as a caption — which is how a phone reads a field
     anyway. */
  .lb-console .cc-biz__pair { grid-template-columns: minmax(0, 1fr); gap: 3px; }
  .lb-console .cc-biz__pairform,
  .lb-console .cc-biz__faq { grid-template-columns: minmax(0, 1fr); }
  .lb-console .cc-biz__faq textarea { grid-column: auto; }
  .lb-console .cc-biz__updated { grid-column: 2; grid-row: 2; justify-self: start; }
  .lb-console .cc-biz__sourcerow {
    grid-template-columns: 32px minmax(0, 1fr);
  }
  .lb-console .cc-biz__fileactions { grid-column: 2; flex-wrap: wrap; }
  .lb-console .cc-biz__card,
  .lb-console .cc-biz__facts,
  .lb-console .cc-biz__fact,
  .lb-console .cc-biz__faq,
  .lb-console .cc-biz__edit { min-width: 0; max-width: 100%; }
  .lb-console .cc-biz__missingitem { grid-template-columns: minmax(0, 1fr) auto; }
  /* ── ONE COLUMN HERE TOO ─────────────────────────────────────────
     A value and three controls side by side inside 300 pixels leaves no
     room for either. The controls go back under the fact — where they cost
     a line, which on a phone is the cheaper of the two prices — and they
     are always visible, because there is no hover to reveal them. */
  .lb-console .cc-biz__card .cc-biz__factread {
    grid-template-columns: minmax(0, 1fr);
  }
  .lb-console .cc-biz__card .cc-biz__factread > .cc-biz__factfoot {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
    margin-top: 6px;
  }
  .lb-console .cc-biz__factfoot { align-items: center; flex-wrap: wrap; }
  .lb-console .cc-biz__sourcebtn,
  .lb-console .cc-biz__editbtn,
  .lb-console .cc-biz__drop,
  .lb-console .cc-biz__add { min-height: 44px; }
  /* Row controls are always visible here: there is no hover on a phone, and
     a control that only appears on one is a control this device does not
     have. The desktop rule is inside a (hover: hover) query for exactly
     this reason, so nothing has to be undone. */
  .lb-console .cc-biz__tabs { max-width: 100%; }
  /* 44, like every other control on this step at this width. The strip is
     the step's main navigation and it was the one press here that was not a
     full touch target. */
  .lb-console .cc-biz__tab { min-height: 44px; }
  .lb-console .cc-biz__progfile {
    grid-template-columns: 20px minmax(0, 1fr) auto;
  }
  .lb-console .cc-biz__progfile .cc-biz__filemeta { display: none; }
  .lb-console .cc-biz__counts { gap: 8px; }
  .lb-console .cc-biz__count + .cc-biz__count { padding-left: 8px; }
  /* ── THE FOOTER, THE WAY STEPS 3 AND 4 ALREADY DO IT ──────────────────
     Scoped to the step and written against the button CLASSES rather than
     three ids. The id list was the shape of the moment it was written: it
     covered Approve, View review and Analyse, and silently missed Back,
     Retry, Replace and Continue — so on a phone the approved state's own two
     actions were 31px tall. Found by measuring the real step at 375px.

     A footer button is the last thing somebody touches on a step, and the
     four missed ones include the two that leave it. */
  .lb-console .cc-onb--step-two .cc-onb__foot .btn-dark,
  .lb-console .cc-onb--step-two .cc-onb__foot .btn-ghost,
  .lb-console .cc-onb--step-two .cc-onb__foot .btn-quiet,
  .lb-console .cc-onb--step-two .cc-onb__foot .btn-danger { min-height: 44px; }
  .lb-console .cc-onb__foot-main { width: 100%; }
  .lb-console .cc-onb--step-two .cc-onb__foot-main > .btn-dark,
  .lb-console .cc-onb--step-two .cc-onb__foot-main > .btn-quiet { flex: 1 1 132px; }
  .lb-console .cc-biz__pastefoot { align-items: stretch; }
  .lb-console .cc-biz__pastefoot .btn-dark { width: 100%; }
  .lb-console .cc-biz__whynot { max-width: none; text-align: left; }
  /* Back on its own row above the step's actions, which keeps the primary
     action full-width and under the thumb rather than sharing a line. */
  .lb-console .cc-onb__foot > .cc-biz__whynot { order: 3; }

  /* ── THE READING COLUMN ON A PHONE ───────────────────────────────────
     Shorter, tighter and inset less, because 22px each side of a 320px
     screen is a seventh of the width spent on nothing. The character loses
     8px so the heading, which is the longest thing in that row, still fits
     on one line beside it and the seconds do not wrap under it. */
  .lb-console .cc-read__box { height: 152px; }
  .lb-console .cc-read__layer { padding: 0 16px; }
  .lb-console .cc-read__avatar { width: 40px; height: 40px; }
  .lb-console .cc-read__head { gap: 10px; margin-bottom: 13px; }
  .lb-console .cc-read__title { font-size: 0.94rem; }
  .lb-console .cc-read__line { font-size: 0.85rem; padding-bottom: 13px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE TALKING-SPEED CONTROL

   Three real radios on a track. NOT a range input: every position of a range
   is a value, so it cannot express "nobody has chosen yet", and it announces
   a number where this control's whole vocabulary is Slow, Normal and Fast. A
   radio group arrows through natively, reads out its own labels, and starts
   with nothing selected — which is the honest state for a question nobody has
   answered.

   The inputs are hidden the way .lb-check hides its own: moved out of sight,
   NOT display:none, so they keep their place in the focus order and the
   keyboard still works.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-speed {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding-top: 8px;
}
/* The track, drawn between the first and last stop rather than edge to edge,
   so it starts and ends under a dot instead of in mid-air. */
.lb-console .cc-speed::before {
  content: "";
  position: absolute;
  top: 15px;
  left: 16.666%;
  right: 16.666%;
  height: 2px;
  border-radius: 999px;
  background: var(--line, #e5e7eb);
}
.lb-console .cc-speed__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.lb-console .cc-speed__stop {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding-top: 4px;
  /* The platform's minimum target. The dot is 14px, which is a miss on a
     phone; the whole column is the target instead. */
  min-height: 44px;
  cursor: pointer;
}
.lb-console .cc-speed__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  box-sizing: border-box;
  background: var(--card, #fff);
  border: 2px solid var(--line, #e5e7eb);
  transition: background-color 140ms ease, border-color 140ms ease;
}
.lb-console .cc-speed__text {
  font-size: 0.78rem;
  color: var(--muted);
}
.lb-console .cc-speed__input:checked + .cc-speed__stop .cc-speed__dot {
  background: var(--accent, #111827);
  border-color: var(--accent, #111827);
}
.lb-console .cc-speed__input:checked + .cc-speed__stop .cc-speed__text {
  color: var(--text);
  font-weight: 550;
}
.lb-console .cc-speed__stop:hover .cc-speed__dot { border-color: rgba(17, 24, 39, 0.5); }
/* NEUTRAL, and the same ink as the rest of the console. No system blue. */
.lb-console .cc-speed__input:focus-visible + .cc-speed__stop .cc-speed__dot {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}


/* ══════════════════════════════════════════════════════════════════════════
   PHONE

   The same shell, the same three rows, the same rule about which of them
   scrolls. What changes is two numbers: 16px of margin each side, and 760px
   of height instead of 650.

   THE HEIGHT IS FIXED HERE TOO, for the reason it is fixed on a desktop: one
   height for every step and every state inside one, so nothing under the
   reader's thumb moves because a message appeared.

   calc(100vw - 32px) is the approved width. max-width: 100% sits beside it as
   the guard: 100vw counts a classic scrollbar gutter and the container does
   not, so on a desktop browser narrowed past this breakpoint the card would
   otherwise be a few pixels wider than the room it has — and a card wider
   than its container is a horizontal scrollbar on the page.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {
  .lb-console .cc-onb {
    width: 100%;
    max-width: 100%;
    /* min-height for the same reason as the desktop rule: a phone is the
       width where step 2's contents are tallest, so a ceiling here is the
       clip at its worst. */
    min-height: 760px;
  }
  /* ── BACK AND CONTINUE COME INTO FLOW ON A PHONE ───────────────────
     Absolute at 40px of side padding is comfortable; at 20px, with the step
     count centred a few pixels below them, it is a collision. So on a narrow
     screen the pair takes its own row at the top of the card — Back still
     first on the left, Continue still on the right — and both are 44px
     targets. */
  .lb-console .cc-onb__top { padding: 16px 20px 18px; }
  .lb-console .cc-onb__nav {
    position: static;
    /* The negative left margin puts Back's LABEL on the card's padding edge
       rather than its tap target. */
    margin: 0 0 10px -11px;
    pointer-events: auto;
  }
  .lb-console .cc-onb__back { min-height: 44px; }
  /* The pill keeps its height and still takes a 44px press: an invisible
     extension above and below, the Start test control's own technique. */
  .lb-console .cc-onb__next { position: relative; }
  .lb-console .cc-onb__next::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }
  .lb-console .cc-onb__title { font-size: 1.3rem; }
  .lb-console .cc-onb__body { padding: 4px 20px 24px; }
  .lb-console .cc-onb__foot { padding: 14px 20px 18px; }
  .lb-console .cc-onb__notice { margin: 0 20px 14px; }
  /* The character above the name rather than beside it: two columns inside
     280-odd pixels leaves the input too narrow to read a name in. */
  .lb-console .cc-onb__idrow {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: 14px;
  }
  .lb-console .cc-onb__avatar-field { min-width: 0; }
  .lb-console .cc-onb__idrow > .cc-onb__field { width: 100%; }
  .lb-console .cc-agents-card { width: 100%; max-width: calc(100% - 32px); }
  .lb-console .cc-agents-card__body { padding: 34px 20px 30px; }
}


/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   The only motion this block introduces is the talking-speed dot filling in.
   Somebody who has asked for less of it gets the same control, with the same
   states, and none of the fade. */
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-speed__dot { transition: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE LEADS LIBRARY
   Two screens, one dialog, and a menu.

   ── WHAT THIS OWNS, AND WHAT IT BORROWS ────────────────────────────────
   Almost nothing here is a new primitive. The cards are .section-card, the
   inputs are .lb-input, the table is .lb-table, the empty state is .lb-empty
   and every button is .btn-dark / .btn-ghost / .btn-quiet / .btn-danger —
   the same components the rest of this console and the Labbie dashboard
   already use, so a later restyle of any of them moves this screen too.

   What this file adds is the part the reference design does differently from
   the shared defaults, and nothing else:

     a heavier page title and a navy subtitle, SCOPED to .cc-ll-head so the
     Agent, Campaigns and Settings screens keep the header they have today;
     a table with sentence-case headings and no grey band;
     the list tile, the three-dot menu, the two illustrations, and a dialog
     whose panel is .cc-modal__inner rather than .cc-modal__panel.

   ── SCOPED TO .lb-console, LIKE EVERYTHING ELSE IN THIS FILE ───────────
   This stylesheet is served to a console whose <body> carries .lb-console,
   and the shared sheets it loads are the SAME FILES the user dashboard
   links. An unscoped rule here would reach the dashboard the next time
   somebody linked this file, so every rule below is scoped.

   ── THE COLOURS ARE READ OFF THE REFERENCE, NOT INVENTED ───────────────
   The seven reference images were sampled rather than eyeballed: for
   antialiased text every pixel is the same ink over the same paper at some
   coverage, so summing each pixel's distance from the paper across a run of
   type cancels the coverage and leaves the ink's exact direction. The navy,
   the muted blue-grey of the column headings, the placeholder grey and the
   two illustration palettes below are all that measurement.
   ══════════════════════════════════════════════════════════════════════════ */

.lb-console {
  /* Navy. The subtitle, the empty state's sentence, and the modal's quiet
     line — the reference uses one blue for all three. */
  --ll-navy: #1f3163;
  /* The column headings and the stat card's label: the same hue, lifted. */
  --ll-head: #717d9d;
  /* Placeholder text in the search field. */
  --ll-faint: #a8aec1;
  /* The list tile, and the page title above it. */
  --ll-ink: #0d0d10;
  /* The stat card's icon: periwinkle on the palest wash of itself. */
  --ll-accent: #6877fd;
  --ll-accent-wash: #eaebfe;
  /* The drop zone's border while a file is being dragged over it, and the
     only thing this blue is still for. It was named for the upload
     illustration, which was a document with a blue disc on the corner and
     is a black folder now; the pale page fill that went with it had no
     second reader and is gone rather than left behind. */
  --ll-drag-accent: #1f5cfa;
  /* The spreadsheet tag on a chosen file. */
  --ll-sheet-tag: #04a13c;
  /* Delete. THE PRODUCT'S ONE RED, not a second one measured off the
     reference. The sampled value was #e5342c, which is 4.0:1 on white and
     therefore too faint for the one control in this feature that destroys
     something; darkening it produced a red a shade away from the red this
     console already uses for every refusal, toast and error line. Two reds
     that differ by a shade are not a palette, they are a bug nobody can
     see — so this is that same red, which is darker still and reads at
     7.0:1. Pinned by coldcalling-ui-check.js, which allows exactly one. */
  --ll-danger: #b91c1c;
  /* The upload review's warning badges. Not a new red: the approved
     follow-up red Needs Attention already uses (coldcalling-ui-check.js),
     white text on it reads at 5.6:1. */
  --ll-alert: #e00004;
  /* Hairlines. Lighter than --line, which is what the reference shows: the
     table's rules are barely there and the toolbar's borders are thin. */
  --ll-rule: rgba(17, 24, 39, 0.07);
  --ll-edge: rgba(17, 24, 39, 0.11);
}


/* ── THE PAGE HEAD ────────────────────────────────────────────────────────
   NO TYPOGRAPHY HERE ANY MORE. This header used to set its own title at
   1.97rem/700 and its own description at 0.97rem, to match a reference — and
   that is exactly how "Leads" ended up half again the size of "Agents" and
   heavy where every other page in the product is regular. One page cannot own
   the page-title style; the console does, once, in ONE PAGE HEADER, EVERY
   PAGE near the top of this file.

   AND NO BUTTONS HERE ANY MORE EITHER. This header used to carry Upload and
   Download example, which put the two controls outside the white panel that
   holds everything they act on. They live inside it now — see THE PANEL'S
   OWN ROW below — so all this class is left holding is the gap between the
   header and the panel under it. It is kept rather than dropped because
   leadsHead() still passes it and because that gap is this screen's, not
   every screen's.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-head { margin-bottom: 22px; }


/* ── THE EMPTY STATE ──────────────────────────────────────────────────────
   The one screen a new account sees. It is the AT-REST markup — nothing here
   is drawn while a request is in flight — so it must look finished rather
   than like something still loading.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-empty-card { min-height: 460px; display: flex; }
/* A COLUMN, because the card now has two children: the action row at the top
   and the state itself. The centring did not move to a second place — it is
   the state that takes the leftover height and centres inside it, which is
   what `align-items: center` on the body used to do when it was the only
   child. .lb-empty is already a centred flex column (shared components), so
   the horizontal half of it was never this rule's. */
.lb-console .cc-ll-empty-card > .cc-card__body {
  flex: 1; display: flex; flex-direction: column;
}
.lb-console .cc-ll-empty-card > .cc-card__body > .cc-ll-empty {
  flex: 1; justify-content: center;
}
.lb-console .cc-ll-empty { padding: 24px; }
.lb-console .cc-ll-empty__art { display: block; margin-bottom: 22px; }

/* NO HEADING SIZE, WEIGHT OR INK HERE. "No leads yet" was 1.63rem/700, then
   the shared .lb-empty__title in this screen's own ink and navy sentence,
   while "No campaigns yet" — the same kind of screen — was set larger in the
   console's own ink. One style now (owner, 1 Oct): the heading and the
   sentence are set by the shared big-empty-state rule beside "No campaigns
   yet" (the CAMPAIGNS PAGE first-run hero), and this screen keeps only its
   own measure — wide enough that, at the shared 1rem, the sentence is one
   line on a desktop rather than leaving "leads." alone on a second. */
.lb-console .cc-ll-empty .lb-empty__text { max-width: 36rem; }

/* The folder. Flat fills rather than gradients: at 128px the reference's
   shading reads as two greys and a stack of white sheets, and two fills
   survive a forced-colours mode that would throw a gradient away.

   HALF THE SIZE IT WAS, and that is the whole change: 218x164 became
   109x82. The DRAWING is untouched — same viewBox, same paths, same
   fills, same stack of sheets — because at 218px wide the illustration
   was the loudest thing on a screen whose message is one short heading
   and one sentence, and shrinking a picture is not redrawing it.

   IT STAYS CENTRED WITHOUT A RULE HERE. .lb-empty is a centred flex
   column, so the art is centred as a flex ITEM: it never depended on
   being any particular width, which is why halving the width moves
   nothing sideways. */
.lb-console .cc-ll-folder {
  width: 109px; height: 82px; display: block;
  filter: drop-shadow(0 10px 18px rgba(17, 24, 39, 0.16));
}
.lb-console .cc-ll-folder__back  { fill: #3f3f43; }
.lb-console .cc-ll-folder__front { fill: #8d8d92; }
.lb-console .cc-ll-folder__sheet {
  fill: #ffffff; stroke: rgba(17, 24, 39, 0.13); stroke-width: 1;
}
/* The two behind are turned and dimmed, so the stack reads as a stack. */
.lb-console .cc-ll-folder__sheet--b { fill: #ededf0; transform: rotate(-7deg); transform-origin: 39px 31px; }
.lb-console .cc-ll-folder__sheet--c { fill: #e2e2e6; transform: rotate(9deg);  transform-origin: 45px 25px; }


/* ── THE OVERVIEW: FINDING A LIST ─────────────────────────────────────────
   There is no Total Leads card here any more, and its rules are gone with
   it rather than left behind to be wondered about. It showed the sum of the
   counts already printed on the tiles below — a figure nobody acted on,
   sitting where a person was looking for their list. The per-list count
   stays on each tile, where it answers a question.

   What took its place is a box that filters those tiles BY NAME, entirely
   in the browser: every list this account has is already on the page, so
   asking the server would add a round trip to a filter that can be instant.
   The list detail screen's search is the other kind — it looks inside the
   rows and it does ask.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-find { min-width: 0; flex: 1 1 320px; }
.lb-console .cc-ll-search--lists { max-width: 320px; }
/* The empty answer. Hidden until a search matches nothing, because a grid
   that simply goes blank reads as a fault rather than as a result. */
.lb-console .cc-ll-nofind { margin-top: 14px; }


/* ── THE PANEL'S OWN ROW: SEARCH LEFT, ACTIONS RIGHT ──────────────────────
   Upload and Download example were the page HEADER's, which put them above
   and outside the white panel holding every list they act on. They are the
   panel's now, opposite its search field, so the surface carries both what
   is in it and what puts things into it.

   ONE ROW THAT WRAPS, NOT THREE BREAKPOINTS. `flex: 1 1 320px` on the search
   and `flex: 0 0 auto` on the actions is the whole tablet rule: the two
   share the line while 320px plus the action column still fit, and the
   actions drop to a line of their own the moment they do not. `margin-left:
   auto` is what keeps them hard right on EITHER line — an auto margin
   resolves per flex line, so the wrapped group does not fall back to the
   left edge the way justify-content would leave it.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-panelhead {
  display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap;
  margin-bottom: 16px;
}
/* The empty state has no search to sit opposite, so the actions are the row. */
.lb-console .cc-ll-panelhead--bare { margin-bottom: 4px; }

.lb-console .cc-ll-panelactions {
  margin-left: auto;
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}

/* A ROUNDED RECTANGLE, NOT A PILL. The shared button is border-radius 999px
   and this design's primary is not — the one place it departs from the shared
   shape, spelled out here rather than by changing the shared class. The
   !important is not emphasis: glass.css sets padding, font-size and
   font-weight on .btn-dark with !important, and this has to outrank it. */
.lb-console .cc-ll-upload {
  border-radius: 10px;
  background: var(--ll-ink);
  font-weight: 500 !important;
  font-size: 0.85rem !important;
  padding: 12px 16px !important;
  gap: 7px;
}
.lb-console .cc-ll-upload .cc-i { width: 18px; height: 18px; opacity: 0.92; }

/* THE QUIET ONE IS TEXT, NOT A SECOND BOX. Two boxes of the same size read
   as a choice between two equal things, and one of them downloads a sample
   file. Smaller, unfilled, and directly under the primary. */
.lb-console .cc-ll-example {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; border-radius: 8px;
  padding: 4px 3px;
  font: inherit; font-size: 0.8rem; font-weight: 500; line-height: 1.2;
  color: var(--ll-head);
  cursor: pointer;
  white-space: nowrap;
  transition: color 140ms;
}
.lb-console .cc-ll-example:hover { color: var(--ll-ink); text-decoration: underline; }
.lb-console .cc-ll-example:focus-visible {
  outline: 2px solid var(--text, #111827);
  outline-offset: 2px;
}
.lb-console .cc-ll-example .cc-i { width: 15px; height: 15px; opacity: 0.85; }

.lb-console .cc-ll-listcard { min-height: 380px; }
.lb-console .cc-ll-grid {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start;
}

/* ── THE TILE'S OWN MEASUREMENTS ──────────────────────────────────────────
   The card was 217 x 97px for two short lines of text: 56 of those 97 pixels
   were padding. Its footprint is set by the three numbers below and by
   nothing else, so it can be changed — and asserted — in one place.

   The height is the sum and is not written anywhere: 23 + 20.5 + 2 + 16.9 + 23
   ≈ 85px, which is padding, the name's line box, the gap, the count's line box
   and padding again. Nothing is given a height, so a name that wraps on a
   narrow screen makes the card taller rather than being clipped. */
.lb-console .cc-ll-grid {
  --cc-tile-pad-y: 23px;
  --cc-tile-pad-l: 20px;
  --cc-tile-min-w: 232px;
}

/* THE TILE. Positioned, because the three-dot menu is its child and is
   placed from the ••• control's own offsets — see openLeadMenu(). No
   z-index here: leaving it `auto` keeps the tile out of the stacking
   context business, so the open menu's z-index competes with the OTHER
   tiles rather than being trapped underneath the ones after it. */
.lb-console .cc-ll-tile {
  position: relative;
  display: flex; align-items: center;
  background: var(--ll-ink);
  border-radius: 11px;
  min-width: var(--cc-tile-min-w); max-width: 100%;
}
.lb-console .cc-ll-tile__open {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: none; border: 0; cursor: pointer;
  padding: var(--cc-tile-pad-y) 6px var(--cc-tile-pad-y) var(--cc-tile-pad-l);
  border-radius: 11px 0 0 11px;
  text-align: left;
}
/* 600, which is semibold and is where this stops. The name has to win against
   the count under it, and it does that by being larger, brighter and heavier
   than it — three things, none of which has to be 700. */
.lb-console .cc-ll-tile__name {
  font-size: 0.95rem; font-weight: 600; color: #fff;
  letter-spacing: -0.01em; line-height: 1.35;
  max-width: 22ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The count is the tile's second line, and it is DRAWN. It spent one release
   clipped to screen-reader-only because the reference then showed a title and
   nothing else; the reference now shows the count under the title, so it is a
   visible line again rather than a thing only a screen reader was told.

   Quieter than the name by three things at once — smaller, regular weight
   where the name is 600, and white at 0.7 rather than solid — so the tile
   still reads name first and the count answers "how big" underneath instead
   of competing with it. The 0.7 white is the same muted-on-near-black the
   chip's own count already uses (see .cc-ll-chip[data-on="true"]
   .cc-ll-chip__n): this is what a count on this background looks like here.

   The WORDING is not this file's business. "0 leads", "1 lead", "2 leads"
   come from plural() at the point the tile is built, because singular and
   plural is a fact about the number and not about its styling. */
.lb-console .cc-ll-tile__count {
  font-size: 0.78rem; font-weight: 400; line-height: 1.35;
  color: rgba(255, 255, 255, 0.7);
}
/* ••• SITS ON THE TITLE LINE, NOT IN THE MIDDLE OF THE TILE. The control
   still stretches the full height — that is its click target and its hover
   wash, and shrinking it to the glyph would make a corner control hard to
   hit — but the GLYPH is aligned to the top of that box rather than its
   centre. Before the count was drawn the tile was one line tall and the two
   were the same place; with a second line they are not, and a ••• hovering
   between the name and the count belongs to neither.

   The top padding is the open button's own, and now literally so: both read
   --cc-tile-pad-y, so the glyph's box starts where the name's box starts and
   the two cannot come apart when the card's density is changed. */
.lb-console .cc-ll-tile__menu {
  flex: none; align-self: stretch;
  display: grid; place-items: start center;
  background: none; border: 0; cursor: pointer;
  padding: var(--cc-tile-pad-y) 13px 0 8px;
  border-radius: 0 11px 11px 0;
  color: #fff;
  font-size: 0.9rem; line-height: 1;
  letter-spacing: 0.06em;
}
.lb-console .cc-ll-tile__menu:hover,
.lb-console .cc-ll-tile__menu[aria-expanded="true"] {
  background: radial-gradient(circle, rgba(255, 255, 255, 0.10) 0 70%, transparent 74%)
    center calc(var(--cc-tile-pad-y) - 9px) / 32px 32px no-repeat;
}
.lb-console .cc-ll-tile__open:focus-visible {
  outline-color: rgba(255, 255, 255, 0.85);
}
.lb-console .cc-ll-tile__menu:focus-visible {
  outline: none;
  background: radial-gradient(circle, transparent 57%, rgba(255, 255, 255, 0.9) 62% 68%, transparent 73%)
    center calc(var(--cc-tile-pad-y) - 11px) / 36px 36px no-repeat;
}


/* ── THE THREE-DOT MENU ───────────────────────────────────────────────────
   Absolutely positioned; its top and left are written by openLeadMenu() from
   the ••• control's own offsets, so it opens under the control however wide
   the tile turned out to be.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-menu {
  position: absolute; z-index: 30;
  min-width: 156px;
  display: flex; flex-direction: column;
  padding: 6px 0;
  background: var(--card, #fff);
  border: 1px solid var(--ll-edge);
  border-radius: 11px;
  box-shadow: 0 14px 34px rgba(17, 24, 39, 0.14), 0 2px 6px rgba(17, 24, 39, 0.06);
}
.lb-console .cc-ll-menu__item {
  background: none; border: 0; cursor: pointer;
  text-align: left;
  padding: 13px 18px;
  font-size: 0.92rem; color: var(--text, #111827);
  white-space: nowrap;
}
.lb-console .cc-ll-menu__item:hover { background: rgba(17, 24, 39, 0.05); }
.lb-console .cc-ll-menu__item--danger { color: var(--ll-danger); }
.lb-console .cc-ll-menu__item--danger:hover { background: rgba(217, 45, 36, 0.07); }


/* ── ONE LIST: THE HEADING ROW ────────────────────────────────────────────
   Back navigation and a two-line list identity sit at the left, with both
   ways to put a lead into it hard right. The reference's detail page shows
   the table alone; this row is what makes the screen navigable, and it stays
   above the card.

   BOTH ACTIONS ON THIS ROW, NOT ONE HERE AND ONE IN THE PAGE HEADER. Adding
   a file and adding a lead by hand are two answers to one question, and they
   were two screen-regions apart. The group carries `margin-left: auto` so it
   sits right on its own wrapped line too, exactly as the library panel's
   does. */

.lb-console .cc-ll-listhead {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  margin-bottom: 16px;
}
.lb-console .cc-ll-listidentity {
  display: flex; align-items: center; gap: 14px;
  flex: 1 1 280px; min-width: 0;
}
.lb-console .cc-ll-back { color: var(--ll-navy); }
/* The shared chevron points right; this one is a Back control. */
.lb-console .cc-ll-back__icon { transform: rotate(180deg); }
.lb-console .cc-ll-listdivider {
  align-self: stretch; width: 1px; min-height: 36px;
  background: var(--ll-rule); flex: none;
}
.lb-console .cc-ll-listtitle { min-width: 0; }
.lb-console .cc-ll-listname {
  margin: 0;
  font-family: var(--cc-font-ui, var(--font-ui));
  font-size: 1.1rem; font-weight: 500; letter-spacing: -0.01em; line-height: 1.35;
  color: var(--ll-ink);
  min-width: 0; overflow-wrap: anywhere;
}
.lb-console .cc-ll-listcount { font-size: 0.78rem; line-height: 1.4; color: var(--ll-head); }

.lb-console .cc-ll-listactions {
  margin-left: auto;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  justify-content: flex-end;
}
/* The same rounded rectangle the library panel's primary is, at the smaller
   size this row's other controls are. !important for the same reason: it has
   to outrank glass.css's own !important on .btn-dark. */
.lb-console .cc-ll-addfile {
  border-radius: 10px;
  background: var(--ll-ink);
  font-weight: 500 !important;
  padding: 8px 14px !important;
  gap: 6px;
}
.lb-console .cc-ll-addfile .cc-i { width: 16px; height: 16px; opacity: 0.92; }
/* A LIGHT-GREY PILL, and a pill on purpose: it keeps the shared 999px shape
   the primary beside it gives up, so the two read as different weights of
   control rather than as two primaries. */
.lb-console .cc-ll-add {
  background: rgba(17, 24, 39, 0.05);
  border: 1px solid transparent;
  color: var(--ll-ink);
  font-weight: 500 !important;
}
.lb-console .cc-ll-add:hover { background: rgba(17, 24, 39, 0.09); }


/* ── ONE LIST: SEARCH, FILTERS, SORT ──────────────────────────────────────
   Every control on this row is the same height and the same 10px radius, so
   the row reads as one instrument rather than four. The search field takes
   whatever is left after the other three have what they need.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-toolbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 6px;
}

.lb-console .cc-ll-search,
.lb-console .cc-ll-select,
.lb-console .cc-ll-more {
  display: flex; align-items: center; gap: 8px;
  height: 40px;
  padding: 0 14px;
  background: var(--card, #fff);
  border: 1px solid var(--ll-edge);
  border-radius: 10px;
  font-size: 0.85rem;
  color: var(--text, #111827);
}

.lb-console .cc-ll-search {
  flex: 1 1 260px; min-width: 0;
  cursor: text;
}
.lb-console .cc-ll-search__icon { display: block; flex: none; color: var(--ll-faint); }
.lb-console .cc-ll-search input {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: none;
  font: inherit; font-size: 0.85rem;
  color: var(--text, #111827);
  padding: 0;
}
.lb-console .cc-ll-search input::placeholder { color: var(--ll-faint); }
/* type="search" draws a clear button in WebKit that would sit on top of the
   border radius; the field already empties from the keyboard. */
.lb-console .cc-ll-search input::-webkit-search-cancel-button { -webkit-appearance: none; }

.lb-console .cc-ll-select { position: relative; cursor: pointer; }
.lb-console .cc-ll-select__label { color: var(--ll-head); white-space: nowrap; }
.lb-console .cc-ll-select select {
  appearance: none; -webkit-appearance: none;
  border: 0; outline: none; background: none;
  font: inherit; font-size: 0.85rem; color: var(--text, #111827);
  padding: 0 2px 0 0; margin: 0;
  cursor: pointer;
  max-width: 15ch;
  text-overflow: ellipsis;
}
.lb-console .cc-ll-select > .cc-i { color: var(--ll-head); pointer-events: none; }

.lb-console .cc-ll-more { cursor: pointer; white-space: nowrap; }
.lb-console .cc-ll-more:hover { background: rgba(17, 24, 39, 0.03); }
.lb-console .cc-ll-more[aria-expanded="true"] {
  background: rgba(17, 24, 39, 0.05);
  border-color: rgba(17, 24, 39, 0.22);
}
.lb-console .cc-ll-more .cc-i { color: var(--ll-head); }

/* The counted chips. Hidden behind More Filters, because the reference's
   toolbar is four controls and this is a fifth thing — but the counts are
   computed over the WHOLE list by the route, so a chip can say how many it
   would show before anybody presses it. */
.lb-console .cc-ll-chips {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 12px 0 2px;
}
.lb-console .cc-ll-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px;
  background: var(--card, #fff);
  border: 1px solid var(--ll-edge);
  border-radius: 999px;
  font-size: 0.78rem; color: var(--text, #111827);
  cursor: pointer;
}
.lb-console .cc-ll-chip:hover { background: rgba(17, 24, 39, 0.04); }
.lb-console .cc-ll-chip[data-on="true"] {
  background: var(--ll-ink); border-color: var(--ll-ink); color: #fff;
}
.lb-console .cc-ll-chip__n {
  font-variant-numeric: tabular-nums;
  font-size: 0.72rem; color: var(--ll-head);
}
.lb-console .cc-ll-chip[data-on="true"] .cc-ll-chip__n { color: rgba(255, 255, 255, 0.7); }


/* ── ONE LIST: THE FOUR COLUMNS ───────────────────────────────────────────
   No., Full Name, Phone Number, Remarks — and a fifth, unlabelled column
   holding the row's own controls.

   The shared .lb-table draws uppercase, letter-spaced headings on a grey
   band inside a bordered box. The reference draws sentence-case headings in
   a muted blue on white, with hairline rules and no box at all, so those
   four decisions are undone here and nothing else is.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-detailcard .lb-tablewrap {
  border: 0; border-radius: 0; background: none;
}
.lb-console .cc-ll-table {
  width: 100%;
  table-layout: fixed;
  font-size: 0.79rem;
}
.lb-console .cc-ll-no-col { width: 5%; }
.lb-console .cc-ll-name-col { width: 27%; }
.lb-console .cc-ll-phone-col { width: 22%; }
.lb-console .cc-ll-actions-col { width: 184px; }
.lb-console .cc-ll-table th {
  background: none;
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.68rem;
  font-weight: 500;
  color: var(--ll-head);
  border-bottom: 1px solid var(--ll-rule);
  padding: 12px 14px;
}
.lb-console .cc-ll-table td {
  border-bottom: 1px solid var(--ll-rule);
  vertical-align: middle;
  padding: 13px 14px;
}
.lb-console .cc-ll-table tbody tr:last-child td { border-bottom: 0; }
.lb-console .cc-ll-table tbody tr:hover td { background: rgba(17, 24, 39, 0.018); }

.lb-console .cc-ll-table .cc-ll-no {
  width: 1%; white-space: nowrap;
  padding-right: 4px; padding-left: 18px;
  color: var(--ll-ink); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-ll-name  { font-weight: 500; color: var(--ll-ink); white-space: normal; overflow-wrap: anywhere; }
.lb-console .cc-ll-phone { white-space: nowrap; font-variant-numeric: tabular-nums; }
.lb-console .cc-ll-remarks {
  color: rgba(17, 24, 39, 0.86);
  min-width: 0;
  overflow-wrap: anywhere;
}
/* THE ROW'S HEIGHT IS THE TEXT'S, NOT THE BUTTONS'.
   The shared .btn-icon is a 34px circle, and two of them inside a cell with
   the row's own 13px padding made every row 61px tall against the
   reference's 44 — a table a third taller than the design, for controls the
   reference does not even draw. The cell gives up its vertical padding and
   the buttons come down to 30px, which is still a comfortable target and now
   fits inside the height the text already needed. */
.lb-console .cc-ll-actions {
  width: 184px; min-width: 184px; white-space: nowrap; text-align: right;
}
.lb-console .cc-ll-actions__inner {
  display: flex; gap: 2px; justify-content: flex-end; align-items: center;
}
/* td.cc-ll-actions, not .cc-ll-actions: the padding this has to beat is the
   one ".lb-console .cc-ll-table td" sets a few rules above, and that selector
   outranks a two-class one. */
.lb-console .cc-ll-table td.cc-ll-actions { padding-top: 0; padding-bottom: 0; }
.lb-console .cc-ll-actions .btn-icon { width: 30px; height: 30px; }
.lb-console .cc-ll-input { width: 100%; min-width: 0; box-sizing: border-box; }

/* THE ROW'S CONTROLS ARE QUIET UNTIL THEY ARE WANTED. The reference shows a
   table with no per-row buttons at all; hiding them on opacity rather than
   on display keeps them in the tab order, and :focus-within brings them
   back the moment a keyboard reaches them — which `display: none` would
   have made unreachable. */
.lb-console .cc-ll-table tbody tr .cc-ll-actions > * {
  opacity: 0; transition: opacity 120ms ease;
}
.lb-console .cc-ll-table tbody tr:hover .cc-ll-actions > *,
.lb-console .cc-ll-table tbody tr:focus-within .cc-ll-actions > *,
.lb-console .cc-ll-table tbody tr[data-editing="true"] .cc-ll-actions > * { opacity: 1; }
/* A touch screen has no hover, so there they are simply always there. */
@media (hover: none) {
  .lb-console .cc-ll-table tbody tr .cc-ll-actions > * { opacity: 1; }
}

/* The two marks a row can carry. Neither is an error: one says the number
   cannot be dialled, the other that somebody else in this list has it. */
.lb-console .cc-ll-flag {
  display: inline-block; margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.68rem; font-weight: 500;
  letter-spacing: 0.01em;
  background: rgba(17, 24, 39, 0.06);
  color: var(--ll-head);
  vertical-align: 1px;
}
.lb-console .cc-ll-flag--warn {
  background: rgba(217, 45, 36, 0.08);
  color: var(--ll-danger);
}

/* A cell the server flagged as something a spreadsheet would execute. The
   value is shown exactly as the file wrote it — this is the warning. */
.lb-console .cc-ll-formula {
  border-bottom: 1px dashed rgba(217, 45, 36, 0.5);
  cursor: help;
}

/* Editing in place: the cells become inputs sized to their column. The
   Name / Company cell holds TWO of them — one fact each, stacked, because
   the column shows one line built from two values and an edit has to give
   both of them back separately. */
.lb-console .cc-ll-input { font-size: 0.82rem; padding: 7px 9px; }
.lb-console .cc-ll-input--company { margin-top: 6px; }
.lb-console .cc-ll-table tbody tr[data-editing="true"] .cc-ll-name {
  white-space: normal; min-width: 180px;
}
.lb-console .cc-ll-table tbody tr[data-editing="true"] td { background: rgba(17, 24, 39, 0.02); }

.lb-console .cc-ll-pager {
  display: flex; align-items: center; gap: 12px;
  padding-top: 14px;
}


/* ── THE DIALOG ───────────────────────────────────────────────────────────
   Upload, add by hand, rename, delete a list, delete a row: five uses, one
   shape.

   ── WHY .cc-modal__inner AND NOT .cc-modal__panel ──────────────────────
   .cc-modal above already exists for the import dialog, and its markup is
   .cc-modal > .cc-modal__scrim + .cc-modal__panel. This feature reuses the
   OUTER class deliberately — the fixed, centring layer is exactly what it
   needs — but carries its own scrim element and its own panel, so a change
   to either dialog cannot silently reshape the other.

   ── THE SCRIM IS WHITE, WHICH IS WHAT THE REFERENCE SHOWS ──────────────
   Measured rather than assumed: with the dialog open the page behind it is
   LIGHTER, not darker — the overview card reads #fafafb closed and #fdfdfd
   open, and the sidebar does not move at all. One white veil at about 0.6
   explains both figures exactly. A dark scrim would be a different design.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-modal-backdrop {
  position: fixed; inset: 0; z-index: 2990;
  background: rgba(255, 255, 255, 0.62);
  /* NO BLUR, and the property that would produce one is not named here.
     The reference keeps the page behind the dialog sharp — the sidebar's
     type is as crisp with the dialog open as without it — and blurring it
     made it legibly softer than the measurements support. The veil alone
     is the whole effect.

     Written without the property's name on purpose: this product declares
     no frosted surface of its own, and two suites assert that by reading
     the stylesheet for it. One of them reads the file with its comments
     still in, so a comment saying "we did not use X" would fail the check
     that nothing uses X. */
}

.lb-console .cc-modal-backdrop .cc-modal__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  width: 100%;
  max-width: 540px;
  max-height: min(760px, calc(100vh - 48px));
  background: var(--card, #fff);
  border-radius: 15px;
  border: 1px solid var(--ll-edge);
  box-shadow: 0 26px 70px rgba(17, 24, 39, 0.20), 0 3px 10px rgba(17, 24, 39, 0.06);
  overflow: hidden;
}
.lb-console .cc-modal--sm .cc-modal__inner { max-width: 430px; }

/* The head, body and foot are the ones .cc-modal already defines. What this
   changes is the reference's heavier title and its borderless head. */
.lb-console .cc-modal-backdrop .cc-modal__head {
  border-bottom: 0;
  padding: 22px 22px 8px;
  align-items: flex-start;
}
.lb-console .cc-modal-backdrop .cc-modal__title {
  font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em;
  color: var(--ll-ink);
}
.lb-console .cc-modal__close {
  flex: none;
  display: grid; place-items: center;
  width: 30px; height: 30px;
  margin: -3px -6px 0 0;
  background: none; border: 0; border-radius: 8px;
  color: var(--ll-head); cursor: pointer;
}
.lb-console .cc-modal__close:hover { background: rgba(17, 24, 39, 0.06); color: var(--ll-ink); }

.lb-console .cc-modal-backdrop .cc-modal__body {
  padding: 12px 22px 4px;
  gap: 20px;
}
.lb-console .cc-modal-backdrop .cc-modal__foot {
  border-top: 0;
  padding: 22px 22px 24px;
  gap: 10px;
}
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-dark,
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-ghost,
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-danger {
  border-radius: 9px;
  padding: 9px 18px !important;
  font-weight: 500 !important;
}
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-dark { background: var(--ll-ink); }

/* ── A DISABLED BUTTON UNDER THE POINTER MUST STILL BE THERE ───────────
   The shared sheet dims a disabled .btn-dark to 0.45 and, on hover, sets
   `background: inherit`. Inside this footer that inherits transparent — so
   the moment the pointer rests on it, the disabled control loses its fill
   entirely and reads as an empty gap where a button was. It is reachable
   the instant the pointer stops moving after a click, which is exactly the
   position somebody is in while the file they just confirmed is being read.

   The dimming is right and stays. This restores only the fill. Scoped to
   this dialog: the shared rule is on the Labbie dashboard too, and what it
   does there is not this feature's to change. */
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-dark:disabled:hover { background: var(--ll-ink); }
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-ghost:disabled:hover { background: var(--card, #fff); }
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-danger:disabled:hover { background: var(--card, #fff); }
.lb-console .cc-modal-backdrop .cc-modal__foot .btn-ghost {
  background: var(--card, #fff);
  border: 1px solid var(--ll-edge);
  color: var(--ll-ink);
}

/* The list's name, with the pencil that says it can be changed. It is a
   real input at rest rather than a label that becomes one: the reference
   shows the name already editable, and a control that only reveals itself
   on click is a control most people never find. */
.lb-console .cc-modal__name {
  display: flex; align-items: center; gap: 10px;
  padding: 0 16px;
  height: 50px;
  border: 1px solid var(--ll-edge);
  border-radius: 10px;
  background: var(--card, #fff);
}
.lb-console .cc-modal__name:focus-within { border-color: rgba(17, 24, 39, 0.34); }
.lb-console .cc-modal__nameinput {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: none;
  font: inherit; font-size: 1rem; font-weight: 500;
  color: var(--ll-ink);
}
.lb-console .cc-modal__pencil { display: block; flex: none; color: var(--ll-head); }

/* ── THE DROP ZONE ──────────────────────────────────────────────────────
   One box, three states — choose, chosen, organising — all the same size,
   so nothing under the reader's pointer moves as the upload progresses. */
.lb-console .cc-modal__drop {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; text-align: center;
  min-height: 196px;
  padding: 26px 24px;
  border: 1px dashed rgba(17, 24, 39, 0.17);
  border-radius: 11px;
  background: var(--card, #fff);
  cursor: pointer;
}
.lb-console .cc-modal__drop[data-drag="true"] {
  border-color: var(--ll-drag-accent);
  background: rgba(31, 92, 250, 0.04);
}
.lb-console .cc-modal__drop:focus-within {
  border-color: rgba(17, 24, 39, 0.4);
  outline: 2px solid rgba(17, 24, 39, 0.35);
  outline-offset: 2px;
}
/* The input covers the box so the whole zone is the control, and it stays
   FOCUSABLE — `display: none` would take the only keyboard route to the
   file picker away. */
.lb-console .cc-modal__drop input[type="file"] {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
.lb-console .cc-modal__dropart { display: block; margin-bottom: 6px; }
.lb-console .cc-modal__droptitle {
  font-size: 1.08rem; font-weight: 700; color: var(--ll-ink);
  letter-spacing: -0.01em;
}
.lb-console .cc-modal__droptext {
  font-size: 0.91rem; color: var(--ll-navy); line-height: 1.5; max-width: 40ch;
}
.lb-console .cc-modal__droptext--quiet { color: var(--ll-head); font-size: 0.86rem; }

/* Organising. The same box, the same height, one sentence in the middle. */
.lb-console .cc-modal__drop--busy { cursor: default; }
.lb-console .cc-modal__busy {
  font-size: 1.05rem; font-weight: 600; color: var(--ll-ink);
  letter-spacing: -0.01em;
}

/* Chosen files. Tiles in the same dashed box, and the "+ Add" pill on its
   upper-right corner.

   EVERY NUMBER HERE IS MEASURED FROM THE OWNER'S REFERENCE, which is the
   dialog at 1.5675x: the pill 77x26 and 11px / 14px in from the box's outer
   top and right; the picture 52.3px; the × 22px, standing 14px above and
   14px beyond the picture's upper-right corner; the picture's page, not its
   badge, over the middle of the name. They are the reference's, not a scale.

   AND THE FILE IS WHERE THE REFERENCE PUTS IT, which is 10.5px to the right
   of the box's middle and not on it. The owner's picture is the design; a
   centre worked out by arithmetic is not. */
.lb-console .cc-modal__drop.cc-modal__drop--files {
  flex-direction: row; flex-wrap: wrap; gap: 18px;
  /* Files in a row stand level: their pictures at one height, whether a name
     takes one line or two. The rows, not each tile, are what sit in the
     middle of the box — so one file is exactly where it was. */
  align-items: flex-start; align-content: center;
  /* However many files there are, the box is as tall as they are and the
     dialog scrolls. Left to shrink, it is squeezed shorter than its rows on a
     small screen, and they spill out of it — upwards, under the pill. */
  flex-shrink: 0;
  /* Top and bottom: room above the first row for the pill, which ends 37px
     down. A tile's × starts 6px inside its tile, so no × can reach the pill
     however many files there are — and with a name on two lines the box is
     still the 196px it always was.
     Left: 21px more than the right, which is what puts the tiles' middle
     10.5px right of the box's. */
  padding: 34px 24px 34px 45px;
  cursor: default;
}
/* The picker is behind the pill now, and the pill and each × show their own
   focus. The zone's own ring is for the state where the zone IS the control. */
.lb-console .cc-modal__drop.cc-modal__drop--files:focus-within {
  border-color: rgba(17, 24, 39, 0.17);
  outline: 0;
}
.lb-console .cc-modal__file {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  max-width: 160px;
  /* Where the × stands. Inside the tile, so what sits in the middle of the
     box's height is the × and the picture and the name together, as the
     reference has it. */
  padding-top: 20px;
}
.lb-console .cc-modal__fileicon { display: block; }
.lb-console .cc-modal__filename {
  font-size: 0.97rem; font-weight: 600; color: var(--ll-ink);
  word-break: break-word; line-height: 1.35;
}
.lb-console .cc-modal__filex {
  position: absolute; top: 6px; right: 50%;
  transform: translateX(35px);
  width: 22px; height: 22px; padding: 0;
  border: 0; border-radius: 999px;
  background: #000; color: #fff;
  cursor: pointer;
}
.lb-console .cc-modal__filex svg {
  display: block; width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round;
}
.lb-console .cc-modal__add {
  position: absolute; top: 10px; right: 13px;
  display: inline-flex; align-items: center; gap: 11px;
  height: 26px; padding: 0 14.5px 0 10.5px;
  border: 0; border-radius: 999px;
  background: #000; color: #fff;
  font-size: 1rem; font-weight: 500; line-height: 1;
  cursor: pointer;
}
.lb-console .cc-modal__add svg {
  display: block; width: 10px; height: 10px;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round;
}
.lb-console .cc-modal__add:hover,
.lb-console .cc-modal__filex:hover { background: #2a2a30; }
.lb-console .cc-modal__add:focus-visible,
.lb-console .cc-modal__filex:focus-visible {
  outline: 2px solid rgba(17, 24, 39, 0.35);
  outline-offset: 2px;
}

/* The two illustrations inside the dialog. */

/* ── THE DROP ZONE'S FOLDER ─────────────────────────────────────────────
   It was a document with a blue disc on its corner: four fills, of which
   two were blues this console's palette otherwise has no use for, drawn
   around a picture of the file somebody already has rather than of what
   they are about to do with it.

   IT IS TWO COLOURS NOW, and they are the only two: a solid black folder
   and a solid white arrow going up out of it. Nothing here is a token,
   because nothing here is a THEME — black and white is the drawing, and a
   variable would only invite it to become something else.

   THE FOOTPRINT IS THE ONE IT REPLACES: 62px square, same viewBox, same
   parent, so the dialog's copy and buttons sit exactly where they sat. */
.lb-console .cc-ll-upfolder { width: 62px; height: 62px; display: block; }
.lb-console .cc-ll-upfolder__body { fill: #000; }
/* Drawn as a round-capped stroke rather than as an outlined shape: one
   path, one weight, and the rounded ends the reference has.

   AND IT IS STATED ONCE, so it cannot be restated for a state. There is no
   :hover and no [data-drag] rule for this illustration anywhere: the drop
   zone's own border already answers "you are dragging something over me",
   and a picture that redrew itself underneath the pointer would be saying
   the same thing a second time. */
.lb-console .cc-ll-upfolder__arrow {
  fill: none; stroke: #fff; stroke-width: 4.2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ── A CHOSEN SPREADSHEET'S PICTURE: THE OWNER'S EXCEL ARTWORK ───────────
   The owner's own file (3 Oct), not a drawing of it. It ships byte for byte
   from this product's own mount, the way the Campaigns megaphone and the
   Needs Attention tray do, and is never redrawn, recoloured or restyled
   here. coldcalling-leads-ui-check.js pins its address — this one
   declaration — where it may be drawn, and the file's bytes, so a different
   picture cannot arrive under its name.

   IT IS A BACKGROUND, NOT AN <img>, AND FOR ONE REASON. The dialog is painted
   again whenever a file is added or taken off, and this mount serves nothing
   a browser may keep. An <img> made again on each paint is fetched again on
   each paint, and blinks. A stylesheet's picture is fetched once for the page
   and every tile draws that one copy. The zone's ::after asks for it as the
   dialog opens — it has no size and draws nothing — so the picture is there
   before the first file is chosen.

   THE FILE IS 1331x1182 WITH CLEAR MARGINS ROUND THE ARTWORK, which is
   1052x1053 of it at (143, 76). It is shown at the reference's size, the
   artwork 52.3px across: so the whole file is drawn 66.2x58.8 and moved up
   and left by its own margins, and this box is exactly the artwork. The 10px
   on its right puts the PAGE over the middle of the name, as the reference
   has it, rather than the page and the badge that hangs off its left. */
.lb-console .cc-ll-xl,
.lb-console .cc-modal__drop::after {
  background-image: url("/coldcalling/assets/assets/leads-file-excel.webp");
  background-repeat: no-repeat;
}
.lb-console .cc-ll-xl {
  display: block; width: 52.3px; height: 52.4px; margin-right: 10px;
  background-size: 66.2px 58.8px;
  background-position: -7.1px -3.8px;
}
.lb-console .cc-modal__drop::after {
  content: ""; position: absolute; width: 0; height: 0;
}

/* Every other kind of file: the picture it always had. */
.lb-console .cc-ll-sheet { width: 48px; height: 56px; display: block; }
.lb-console .cc-ll-sheet__page {
  fill: #ffffff; stroke: rgba(17, 24, 39, 0.16); stroke-width: 1.4;
}
.lb-console .cc-ll-sheet__fold { fill: #e8ecf5; }
.lb-console .cc-ll-sheet__tag  { fill: var(--ll-sheet-tag); }
.lb-console .cc-ll-sheet__mark {
  fill: none; stroke: #fff; stroke-width: 3;
  stroke-linecap: round;
}

/* ── THE TWO REVIEWS ────────────────────────────────────────────────────
   Neither is a warning to dismiss. A row with no usable number cannot be
   called, and two rows with one number are one person called twice — so
   both are asked as questions, with the answer editable in place. */
.lb-console .cc-modal__review { display: flex; flex-direction: column; gap: 18px; }
.lb-console .cc-modal__section { display: flex; flex-direction: column; gap: 8px; }
.lb-console .cc-modal__h {
  font-size: 0.85rem; font-weight: 600; color: var(--ll-ink);
  letter-spacing: -0.01em;
}
.lb-console .cc-modal__count {
  font-size: 0.85rem; font-weight: 500; color: var(--ll-ink);
}
.lb-console .cc-modal__text { font-size: 0.86rem; color: var(--text, #111827); line-height: 1.55; }
.lb-console .cc-modal__form { display: flex; flex-direction: column; gap: 7px; }
.lb-console .cc-modal__form .lb-label { margin-top: 5px; }
.lb-console .cc-ll-refusal { color: #b91c1c; }

.lb-console .cc-ll-review {
  display: flex; flex-direction: column;
  border: 1px solid var(--ll-edge);
  border-radius: 11px;
  overflow: hidden;
}
.lb-console .cc-ll-review__row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--ll-rule);
}
.lb-console .cc-ll-review__row:last-child { border-bottom: 0; }
.lb-console .cc-ll-review__name {
  flex: 1 1 40%; min-width: 0;
  font-size: 0.82rem; font-weight: 500; color: var(--ll-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-console .cc-ll-review__num {
  flex: 0 1 auto;
  font-size: 0.8rem; color: var(--ll-head);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.lb-console .cc-ll-review__input { flex: 1 1 40%; min-width: 0; font-size: 0.82rem; padding: 7px 10px; }
.lb-console .cc-ll-review__row [data-on="true"] { color: var(--ll-ink); font-weight: 500; }

/* ── THE REVIEW STEP, AS ITS REFERENCE DRAWS IT ─────────────────────────
   Scoped to .cc-modal__inner--review, which paintUploadModal() sets only
   while the review is showing: choosing and organising keep their look.
   A larger title and list name, the total under it on the right, one red
   badge per question, rows on one shared grid so their columns line up,
   and a black Merge. The head and the actions stay put; only the body
   scrolls between them. */
.lb-console .cc-modal-backdrop .cc-modal__inner--review {
  max-width: 552px;
  border-radius: 20px;
  border-color: rgba(17, 24, 39, 0.06);
  box-shadow: 0 18px 48px rgba(17, 24, 39, 0.12), 0 2px 6px rgba(17, 24, 39, 0.04);
}
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__head { padding: 26px 24px 20px; }
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__title {
  font-size: 1.625rem; font-weight: 300; letter-spacing: -0.01em; line-height: 1.2;
  color: var(--ll-ink);
}
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__body {
  padding: 0 24px 8px;
  gap: 0;
  scrollbar-width: thin;
  scrollbar-color: rgba(17, 24, 39, 0.22) transparent;
  scrollbar-gutter: stable;
}
.lb-console .cc-modal__inner--review .cc-modal__body > * { flex: none; }
.lb-console .cc-modal__inner--review .cc-modal__body::-webkit-scrollbar { width: 6px; }
.lb-console .cc-modal__inner--review .cc-modal__body::-webkit-scrollbar-thumb {
  background: rgba(17, 24, 39, 0.22); border-radius: 3px;
}
.lb-console .cc-modal__inner--review .cc-modal__name {
  height: 46px;
  padding: 0 14px 0 18px;
  border: 1px solid rgba(17, 24, 39, 0.45);
  border-radius: 8px;
}
.lb-console .cc-modal__inner--review .cc-modal__name:focus-within { border-color: var(--ll-ink); }
.lb-console .cc-modal__inner--review .cc-modal__nameinput {
  font-size: 1.5rem; font-weight: 400; letter-spacing: -0.01em;
}
.lb-console .cc-modal__total {
  margin: 12px 0 0;
  text-align: right;
  font-size: 1.25rem; font-weight: 600; letter-spacing: -0.01em;
  color: var(--ll-ink);
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-modal__inner--review .cc-modal__review { gap: 22px; margin-top: 14px; }
.lb-console .cc-modal__inner--review .cc-modal__section { gap: 0; }
.lb-console .cc-modal__inner--review .cc-modal__h { margin: 0; font-size: inherit; }

/* ONE BADGE, for every question the review asks. */
.lb-console .cc-ll-badge {
  display: inline-block;
  padding: 1px 12px 2px;
  border-radius: 999px;
  background: var(--ll-alert);
  color: #fff;
  font-size: 1.0625rem; font-weight: 500; line-height: 1.35; letter-spacing: -0.005em;
  white-space: nowrap;
}
.lb-console .cc-ll-review__lede {
  margin: 4px 0 10px;
  font-size: 0.75rem; line-height: 1.4;
  color: #4d4d4d;
}
.lb-console .cc-modal__inner--review .cc-ll-review {
  margin: 0 6px;
  border-color: rgba(17, 24, 39, 0.08);
  border-radius: 10px;
}
.lb-console .cc-modal__inner--review .cc-ll-review__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 178px) auto;
  align-items: center;
  column-gap: 13px;
  min-height: 50px;
  padding: 0 22px 0 12px;
  border-bottom-color: rgba(17, 24, 39, 0.07);
}
.lb-console .cc-modal__inner--review .cc-ll-review__row:last-child { border-bottom: 0; }
.lb-console .cc-modal__inner--review .cc-ll-review__name {
  font-size: 0.8125rem; font-weight: 600; color: #262628;
}
.lb-console .cc-modal__inner--review .cc-ll-review__input {
  width: 100%; height: 32px;
  padding: 0 10px;
  border-radius: 8px;
  border: 1px solid rgba(17, 24, 39, 0.11);
  font-size: 0.8125rem; color: #545864;
}
.lb-console .cc-modal__inner--review .cc-ll-review__row [data-ll-drop] {
  padding: 6px 0 6px 10px !important;
  border: 0; background: none;
  font-size: 0.78rem !important; font-weight: 400; color: #aaadb6;
}
.lb-console .cc-modal__inner--review .cc-ll-review__row [data-ll-drop]:hover { color: var(--ll-ink); }
.lb-console .cc-modal__inner--review .cc-ll-review__row--dupe {
  grid-template-columns: minmax(0, 1fr) auto auto;
  min-height: 52px;
  padding-right: 10px;
}
.lb-console .cc-modal__inner--review .cc-ll-review__row--dupe .cc-ll-review__name {
  font-size: 0.875rem; font-weight: 500;
}
.lb-console .cc-modal__inner--review .cc-ll-review__num {
  font-size: 0.875rem; color: #8f97ad;
}
/* Merge, as a black pill. Merged keeps the pill: it is pressed again to
   keep the rows apart, so it stays a button. */
.lb-console .cc-ll-review__merge,
.lb-console .cc-ll-review__row .cc-ll-review__merge[data-on="true"] {
  min-height: 28px;
  padding: 0 16px !important;
  border: 0; border-radius: 999px;
  background: var(--ll-ink); color: #fff;
  font-size: 0.875rem !important; font-weight: 500 !important;
}
.lb-console .cc-ll-review__merge:hover { background: #26262b; }
/* The uncertain review keeps its own two-line layout. */
.lb-console .cc-modal__inner--review .cc-ll-review--uncertain .cc-ll-review__row { display: flex; }
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot {
  padding: 16px 18px 18px;
  gap: 12px;
  background: var(--card, #fff);
}
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot .btn-dark,
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot .btn-ghost {
  min-height: 38px;
  padding: 0 20px !important;
  border-radius: 10px;
  font-size: 0.875rem !important;
  font-weight: 500 !important;
}
.lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot .btn-ghost {
  border-color: rgba(17, 24, 39, 0.12);
}
@media (max-width: 720px) {
  .lb-console .cc-modal-backdrop .cc-modal__inner--review { border-radius: 16px; }
  .lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__head { padding: 20px 18px 16px; }
  .lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__title { font-size: 1.375rem; }
  .lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__body { padding: 0 18px 8px; }
  .lb-console .cc-modal__inner--review .cc-modal__nameinput { font-size: 1.25rem; }
  .lb-console .cc-modal__total { font-size: 1.0625rem; }
  .lb-console .cc-modal__inner--review .cc-ll-review { margin: 0; }
  /* The name over its own line; the number and Remove share the next. */
  .lb-console .cc-modal__inner--review .cc-ll-review__row {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 6px;
    padding: 10px 12px;
  }
  .lb-console .cc-modal__inner--review .cc-ll-review__row > .cc-ll-review__name { grid-column: 1 / -1; }
  .lb-console .cc-modal__inner--review .cc-ll-review__row--dupe { grid-template-columns: minmax(0, 1fr) auto; }
  .lb-console .cc-modal__inner--review .cc-ll-review__row [data-ll-drop] { min-height: 44px; }
  .lb-console .cc-ll-review__merge { min-height: 44px; }
  .lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot .btn-dark,
  .lb-console .cc-modal-backdrop .cc-modal__inner--review .cc-modal__foot .btn-ghost { min-height: 44px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   ORGANISING YOUR LEADS

   The panel shown while documents are being read. Deliberately quiet: five
   lines, a tick beside the ones that are finished, and one spinning mark
   beside the one that is running.

   THERE IS NO PROGRESS BAR AND NO PERCENTAGE, and nothing here could carry
   one — a bar would have to be given a width, and there is no honest number
   to give it. See uploadStages() in coldcalling.js.
   ══════════════════════════════════════════════════════════════════════════ */

.lb-console .cc-ll-organising {
  display: flex; flex-direction: column; gap: 4px;
  padding: 30px 26px;
  border: 1px solid var(--ll-edge);
  border-radius: 14px;
  background: #fff;
}
/* The file, which is now what the card leads with — the dialog carries
   the heading. Slightly stronger than a caption for that reason, and still
   quieter than the stage it is above. */
.lb-console .cc-ll-organising__file {
  margin: 0 0 16px;
  font-size: 0.9rem; font-weight: 500; color: var(--ll-navy);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-console .cc-ll-organising__of { color: var(--ll-faint); }
.lb-console .cc-ll-organising__note {
  margin: 16px 0 0;
  font-size: 0.8rem; color: var(--ll-faint); line-height: 1.5;
}

.lb-console .cc-ll-stages {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 11px;
}
.lb-console .cc-ll-stage {
  display: flex; align-items: center; gap: 11px;
  font-size: 0.88rem; line-height: 1.3;
  color: var(--ll-faint);
}
/* Done and running are told apart by WEIGHT AND A MARK as well as by colour,
   so the panel still reads for somebody who cannot see the difference
   between two greys. */
.lb-console .cc-ll-stage[data-state="done"] { color: var(--ll-navy); }
.lb-console .cc-ll-stage[data-state="now"] {
  color: var(--ll-ink); font-weight: 600;
}
.lb-console .cc-ll-stage__mark {
  flex: 0 0 auto;
  width: 16px; height: 16px;
  display: inline-flex; align-items: center; justify-content: center;
}
.lb-console .cc-ll-stage__tick {
  width: 16px; height: 16px;
  fill: none; stroke: var(--ll-sheet-tag); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.lb-console .cc-ll-stage__spin {
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid var(--ll-accent-wash);
  border-top-color: var(--ll-accent);
  animation: cc-ll-spin 0.7s linear infinite;
}
@keyframes cc-ll-spin { to { transform: rotate(360deg); } }

/* ── The third review ────────────────────────────────────────────────────
   Rows this product could not confirm against the document. Taller than the
   other two, because each one carries the line it came from — which is the
   only thing that settles whether it is right. */

.lb-console .cc-ll-review--uncertain .cc-ll-review__row {
  flex-direction: column; align-items: stretch; gap: 7px;
  padding: 11px 12px;
}
.lb-console .cc-ll-uncertain__head {
  display: flex; align-items: center; gap: 10px;
}
.lb-console .cc-ll-uncertain__name {
  flex: 1 1 45%; min-width: 0;
  font-size: 0.82rem; padding: 7px 10px;
}
.lb-console .cc-ll-uncertain__why {
  margin: 0;
  font-size: 0.78rem; line-height: 1.45; color: var(--ll-danger);
}
.lb-console .cc-ll-uncertain__src {
  margin: 0;
  font-size: 0.78rem; line-height: 1.5; color: var(--ll-head);
}
.lb-console .cc-ll-uncertain__at {
  color: var(--ll-faint); font-variant-numeric: tabular-nums;
}
.lb-console .cc-ll-uncertain__src q {
  quotes: '\201C' '\201D';
  font-style: normal;
}

/* ── A file that was read without AI ─────────────────────────────────────*/
.lb-console .cc-ll-review__row--failed { align-items: flex-start; }
.lb-console .cc-ll-failure__why {
  flex: 1 1 55%; min-width: 0;
  display: flex; flex-direction: column; gap: 3px;
  font-size: 0.8rem; line-height: 1.45; color: var(--ll-navy);
}
.lb-console .cc-ll-failure__detail {
  font-size: 0.76rem; color: var(--ll-head);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-word;
}
.lb-console .cc-ll-discarded { color: var(--ll-navy); }

/* Said only to a screen reader. The stage list carries its state visually
   as a tick and a weight, and neither of those is an announcement. */
.lb-console .cc-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}


/* ══════════════════════════════════════════════════════════════════════════
   CAMPAIGN DETAIL — the lead table (Master Plan v4 §20.1, §20.4–20.6, G.10)

   Shared tokens and shared primitives only: .lb-input, .lb-table, .lb-label
   and the badge do the work, and nothing below redefines a colour, a radius
   or a shadow of its own.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── §20.4/20.5/20.6 — the view controls ─────────────────────────────────
   Search takes the room that is left; the two pickers keep their own width.
   Wraps rather than scrolling, for the reason the Campaign tabs do. */
.lb-console .cc-lead-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px 12px;
  margin: 0 0 14px;
}
.lb-console .cc-lead-bar__search { flex: 1 1 260px; min-width: 0; }
.lb-console .cc-lead-bar__pick { flex: 0 0 auto; min-width: 0; }
.lb-console .cc-lead-bar__pick .lb-label { display: block; margin-bottom: 2px; }
.lb-console .cc-lead-bar__pick .lb-input { min-height: 44px; }
.lb-console .cc-lead-bar__search .lb-input { min-height: 44px; width: 100%; }
.lb-console .cc-lead-bar__count {
  flex: 0 0 auto;
  margin: 0 0 10px;
  font-size: 0.82rem;
  color: var(--muted, #6b7280);
}

/* A label that only a screen reader reads. The search box carries a visible
   placeholder AND this, because a placeholder is not a label: it disappears
   the moment somebody types. */
.lb-console .lb-sronly {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── §20.1's large-data rule, and G.10's ─────────────────────────────────
   "support at least 100/300+ rows without page-height failure … use a
   bounded vertical viewport with sticky header … do not create a page
   thousands of pixels tall."

   The bound is on the SCROLLER, not the page, so the page itself never grows
   past the viewport no matter how many leads a Campaign holds.

   The sticky header in that quote is the half NOT taken — §20.1 qualifies it
   with "where useful", and this shell does not allow it. See the note on the
   thead rule below. */
.lb-console .cc-lead-scroll {
  max-height: min(60vh, 560px);
  overflow-y: auto;
  overflow-x: auto;
  overscroll-behavior: contain;
}
.lb-console .cc-lead-table { width: 100%; }
/* NOT position: sticky, deliberately. §20.1 asks for the sticky header
   "where useful" rather than always, and this console is built on a shell
   whose scroll contract coldcalling-ui-check.js enforces as a blanket ban
   on position: sticky in this stylesheet — origin/main carries none. The
   bounded scroller below is what §20.1 actually requires and it is kept;
   the header simply scrolls with its rows, as every other table in this
   console does. (§17.10 lists a sticky header unqualified, but §17.10
   governs the Leads Library large-list UI, not Campaign Detail.) */
.lb-console .cc-lead-table thead th { background: var(--card, #fff); }
.lb-console .cc-lead__no { width: 3.5rem; white-space: nowrap; }
.lb-console .cc-lead__phone { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Remarks are free text and can be long. Bounded so one verbose row cannot
   set the width of the whole table, and wrapped rather than clipped so the
   text stays readable. */
.lb-console .cc-lead__remarks {
  max-width: 22rem;
  white-space: normal;
  overflow-wrap: anywhere;
}
.lb-console .cc-lead__act { white-space: nowrap; }
/* A status is one or two words and the pill is read at a glance. The table
   inherits the console's overflow-wrap: anywhere so a long remark can never
   push the page sideways, but that permission let "Not Contacted" break
   INSIDE a word into a three-line pill once a verbose Remarks column
   narrowed the Status column at 1440 (final acceptance, 2026-09-16). The
   pill keeps its words whole; the Remarks column above stays the one that
   wraps. */
.lb-console .cc-lead-table .stat-badge { white-space: nowrap; }

/* ── Mobile: the row reflows vertically and stays ONE list ───────────────
   §20.1: "mobile reflows each row vertically without page-level horizontal
   scrolling while retaining a table/list relationship rather than unrelated
   standalone cards." So the header is hidden and each cell carries its own
   label, which keeps the row a row. */
@media (max-width: 720px) {
  .lb-console .cc-lead-scroll { overflow-x: hidden; max-height: none; }
  .lb-console .cc-lead-table,
  .lb-console .cc-lead-table tbody,
  .lb-console .cc-lead-table tr,
  .lb-console .cc-lead-table td { display: block; width: auto; }
  .lb-console .cc-lead-table thead { display: none; }
  .lb-console .cc-lead-table tr {
    border-bottom: 1px solid var(--line, #e5e7eb);
    padding: 10px 0;
  }
  .lb-console .cc-lead-table td { border: 0; padding: 2px 0; }
  /* A little room between the stacked fields and the table's left border,
     the same 12px the Search field above insets its text by, so the two
     line up (owner, 4 October 2026). The Campaign page's table only: the
     edit card's (.cc-camp-edit__table) was not part of that request. */
  .lb-console .cc-lead-table:not(.cc-camp-edit__table) tr { padding-left: 12px; }
  /* The heading each cell lost, given back — the same idiom the Leads
     Library table already uses at this width, so the two read alike.
     Without it a stacked row is a column of bare values, and two empty
     cells — no phone, and never contacted — are both a lone em dash with
     nothing to say which is which. */
  .lb-console .cc-lead-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.68rem;
    font-weight: 500;
    color: var(--muted, #6b7280);
    margin-bottom: 1px;
  }
  /* The row number is the block's own index, not a labelled field. */
  .lb-console .cc-lead-table td.cc-lead__no::before { content: none; }
  .lb-console .cc-lead-table td.cc-lead__no {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--muted, #6b7280);
  }
  .lb-console .cc-lead__remarks { max-width: none; }
  .lb-console .cc-lead-bar__pick { flex: 1 1 46%; }
  .lb-console .cc-lead-bar__pick .lb-input { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-lead-scroll { scroll-behavior: auto; }
}

@media (forced-colors: active) {
  .lb-console .cc-lead-table thead th { border-bottom: 1px solid; }
  .lb-console .cc-lead-table tr { border-bottom: 1px solid; }
}

/* ── §20.7 — selecting leads, and removing them ──────────────────────────
   Shared primitives only: .btn-danger, .btn-ghost and the drawer do the
   work. Nothing below defines a colour, a radius or a shadow of its own. */
.lb-console .cc-lead__pick-cell { width: 2.75rem; }
.lb-console .cc-lead__pick {
  width: 18px;
  height: 18px;
  /* 44px of hittable area around an 18px box, without moving the layout. */
  margin: 13px;
  cursor: pointer;
}
.lb-console .cc-lead-table tr.is-picked { background: var(--ll-accent-wash, #f5f5f4); }

/* The contextual action row. Appears only when something is selected, so it
   sits between the controls and the table rather than reserving space. */
.lb-console .cc-lead-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 12px;
  padding: 8px 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 10px;
  background: var(--cc-read-surface, #fafafa);
}
.lb-console .cc-lead-actions__count {
  flex: 1 1 auto;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
}
.lb-console .cc-lead-actions .btn-sm { min-height: 36px; }

@media (max-width: 720px) {
  /* The checkbox keeps its own labelled line when the row reflows, so it is
     never mistaken for part of the value above it. */
  .lb-console .cc-lead__pick-cell { width: auto; }
  .lb-console .cc-lead__pick { margin: 4px 8px 4px 0; vertical-align: middle; }
  .lb-console .cc-lead-actions .btn-sm { flex: 1 1 auto; }
}

@media (forced-colors: active) {
  .lb-console .cc-lead-table tr.is-picked { outline: 2px solid; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE LEADS LIBRARY — TABLET
   The four toolbar controls stop competing for one line, and the table's
   remarks column is allowed to be the one that gives up width.
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  /* The title is not sized here. Every page title in the console narrows
     together, by --cc-title-size stepping down at this breakpoint — see the
     token block at the top of this file. */
  .lb-console .cc-ll-search { flex: 1 1 100%; order: -1; }
  .lb-console .cc-ll-remarks { min-width: 0; }
  .lb-console .cc-ll-empty-card { min-height: 380px; }
  .lb-console .cc-ll-listcard { min-height: 260px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE LEADS LIBRARY — MOBILE

   ── THE TABLE STOPS BEING A TABLE ──────────────────────────────────────
   Four columns and a set of controls do not fit in 320 points, and a table
   that scrolls sideways hides the column somebody is looking for. Each row
   becomes a card, and every cell carries the heading it lost: the markup
   already writes data-label on the three that need one — see
   leadRowMarkup() — so this is a presentation change and not a second
   rendering of the same rows.

   The header row is hidden rather than removed, so the table is still a
   table to a screen reader reading it in the linear order.
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 720px) {
  .lb-console .cc-ll-head { margin-bottom: 18px; }
  .lb-console .lb-page-actions { width: 100%; }

  /* THE ACTIONS STACK UNDER THE SEARCH, RATHER THAN BESIDE IT. There is no
     width at which a 320px field and a button share 320 points, so the row
     stops trying: the search takes the line, the primary takes the next one
     full width, and the quiet action sits under it at a size a thumb can
     find. Nothing is compressed and nothing overlaps, which is the whole
     reason this is a column here and a row above it. */
  .lb-console .cc-ll-panelhead { flex-direction: column; align-items: stretch; gap: 12px; }
  /* THE BASIS HAS TO BE RELEASED WITH THE DIRECTION. `flex: 1 1 320px` is a
     WIDTH while the row is a row and a HEIGHT the moment it is a column — so
     left alone it made the search box 320 points tall and then let it grow,
     opening half a screen of white between the field and the button under
     it. The one line that could not be left to the direction change. */
  .lb-console .cc-ll-find { flex: 0 0 auto; }
  .lb-console .cc-ll-panelactions {
    margin-left: 0; width: 100%; align-items: stretch; gap: 8px;
  }
  .lb-console .cc-ll-upload { width: 100%; justify-content: center; min-height: 44px; }
  .lb-console .cc-ll-example {
    justify-content: center; min-height: 44px; font-size: 0.85rem;
  }

  .lb-console .cc-ll-search--lists { max-width: none; }
  .lb-console .cc-ll-grid { flex-direction: column; }
  .lb-console .cc-ll-tile { width: 100%; }
  .lb-console .cc-ll-tile__name { max-width: none; }
  .lb-console .cc-ll-tile__open,
  .lb-console .cc-ll-tile__menu { min-height: 52px; }

  /* The menu is placed from the control's offsets, which on a narrow screen
     would put a 148px panel off the right edge. This is the one place the
     inline style has to lose, so it says !important and says why. */
  .lb-console .cc-ll-menu {
    left: auto !important; right: 8px;
    min-width: 168px;
  }

  .lb-console .cc-ll-listhead { gap: 12px; }
  .lb-console .cc-ll-listidentity { flex: 1 1 100%; gap: 10px; }
  .lb-console .cc-ll-listdivider { min-height: 44px; }
  .lb-console .cc-ll-listname { font-size: 1rem; }
  /* The identity gets the first line; the actions use the next line and
     stay grouped against its right edge. */
  .lb-console .cc-ll-listactions {
    margin-left: 0; width: 100%;
    justify-content: flex-end; gap: 8px;
  }
  .lb-console .cc-ll-addfile,
  .lb-console .cc-ll-add { justify-content: center; min-height: 44px; }

  .lb-console .cc-ll-search,
  .lb-console .cc-ll-select,
  .lb-console .cc-ll-more { flex: 1 1 100%; height: 44px; }
  .lb-console .cc-ll-select select { flex: 1; max-width: none; }
  .lb-console .cc-ll-more { justify-content: center; }

  .lb-console .cc-ll-detailcard .lb-tablewrap { overflow-x: visible; }
  .lb-console .cc-ll-table,
  .lb-console .cc-ll-table tbody,
  .lb-console .cc-ll-table tr,
  .lb-console .cc-ll-table td { display: block; width: auto; }
  .lb-console .cc-ll-table thead {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .lb-console .cc-ll-table tbody tr {
    position: relative;
    border: 1px solid var(--ll-edge);
    border-radius: 12px;
    padding: 12px 14px 10px;
    margin-bottom: 10px;
  }
  .lb-console .cc-ll-table tbody tr:hover td { background: none; }
  .lb-console .cc-ll-table td,
  .lb-console .cc-ll-table tbody tr:last-child td {
    border-bottom: 0;
    padding: 3px 0;
    white-space: normal;
  }
  /* The heading each cell lost, given back. */
  .lb-console .cc-ll-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.68rem; font-weight: 500; color: var(--ll-head);
    margin-bottom: 1px;
  }
  /* The row number is a corner mark rather than a line of its own. */
  .lb-console .cc-ll-table .cc-ll-no {
    position: absolute; top: 10px; right: 14px;
    width: auto; padding: 0;
    font-size: 0.72rem; color: var(--ll-head); font-weight: 500;
  }
  .lb-console .cc-ll-name { font-size: 0.9rem; padding-right: 28px; }
  .lb-console .cc-ll-remarks { min-width: 0; }
  .lb-console .cc-ll-actions {
    width: auto; min-width: 0; text-align: right;
    padding-top: 8px;
  }
  .lb-console .cc-ll-actions__inner { justify-content: flex-end; }
  .lb-console .cc-ll-table tbody tr[data-editing="true"] .cc-ll-input { width: 100%; }
  .lb-console .cc-ll-table tbody tr .cc-ll-actions > * { opacity: 1; }

  .lb-console .cc-modal-backdrop .cc-modal__inner {
    max-width: 100%;
    max-height: calc(100vh - 24px);
  }
  .lb-console .cc-modal-backdrop .cc-modal__head { padding: 18px 18px 4px; }
  .lb-console .cc-modal-backdrop .cc-modal__body { padding: 12px 18px 4px; }
  .lb-console .cc-modal-backdrop .cc-modal__foot { padding: 14px 18px 18px; }
  .lb-console .cc-modal-backdrop .cc-modal__foot .btn-dark,
  .lb-console .cc-modal-backdrop .cc-modal__foot .btn-ghost,
  .lb-console .cc-modal-backdrop .cc-modal__foot .btn-danger {
    flex: 1; justify-content: center; min-height: 44px;
  }
  .lb-console .cc-modal__drop { min-height: 168px; padding: 22px 16px; }
  /* The reference is the desktop dialog. At this width the tiles sit in the
     middle of the box, which leaves them the most room to wrap in. */
  .lb-console .cc-modal__drop.cc-modal__drop--files { padding-left: 16px; padding-right: 16px; }
  .lb-console .cc-ll-review__row { flex-wrap: wrap; }
  .lb-console .cc-ll-review__name { flex: 1 1 100%; }
  /* The name, the number and Remove will not fit on one line at 375px,
     so they stack — and Remove keeps its full tap target rather than
     being squeezed to a sliver beside two inputs. */
  .lb-console .cc-ll-uncertain__head { flex-wrap: wrap; }
  .lb-console .cc-ll-uncertain__name { flex: 1 1 100%; }
  .lb-console .cc-ll-review--uncertain .cc-ll-review__input { flex: 1 1 100%; }
  .lb-console .cc-ll-organising { padding: 22px 16px; }
  .lb-console .cc-ll-failure__why { flex: 1 1 100%; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE LEADS LIBRARY — WHAT SOMEBODY ASKED FOR

   Reduced motion, and a forced-colours mode where none of the fills above
   survive. Both are the same principle: the screen still says everything it
   said before, using whatever the reader's own settings allow.
   ══════════════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-ll-table tbody tr .cc-ll-actions > * { transition: none; }
  /* The stage marker stops spinning and becomes a filled dot. It still
     says which stage is running — the state was never carried by the
     motion, only accompanied by it. */
  .lb-console .cc-ll-stage__spin {
    animation: none;
    border-color: var(--ll-accent);
    background: var(--ll-accent);
  }
}

@media (forced-colors: active) {
  /* Colour is not available, so the marks that were carried by colour are
     carried by a border instead — and the tile, which was a black
     rectangle, becomes an outlined one. */
  .lb-console .cc-ll-tile { border: 1px solid; }
  .lb-console .cc-ll-flag { border: 1px solid; }
  .lb-console .cc-ll-chip[data-on="true"] { border: 2px solid; }
  .lb-console .cc-ll-menu { border: 1px solid; }
  .lb-console .cc-modal-backdrop .cc-modal__inner { border: 1px solid; }
  .lb-console .cc-modal__drop { border: 1px dashed; }
  .lb-console .cc-ll-organising { border: 1px solid; }
  /* Every stage is the same colour here, so the tick and the marker are
     what tell them apart — which is why the state was never colour
     alone. */
  .lb-console .cc-ll-stage__tick { forced-color-adjust: none; }
  .lb-console .cc-ll-stage__spin { border: 2px solid; }
  .lb-console .cc-ll-folder__back,
  .lb-console .cc-ll-folder__front,
  .lb-console .cc-ll-folder__sheet,
  .lb-console .cc-ll-sheet__page,
  .lb-console .cc-ll-sheet__tag,
  .lb-console .cc-ll-xl { forced-color-adjust: none; }
  /* THE DROP ZONE'S FOLDER IS NOT KEPT AS AUTHORED. It is a knockout — a
     white arrow cut out of a black body — and black on a dark
     high-contrast canvas is a folder nobody can see. So it is redrawn in
     the SYSTEM's own two colours, which is the same picture in either
     high-contrast theme rather than the right one in only half of them. */
  .lb-console .cc-ll-upfolder__body  { fill: CanvasText; forced-color-adjust: none; }
  .lb-console .cc-ll-upfolder__arrow { stroke: Canvas;  forced-color-adjust: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   USAGE & BILLING — M-14

   The owner's layout, in every state: the balance, with Add funds and the
   auto-reload state, beside Spend this month with its periods and its chart
   area; then one Billing card holding the Default card, "Payment method &
   receipts" and the billing history table. A freeze, or a read that failed,
   is stated once above them. The chart area is a grid with no series — no
   spending exists to draw yet — and the history table is the shared
   .lb-table.

   ── WHAT THIS BLOCK IS ALLOWED TO DECLARE ──────────────────────────────
   Layout, and the one colour a monochrome page cannot carry: the green that
   says auto-reload is on. Every other value is a shared token — the ink, the
   muted grey, the hairline, the card, the radius — and the freeze notice is
   the shared .lb-notice--error, not a red of this page's own.

   ── THE CARDS ARE THE SHARED CARD ──────────────────────────────────────
   .section-card, not a billing card. What is restated here is the padding,
   because the console's own density rule sets 14px/18px for a column of
   small panels and these two are the page rather than rows on it.
   ══════════════════════════════════════════════════════════════════════════ */

.lb-console {
  /* ── THE ONE GREEN ────────────────────────────────────────────────────
     Auto-reload being ON is the single state on this page that is good
     news, and the only place the product says so in colour. 5.4:1 on
     white — the tick beside it and the word On carry the same meaning, so a
     reader who cannot separate the hue still reads the state. */
  --ll-bill-ok: #0f7a3d;
}

.lb-console .cc-bill { display: block; }

/* Two cards of one height: what the account holds, and what it has spent —
   the spend card the wider of the two, as in the owner's design, because
   its chart needs the room. */
.lb-console .cc-bill__top {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 16px;
  margin-bottom: 20px;
  align-items: stretch;
}

.lb-console .cc-bill__card {
  padding: 18px 20px !important;
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.lb-console .cc-bill__body { display: flex; flex-direction: column; flex: 1; min-width: 0; }

.lb-console .cc-bill__label {
  display: flex; align-items: center; gap: 6px;
  font-size: 0.88rem; font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--text);
}

/* The freeze sits above the cards it explains. */
.lb-console .cc-bill__status { margin-bottom: 16px; }

/* ── THE BALANCE ROW ──────────────────────────────────────────────────────
   The figure and the one action on it, on one line, with the action hard
   right. They are a pair — "this is what you have, this is how you add to
   it" — and putting the button anywhere else makes the reader look for it.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__balance {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  margin-top: 14px;
}

/* The page's largest figure, and the only thing on it set in bold. Printed as
   the server wrote it, which can be long, so it wraps rather than overflows. */
.lb-console .cc-bill__amount {
  font-size: 2.25rem; font-weight: 700;
  letter-spacing: -0.03em; line-height: 1.1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The shared primary, at the reference's radius and weight. !important for
   the reason every override of it in this file is: glass.css sets padding,
   size and weight on .btn-dark with !important of its own. Its disabled look
   is the shared one. */
.lb-console .cc-bill__primary {
  flex: none;
  border-radius: 10px;
  padding: 10px 20px !important;
  font-size: 0.85rem !important;
  font-weight: 500 !important;
}

/* Why Add funds is disabled, directly under it. */
.lb-console .cc-bill__off { margin-top: 8px; }

/* ── AUTO-RELOAD: ITS STATE AND ITS ONE ACTION ────────────────────────────
   A tick and "On", or an information mark and "Off", the amounts under it,
   and beside it the one action — the shared secondary, as the payment
   method's is. The action wraps under the state when the card is narrow and
   takes the full width on a phone (the 700px block below). In the side panel
   the same state line heads the Turn off view, and the amounts are picked
   with Add funds' own picker and money field.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__autorow {
  display: flex; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap; gap: 10px 16px;
  margin-top: 18px;
}
/* The shared secondary at the primary's radius, so the card's two actions
   are one family. !important for glass.css's own on .btn-ghost. */
.lb-console .cc-bill__secondary {
  border-radius: 10px;
  padding: 10px 18px !important;
  font-size: 0.85rem !important;
  font-weight: 500 !important;
}
.lb-console .cc-bill__autostate { flex: 1 1 12rem; min-width: 0; }
.lb-console .cc-bill__auto {
  display: flex; align-items: center; gap: 7px;
  font-size: 0.85rem; font-weight: 500;
  color: var(--muted);
}
.lb-console .cc-bill__auto[data-on="true"] { color: var(--ll-bill-ok); }
.lb-console .cc-bill__autoicon { display: flex; flex: none; }
.lb-console .cc-bill__autodetail { margin-top: 4px; }
.lb-console .cc-bill__autoform .cc-bill__field + .cc-bill__field { margin-top: 16px; }
.lb-console .cc-bill__autoform .lb-hint,
.lb-console .cc-bill__autonote { margin-top: 12px; }
/* The consent step: the server's terms at the notice's size and in the text
   colour — not hint grey, because they are what the customer agrees to — and
   the shared .lb-check box under them. */
.lb-console .cc-bill__autoterms {
  margin: 12px 0 0; padding-left: 1.2rem;
  list-style: disc outside;
  font-size: 0.85rem; line-height: 1.55; color: var(--text);
}
.lb-console .cc-bill__autoterms li + li { margin-top: 6px; }
/* The agreement sits in the panel's footer, on a row of its own above the
   two buttons, so it is in view whenever "Turn on auto-reload" is: on a
   short phone the terms scroll above it and the box is never below the
   fold. And the shared row wraps, which dropped this sentence onto a line
   of its own under an orphaned box; here it wraps BESIDE its box, as a
   Campaign's list rows do. The box itself is the shared one, untouched. */
.lb-console .lb-drawer__foot:has(> .cc-bill__autoagree) { flex-wrap: wrap; }
.lb-console .cc-bill__autoagree { flex: 1 1 100%; flex-wrap: nowrap; }
.lb-console .cc-bill__autoagree .lb-check__text { flex: 1 1 auto; min-width: 0; }

/* ── THE CARDS IN THE SIDE PANEL ──────────────────────────────────────────
   Brand, last four and expiry, printed, one row per saved card.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__cardline {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 14px;
  font-size: 0.95rem; font-weight: 500; color: var(--text);
  min-width: 0;
}
/* A brand with no mark of its own, printed — in the side panel's rows, and
   inside the page's tile for any network the tile does not draw. */
.lb-console .cc-bill__brand {
  flex: none;
  padding: 2px 5px;
  border: 1px solid var(--ll-edge); border-radius: 4px;
  background: rgba(17, 24, 39, 0.03);
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.04em;
  color: var(--text);
}

/* ── SPENDING, BY PERIOD ──────────────────────────────────────────────────
   Title and ranges on one line, the figure under them, then the chart: the
   server's buckets as a 2px line in the product's ink over a light wash of
   the same ink, one solid hairline at each mark of the server's scale with
   the mark's text in the muted ink beside it, the bucket names under the
   plot — fewer of them where the plot is narrow — and an 8px dot where the
   line ends, ringed in the card's white so it reads where it meets the
   line. The readout over the plot is the cards' own surface, border and
   shadow, and is kept inside the plot. While another range is read, the figure
   and the chart hold what they showed, dimmed (aria-busy), rather than
   flashing empty. The plot is percent-scaled SVG, so it stretches with the
   card; everything with a shape that must not stretch — text, the dot, the
   readout — is HTML positioned over it.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__spendhead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.lb-console .cc-bill__periods { flex-wrap: nowrap; }
.lb-console .cc-bill__periods .cc-seg__btn { padding: 6px 12px; font-size: 0.8rem; }
.lb-console .cc-bill__spendamount { margin-top: 12px; }
.lb-console .cc-bill__spendfig { transition: opacity 120ms ease; }
.lb-console [aria-busy="true"] .cc-bill__spendfig { opacity: 0.45; }

/* The scale's text, then the plot; the bucket names under the plot. The
   text column is as wide as the widest mark, which the hidden sizer holds
   open, so a long figure never runs into the plot. */
.lb-console .cc-bill__chart {
  margin-top: 14px;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 8px;
}
.lb-console .cc-bill__yaxis { position: relative; grid-column: 1; grid-row: 1; }
.lb-console .cc-bill__ysizer { display: block; visibility: hidden; }
.lb-console .cc-bill__ytick { position: absolute; right: 0; transform: translateY(-50%); }
.lb-console .cc-bill__ysizer,
.lb-console .cc-bill__ytick,
.lb-console .cc-bill__xtick {
  font-size: 0.72rem; line-height: 1;
  white-space: nowrap;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.lb-console .cc-bill__plot {
  position: relative;
  grid-column: 2; grid-row: 1;
  height: 120px;
  border-radius: 4px;
  touch-action: pan-y;
}
.lb-console .cc-bill__plot:focus { outline: none; }
.lb-console .cc-bill__plot:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}
.lb-console .cc-bill__chartgrid {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  overflow: visible;
}
.lb-console .cc-bill__chartgrid line {
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.lb-console .cc-bill__area { fill: var(--text); fill-opacity: 0.08; stroke: none; }
.lb-console .cc-bill__line {
  fill: none;
  stroke: var(--text);
  stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.lb-console .cc-bill__dot {
  position: absolute;
  width: 8px; height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 0 2px var(--card, #fff);
  pointer-events: none;
}

/* The readout: a hairline at the bucket, and the bucket's figure and date
   beside it — on whichever side has room, placed by the page so that it
   stays inside the plot. */
.lb-console .cc-bill__cross {
  position: absolute; top: 0; bottom: 0;
  border-left: 1px solid var(--muted);
  pointer-events: none;
}
.lb-console .cc-bill__tip {
  position: absolute; z-index: 1;
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px;
  background: var(--card, #fff);
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
  box-shadow: var(--shadow);
  transform: translateY(-50%);
  white-space: nowrap;
  pointer-events: none;
}
.lb-console .cc-bill__cross[hidden],
.lb-console .cc-bill__tip[hidden] { display: none; }
.lb-console .cc-bill__tipvalue {
  font-size: 0.9rem; font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-bill__tipdate { font-size: 0.75rem; color: var(--muted); }

/* The bucket names. Only the major ones are named unless the axis itself is
   wide enough for all of them to be read apart. It is asked its own width,
   not the window's: the card is at its narrowest on a mid-sized screen,
   where it shares a row with the balance, not on a phone. A browser that
   cannot ask keeps the major ones, which fit anywhere. */
.lb-console .cc-bill__xaxis {
  position: relative;
  grid-column: 2; grid-row: 2;
  height: 1em;
  margin-top: 8px;
  container-type: inline-size;
}
.lb-console .cc-bill__xtick { position: absolute; top: 0; transform: translateX(-50%); }
.lb-console .cc-bill__xtick[data-edge="start"] { transform: none; }
.lb-console .cc-bill__xtick[data-edge="end"] { transform: translateX(-100%); }
.lb-console .cc-bill__xtick[data-minor] { display: none; }
@container (min-width: 400px) {
  .lb-console .cc-bill__xtick[data-minor] { display: block; }
}
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-bill__spendfig { transition: none; }
}
/* Colour is not available: the line, the wash and the dot are the text
   colour, the hairlines the system's own grey, the readout outlined. */
@media (forced-colors: active) {
  .lb-console .cc-bill__line { stroke: CanvasText; }
  .lb-console .cc-bill__area { fill: CanvasText; }
  .lb-console .cc-bill__dot { background: CanvasText; }
  .lb-console .cc-bill__cross { border-left-color: CanvasText; }
  .lb-console .cc-bill__chartgrid line { stroke: GrayText; }
  .lb-console .cc-bill__tip { border: 1px solid CanvasText; }
}

/* ── BILLING, THEN HISTORY ────────────────────────────────────────────────
   Two full-width cards (owner's reference, 6 October 2026). Billing: its
   title, the Default card's tile, masked number and expiry under it, and
   the payment method's one action — the page's primary, as Add funds is —
   hard right and centred on the card. History: its title and the table in a framed box
   that carries the cards' own shadow. Both titles are the top cards' own
   label: the console's Inter tops out at Medium, so the reference's heavier
   titles are drawn at the weight every other title on the page has.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__account { margin-top: 0; }
.lb-console .cc-bill__account + .cc-bill__account { margin-top: 20px; }
.lb-console .cc-bill__accounthead {
  display: flex; align-items: center; gap: 16px 32px; flex-wrap: wrap;
}
.lb-console .cc-bill__method { flex: 1; min-width: 0; }
.lb-console .cc-bill__manage {
  display: flex; flex-direction: column; align-items: flex-end;
  flex: none; max-width: 100%;
}

/* The Default card, read-only: a white tile with a hairline edge holding
   the brand's mark, then the last four behind a mask, in the muted ink at
   the weight drawn, with the expiry under it. Not a control — the action
   is the button beside it. The slot with no card proven is the same row,
   with the wallet mark in the tile. */
.lb-console .cc-bill__cardslot {
  display: flex; align-items: center; gap: 14px;
  max-width: 100%;
  margin: 12px 0 4px;
}
.lb-console .cc-bill__cardtext { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.lb-console .cc-bill__cardexp { line-height: 1.4; }
.lb-console .cc-bill__tile {
  display: flex; align-items: center; justify-content: center;
  flex: none;
  width: 70px; height: 48px;
  border: 1px solid var(--ll-edge);
  border-radius: 6px;
  background: var(--card, #fff);
}
.lb-console .cc-bill__mark { display: block; flex: none; }
/* A printed brand already sits in the tile's edge, so it drops its own. */
.lb-console .cc-bill__tile .cc-bill__brand { border: 0; background: none; }
.lb-console .cc-bill__tileicon {
  width: 24px; height: 24px;
  color: var(--lds-icon-color-inactive, #8E8E93);
}
.lb-console .cc-bill__cardnum { min-width: 0; overflow-wrap: anywhere; }
/* The page's card only: the side panel's rows keep their own ink. */
.lb-console .cc-bill__cardslot .cc-bill__cardnum {
  font-size: 0.95rem; font-weight: 500;
  color: var(--muted);
}
.lb-console .cc-bill__cardslot .cc-bill__cardnone { font-size: 0.88rem; font-weight: 400; }

/* The table: the shared .lb-table in a frame with the cards' shadow, the
   shared header band, body text a step larger, every column left-aligned
   under its heading, and the columns in the reference's proportions.

   ── THE HEADER KEEPS ITS PLACE; ONLY THE ROWS SCROLL ───────────────────
   History shows at most eight rows at once (owner decision, 9 October
   2026), and the shell's scroll contract leaves this stylesheet no pinned
   positioning to hold a header with. So the header is stationary by
   construction: the table, its head and its body are three blocks, and the
   body is the only one that scrolls. How tall eight rows are is set on the
   body by the page, measured from the rows as drawn (billingHistoryFit) —
   so with eight rows or fewer nothing is bound and no scrollbar shows.

   A table sizes its columns from every row at once; a head and a body that
   are separate blocks cannot. So each row is its own grid on ONE template,
   the header's row among them, and the columns line up because they are
   one template. Each column is a pair of tracks, and a cell spans its pair:
   the least width the column can always be read in, then its share of
   whatever room the frame has left over — the shares that give the
   reference's proportions on a desktop. No track is sized by what a row
   happens to hold, and every fractional one is minmax(0, …), so no text can
   set a floor for it. The page gives the head the width the rows' scrollbar
   takes, as padding, and the head carries the band and its hairline itself
   so both run the full width.
   ══════════════════════════════════════════════════════════════════════ */
.lb-console .cc-bill__history {
  margin-top: 12px;
  box-shadow: var(--cc-card-shadow);
  container-type: inline-size;
}
.lb-console .cc-bill__history .lb-table,
.lb-console .cc-bill__history .lb-table thead,
.lb-console .cc-bill__history .lb-table tbody { display: block; }
/* Five columns need this much to be read side by side. Only a browser that
   cannot ask the frame its width (below) ever meets it in a narrower frame,
   and there the table scrolls sideways inside the frame, as it always did. */
.lb-console .cc-bill__history .lb-table { min-width: 39rem; }
.lb-console .cc-bill__history .lb-table thead {
  background: var(--token-surface-secondary);
  border-bottom: 1px solid var(--line);
}
.lb-console .cc-bill__history .lb-table tbody { overflow-x: hidden; overflow-y: auto; }
.lb-console .cc-bill__history .lb-table tbody:focus { outline: none; }
.lb-console .cc-bill__history .lb-table tbody:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: -2px;
}
.lb-console .cc-bill__history .lb-table tr {
  display: grid;
  grid-template-columns:
    8.1rem minmax(0, 4fr) 7.5rem minmax(0, 20fr) 9.6rem minmax(0, 52fr) 7rem minmax(0, 1fr) 6.7rem minmax(0, 23fr);
}
.lb-console .cc-bill__history .lb-table th,
.lb-console .cc-bill__history .lb-table td { grid-column: span 2; min-width: 0; padding: 11px 16px; }
.lb-console .cc-bill__history .lb-table th { border-bottom: 0; }
.lb-console .cc-bill__history .lb-table td {
  font-size: 0.9rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.lb-console .cc-bill__history .lb-table td.lb-td--wrap,
.lb-console .cc-bill__history .lb-table td:nth-child(3) { white-space: normal; }
.lb-console .cc-bill__history .lb-table td.cc-bill__histnote {
  grid-column: 1 / -1;
  padding: 22px 14px;
  text-align: center;
  white-space: normal;
  color: var(--muted);
}

/* Where the frame is too narrow for five columns to be read side by side —
   a phone, or the card beside the sidebar on a small screen — each row
   folds to two lines on one template, the header's row with it, so nothing
   runs off the side and every heading still sits where its values do: the
   description and the amount, then the date, the payment details and the
   status. The frame is asked its own width, not the window's. */
@container (max-width: 639px) {
  .lb-console .cc-bill__history .lb-table { min-width: 0; }
  .lb-console .cc-bill__history .lb-table tr {
    grid-template-columns: 5.4rem minmax(0, 1fr) auto;
    grid-template-areas: "desc desc amount" "date pay status";
    gap: 2px 8px;
    padding: 10px 14px;
  }
  .lb-console .cc-bill__history .lb-table tbody tr { border-bottom: 1px solid var(--line); }
  .lb-console .cc-bill__history .lb-table tbody tr:last-child { border-bottom: 0; }
  .lb-console .cc-bill__history .lb-table th,
  .lb-console .cc-bill__history .lb-table td { padding: 0; border-bottom: 0; }
  /* On the narrowest phones a heading wraps rather than run under the next. */
  .lb-console .cc-bill__history .lb-table th { white-space: normal; }
  .lb-console .cc-bill__history .lb-table :is(th, td):nth-child(1) { grid-area: date; }
  .lb-console .cc-bill__history .lb-table :is(th, td):nth-child(2) { grid-area: desc; }
  .lb-console .cc-bill__history .lb-table :is(th, td):nth-child(3) { grid-area: pay; }
  .lb-console .cc-bill__history .lb-table :is(th, td):nth-child(4) { grid-area: amount; text-align: right; }
  .lb-console .cc-bill__history .lb-table :is(th, td):nth-child(5) { grid-area: status; text-align: right; }
  .lb-console .cc-bill__history .lb-table td:nth-child(1),
  .lb-console .cc-bill__history .lb-table td:nth-child(3),
  .lb-console .cc-bill__history .lb-table td:nth-child(5) { font-size: 0.8rem; color: var(--muted); }
  /* A date longer than its column — another language's — wraps under itself
     rather than run under the payment details. */
  .lb-console .cc-bill__history .lb-table td:nth-child(1) { white-space: normal; }
  .lb-console .cc-bill__history .lb-table td.cc-bill__histnote {
    grid-area: 1 / 1 / -1 / -1;
    padding: 12px 0;
    font-size: 0.9rem;
    text-align: center;
  }
}
/* The narrowest phones: the payment details take a line of their own, so
   they are read whole instead of broken across a column too thin for them. */
@container (max-width: 299px) {
  .lb-console .cc-bill__history .lb-table tr {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "desc amount" "date status" "pay pay";
  }
}

/* ── PAYMENT METHODS, IN THE SHARED SIDE PANEL ────────────────────────────
   Each saved card as a row: the card printed exactly as the page prints the
   Default one, the shared badge on the Default, and the row's own actions
   hard right — the shared quiet buttons. Hairlines between rows, as the
   history table has. Nothing else here is new.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__cards { list-style: none; margin: 0; padding: 0; }
.lb-console .cc-bill__cardrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px 16px; flex-wrap: wrap;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.lb-console .cc-bill__cardrow:first-child { padding-top: 0; }
.lb-console .cc-bill__cardinfo { flex: 1 1 200px; min-width: 0; }
.lb-console .cc-bill__cardinfo .cc-bill__cardline { margin-top: 0; }
.lb-console .cc-bill__cardacts { display: flex; flex-wrap: wrap; gap: 8px; flex: none; }

/* ── THE ADD FUNDS DIALOG ─────────────────────────────────────────────────
   The console's own dialog — .cc-modal-backdrop, the same panel, the same
   focus trap — carrying the shared segmented control as the amount picker
   and one money field for Other.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-bill__field { gap: 7px; }

/* The SHARED segmented control, restated where the reference differs: a
   rounded rectangle rather than a pill, and an active segment that is the
   product's ink with the card's own white on it. The pressed state is still
   aria-pressed, so the styling and the announcement cannot drift. */
.lb-console .cc-bill__seg {
  padding: 3px;
  border-radius: 10px;
  background: var(--card, #fff);
  flex-wrap: wrap;
}
.lb-console .cc-bill__seg .cc-seg__btn {
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 0.85rem;
  font-weight: 400;
}
.lb-console .cc-bill__seg .cc-seg__btn[aria-pressed="true"] {
  background: var(--text);
  color: var(--card, #fff);
  box-shadow: none;
  font-weight: 500;
}
@media (hover: hover) {
  .lb-console .cc-bill__seg .cc-seg__btn:not([aria-pressed="true"]):not([disabled]):hover {
    background: rgba(17, 24, 39, 0.05);
  }
}

/* The symbol is printed BESIDE the field rather than typed into it: a
   currency mark somebody can delete is a currency mark somebody will. */
.lb-console .cc-bill__money {
  display: flex; align-items: center; gap: 8px;
  height: 46px; padding: 0 14px;
  background: var(--card, #fff);
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
}
.lb-console .cc-bill__money:focus-within { border-color: rgba(17, 24, 39, 0.42); }
.lb-console .cc-bill__money:has(.cc-bill__moneyinput[aria-invalid="true"]) {
  border-color: rgba(185, 28, 28, 0.45);
}
.lb-console .cc-bill__moneysym { flex: none; color: var(--muted); font-size: 0.95rem; }
/* The whole height of the field is the input, so a tap anywhere on it but the
   currency sign focuses it — on a phone, the 46px field is the touch target. */
.lb-console .cc-bill__moneyinput {
  flex: 1; min-width: 0; align-self: stretch;
  border: 0; outline: none; background: none;
  font: inherit; font-size: 0.95rem; color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ── NARROW ───────────────────────────────────────────────────────────────
   At 700 and below the two cards stack, and the figure and its action stop
   competing for one line.
   ══════════════════════════════════════════════════════════════════════ */

@media (max-width: 700px) {
  .lb-console .cc-bill__top { grid-template-columns: minmax(0, 1fr); }
  .lb-console .cc-bill__balance { align-items: stretch; }
  .lb-console .cc-bill__primary { width: 100%; justify-content: center; min-height: 44px; }
  .lb-console .cc-bill__manage { align-items: stretch; width: 100%; }
  .lb-console .cc-bill__secondary { width: 100%; justify-content: center; min-height: 44px; }
  .lb-console .cc-bill__cardacts .btn-quiet { min-height: 44px; }
  /* The payment-methods panel's footer gets the same floor, its buttons
     sharing the row the way the Add funds sheet's do. Grown from their own
     width, never squeezed below it: "Add payment method" stays one line. */
  .lb-console .cc-bill__sheetbtn { flex: 1 1 auto; justify-content: center; min-height: 44px; }
  .lb-console .cc-bill__seg { width: 100%; }
  .lb-console .cc-bill__seg .cc-seg__btn { flex: 1; min-height: 44px; padding: 8px 6px; }
}

@media (max-width: 380px) {
  /* The figure gives up a step rather than the layout giving up a column. */
  .lb-console .cc-bill__amount { font-size: 1.8rem; }
  .lb-console .cc-bill__card { padding: 16px !important; }
  .lb-console .cc-bill__seg .cc-seg__btn { padding: 8px 2px; font-size: 0.8rem; }
}

/* Colour is not available, so the states are carried by the system's own two
   colours and by the shapes and words that were always there. */
@media (forced-colors: active) {
  .lb-console .cc-bill__seg .cc-seg__btn[aria-pressed="true"] { border: 2px solid; }
  .lb-console .cc-bill__brand { border: 1px solid; }
  .lb-console .cc-bill__tile { border: 1px solid; }
  .lb-console .cc-bill__money { border: 1px solid; }
}

/* ══════════════════════════════════════════════════════════════════════
   NEEDS ATTENTION — Gate 7, Master Plan v4 Part 22

   Every rule here is scoped to .lb-console and prefixed cc-attn-, so this
   screen composes the shared system rather than redefining any of it. The
   type and neutral border follow the existing console tokens.

   Needs Attention's switch is scoped here so the shared tabs elsewhere keep
   their existing appearance.
   ══════════════════════════════════════════════════════════════════════ */

/* ── The switch sits above the card, centered in the content column ──── */
.lb-console .cc-attn-bar {
  display: flex;
  justify-content: center;
  margin: 0 0 14px;
}

.lb-console .cc-attn-switch {
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 2px;
  max-width: 100%;
  margin: 0;
  padding: 3px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.90);
  box-shadow: 0 2px 8px -4px rgba(28, 24, 16, 0.16);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}

.lb-console .cc-attn-switch .cc-tab {
  flex: 0 1 auto;
  min-height: 34px;
  padding: 6px 13px;
  border: 0;
  border-radius: 9999px;
  background: transparent;
  color: #6b7280;
  font-family: var(--cc-font-ui, var(--font-ui));
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
}

.lb-console .cc-attn-switch .cc-tab.is-on {
  background: rgba(255, 255, 255, 0.98);
  color: #111827;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04);
}

.lb-console .cc-attn-total {
  margin: 0 0 12px;
  font-size: 0.85rem;
  color: var(--muted, #6b7280);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ── The underline tab ───────────────────────────────────────────────── */
.lb-console .cc-tabs--underline {
  gap: 22px;
  margin: 0;
  border-bottom: 1px solid var(--line, #e5e7eb);
  flex: 1 1 auto;
}

.lb-console .cc-tab--underline {
  padding: 6px 2px 12px;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: 0.95rem;
  /* The active tab is heavier AND underlined. Weight alone would be a
     colour-free signal that a reader still has to compare tabs to notice;
     the rule under it is the one that reads at a glance. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.lb-console .cc-tab--underline.is-on {
  background: none;
  color: var(--token-text, #111827);
  font-weight: 600;
  border-bottom-color: var(--token-text, #111827);
}

.lb-console .cc-tab--underline:hover { color: var(--token-text, #111827); }

.lb-console .cc-tab--underline:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ── The Campaign group cards ────────────────────────────────────────── */
.lb-console .cc-attn-grid {
  /* Flex rather than a fractional grid track. `1fr` means "at least
     min-content", so one long unbreakable Campaign name would set the
     track's floor and starve its neighbour — the trap this stylesheet
     forbids everywhere. `flex: 1 1 240px` gives the same behaviour (cards
     fill the row they are in, wrapping when they no longer fit) with a
     real floor instead of a content-derived one. */
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.lb-console .cc-attn-grid--follow-up,
.lb-console .cc-attn-grid--not-answered { gap: 20px; }

/* ── Nothing waiting: the empty tray (owner, 1 Oct) ─────────────────────
   The owner's reference is the authority for where the group sits: centred
   across, and down the room under the tabs at the reference's own
   proportion — 132px of room above the tray's marks to 228px below the
   sentence, the group a little above the middle. The group is the card
   body's own child under the tabs; on a desktop it fills the room the grown
   panel leaves (the one-panel rule, near the top of this file), and on a
   phone — where panels do not grow — the 360px the "No campaigns yet" hero
   uses there. Heading and sentence come from the shared big-empty-state
   rule (CAMPAIGNS PAGE). */
.lb-console .cc-attn-empty {
  flex: 1 0 auto;
  min-height: 360px;
  justify-content: flex-start;
  padding: 0 24px;
}
/* The room is shared out by the two spacers at the reference's 132:228.
   The 77px start on the lower one is what the tab bar's 18px margin and the
   render's own white margin above its marks already put above the group:
   with it, the VISIBLE marks — not the file's edge — land where the
   reference puts them, at any height of room. */
.lb-console .cc-attn-empty::before,
.lb-console .cc-attn-empty::after { content: ""; }
.lb-console .cc-attn-empty::before { flex: 132 1 0; }
.lb-console .cc-attn-empty::after { flex: 228 1 77px; }
.lb-console .cc-attn-empty::before { flex-grow: 3; }
.lb-console .cc-attn-empty::after { flex-grow: 2; }
/* The render is a square of near-white paper round the tray. 200px wide
   draws the tray the reference's size; the negative margin takes back the
   paper below its shadow, so the heading sits the reference's 33px under
   the tray itself. */
.lb-console .cc-attn-empty__art { display: block; margin-bottom: -15px; }
/* The paper is 253-254 grey on the card's 255 white, which a sharp screen
   shows as a pale square plate behind the tray (measured, 1 Oct). Two
   things remove it and leave the tray as drawn: a 1% lift takes the paper
   to the card's white (the tray's own greys move by at most 2 of 255 —
   its silhouette and shading are untouched), and the outer 8% of the
   square — paper only, levels 251-255; the tray, its marks and its shadow
   all lie inside it — fades into the card, so no edge is left either. */
.lb-console .cc-attn-empty__art img {
  display: block; width: 200px; height: auto;
  filter: brightness(1.01);
  -webkit-mask-image:
    linear-gradient(to right, transparent, #000 8%, #000 92%, transparent),
    linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent, #000 8%, #000 92%, transparent),
    linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
  mask-composite: intersect;
}
.lb-console .cc-attn-empty .cc-attn-empty__art img { transform: translateY(21px) scale(0.644); }
/* Held unshown until the render can be drawn with it (holdHeroForArt). */
.lb-console .cc-attn-empty--waiting { visibility: hidden; }
/* One line on a desktop, as the owner's reference shows it; a phone wraps. */
.lb-console .cc-attn-empty .lb-empty__text { max-width: 36rem; }
/* A phone draws the render at 160px, so its margins — and the start they
   give the lower spacer — scale with it. The narrower side padding keeps the
   heading on one line at 375px, as the reference sets it. */
@media (max-width: 560px) {
  .lb-console .cc-attn-empty { padding: 0 8px; }
  .lb-console .cc-attn-empty::after { flex-basis: 64px; }
  .lb-console .cc-attn-empty__art { margin-bottom: -6px; }
  .lb-console .cc-attn-empty__art img { width: 160px; }
  .lb-console .cc-attn-empty .cc-attn-empty__art img { transform: translateY(11px) scale(0.805); }
}

.lb-console .cc-attn-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1 1 240px;
  /* A flex child defaults to min-width:auto, which is the same starvation
     by another name. */
  min-width: 0;
  padding: 16px 18px;
  border: 1px solid var(--token-text, #111827);
  border-radius: 14px;
  background: var(--token-text, #111827);
  color: var(--card, #fff);
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.lb-console .cc-attn-card:hover { opacity: 0.92; }

.lb-console .cc-attn-card:focus-visible {
  outline: 2px solid var(--accent, #111827);
  outline-offset: 3px;
}

.lb-console .cc-attn-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.lb-console .cc-attn-card__title {
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.25;
}

.lb-console .cc-attn-card__sub {
  font-size: 0.85rem;
  /* Against the dark card, not a token meant for a light ground. */
  color: rgba(255, 255, 255, 0.72);
  overflow-wrap: anywhere;
}

/* The populated follow-up cards keep their own compact width, even alone. */
.lb-console .cc-attn-card--compact,
.lb-console .cc-attn-card--follow-up {
  flex: 0 1 256px;
  width: 256px;
  max-width: 100%;
  min-height: 86px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 14px;
  grid-template-rows: auto auto;
  align-content: center;
  gap: 9px 7px;
  padding: 14px 17px;
  border: 0;
  border-radius: 17px;
  background: #000;
  color: #fff;
}
.lb-console .cc-attn-card--compact .cc-attn-card__head { grid-column: 1; }
.lb-console .cc-attn-card--compact .cc-attn-card__title { font-size: 1.0625rem; }
.lb-console .cc-attn-card--compact .cc-attn-card__sub {
  grid-column: 1;
  display: flex;
  align-items: center;
  gap: 7px;
  color: #ff2d55;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.25;
}
.lb-console .cc-attn-card__alert {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #e00004;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}
.lb-console .cc-attn-card--compact .cc-attn-card__alert {
  width: 17px;
  height: 17px;
  font-size: 13.5px;
}
.lb-console .cc-attn-card__chevron {
  grid-column: 2;
  grid-row: 1 / 3;
  align-self: center;
  color: #fff;
  font-size: 28px;
  font-weight: 600;
  line-height: 1;
}
.lb-console .cc-attn-card--compact .cc-attn-card__chevron { font-size: 32px; }
.lb-console .cc-attn-card--not-answered .cc-attn-card__sub { color: #a36800; }
.lb-console .cc-attn-card--not-answered .cc-attn-card__alert { background: #a3661b; }
.lb-console .cc-attn-card--not-answered .cc-attn-card__phone-x {
  width: 12px;
  height: 12px;
  stroke-width: 2.1;
  transform: translateY(0.3px);
}

/* Detail headers sit inside the panel; the centered switch sits outside it. */
.lb-console .section-card .cc-attn-bar {
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  margin-bottom: 18px;
}
.lb-console .section-card .cc-attn-bar .cc-attn-total {
  margin: 0;
  text-align: left;
}
.lb-console .cc-attn-detail--refined .cc-attn-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 4px;
  border: 0 !important;
  border-radius: 0;
  background: transparent !important;
  box-shadow: none !important;
  color: var(--token-text, #111827);
  font-family: var(--cc-font-ui, var(--font-ui));
  font-weight: 500;
}
.lb-console .cc-attn-back__chevron {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 16px;
  height: 24px;
  font-size: 0;
  transform: translateY(4px);
}
.lb-console .cc-attn-back__chevron::before {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(135deg);
}
/* Campaign detail's leads table is drawn by these same rules (owner, 6
   October 2026): one frame, one header band, one cell rhythm and one View
   details for the two pages that list the same leads. Its selector is
   listed FIRST in each group, so every rule still ends on this page's own
   selector. */
.lb-console .cc-camp-detail__leads .cc-lead-scroll,
.lb-console .cc-attn-detail--refined .cc-attn-scroll {
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 14px;
  background: #fff;
}
.lb-console .cc-attn-detail--refined .lb-table { width: 100%; }
.lb-console .cc-camp-detail__leads .lb-table th,
.lb-console .cc-attn-detail--refined .lb-table th {
  padding: 13px 16px;
  background: #f8f9fb;
  color: var(--muted, #6b7280);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
.lb-console .cc-camp-detail__leads .lb-table td,
.lb-console .cc-attn-detail--refined .lb-table td {
  padding: 13px 16px;
  vertical-align: middle;
}
.lb-console .cc-camp-detail__leads .lb-table .cc-attn-status-cell,
.lb-console .cc-attn-detail--refined .lb-table .cc-attn-status-cell { text-align: center; }
.lb-console .cc-camp-detail__leads .lb-table tbody tr:last-child td,
.lb-console .cc-attn-detail--refined .lb-table tbody tr:last-child td { border-bottom: 0; }
.lb-console .cc-camp-detail__leads .cc-attn-view-details,
.lb-console .cc-attn-detail--refined .cc-attn-view-details {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 32px;
  padding: 5px 12px;
  border-color: #000 !important;
  border-radius: 999px;
  background: #000 !important;
  box-shadow: none !important;
  color: #fff !important;
}
.lb-console .cc-attn-view-details__chevron {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 12px;
  height: 16px;
  font-size: 0;
  transform: translateY(1.5px);
}
.lb-console .cc-attn-view-details__chevron::before {
  content: "";
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
}

/* ── The group table ─────────────────────────────────────────────────── */

/* §22.1 asks for bounded vertical scrolling for large lists, so a Campaign
   with four hundred unanswered leads does not push the action off screen. */
.lb-console .cc-attn-scroll {
  max-height: 60vh;
  overflow-y: auto;
}

.lb-console .cc-attn-tick { width: 44px; }

.lb-console .cc-attn-act { text-align: right; white-space: nowrap; }

.lb-console .cc-attn-counts {
  margin: 0 0 10px;
  font-size: 0.85rem;
  color: var(--muted, #6b7280);
  font-variant-numeric: tabular-nums;
}

.lb-console .cc-attn-actions {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.lb-console .cc-attn-blocked {
  margin: 0;
  flex: 1 1 240px;
}

/* ── The status chip ─────────────────────────────────────────────────── */

/* Fully specified here rather than composed from .cc-pill, which has
   modifiers in this sheet but no base rule anywhere — a chip built on it
   would inherit nothing and render as bare text.

   The two states differ in FILL as well as wording, never in hue alone. */
.lb-console .cc-attn-status {
  display: inline-block;
  padding: 3px 10px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 999px;
  font-size: 0.78rem;
  white-space: nowrap;
  color: var(--muted, #6b7280);
}

.lb-console .cc-attn-status--need_follow_up {
  border-color: var(--token-text, #111827);
  background: var(--token-text, #111827);
  color: var(--card, #fff);
}

/* The pill as this page's group tables draw it — and, in the same shape,
   every status pill on Campaign detail (owner, 6 October 2026), so the two
   pages' pills cannot drift apart. Only the fills below differ. A Campaign
   pill with no fill of its own keeps the plain chip above: a hairline and
   grey words. */
.lb-console .cc-camp-detail__leads .cc-attn-status,
.lb-console .cc-attn-detail--follow-up .cc-attn-status--need_follow_up,
.lb-console .cc-attn-detail--not-answered .cc-attn-status--not_answered {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 32px;
  box-sizing: border-box;
  padding: 5px 12px;
}

.lb-console .cc-camp-detail__leads .cc-attn-status--need_follow_up,
.lb-console .cc-attn-detail--follow-up .cc-attn-status--need_follow_up {
  border-color: #e00004;
  background: #e00004;
  color: #fff;
}

.lb-console .cc-attn-status--not_answered {
  border-style: dashed;
}
.lb-console .cc-camp-detail__leads .cc-attn-status--not_answered,
.lb-console .cc-attn-detail--not-answered .cc-attn-status--not_answered {
  border: 1px solid #a36800;
  background: #a36800;
  color: #fff;
}

/* ── The detail drawer ───────────────────────────────────────────────── */
.lb-console .cc-facts {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0 0 18px;
}

.lb-console .cc-fact { display: flex; flex-direction: column; gap: 2px; }

.lb-console .cc-fact dt {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
}

.lb-console .cc-fact dd {
  margin: 0;
  color: var(--token-text, #111827);
  overflow-wrap: anywhere;
}

.lb-console .cc-attn-block { margin: 0 0 18px; }

.lb-console .cc-attn-h {
  margin: 0 0 6px;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
  font-weight: 600;
}

.lb-console .cc-attn-ul {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.lb-console .cc-attn-ul li { overflow-wrap: anywhere; }

.lb-console .cc-attn-note { width: 100%; resize: vertical; }

/* ══════════════════════════════════════════════════════════════════════════
   VIEW RESULT, AS A CENTRED DIALOG (owner, 4 October 2026)

   The SAME #drawer — its backdrop, its X, Escape, focus return, the body
   openLeadResult fills and the refresh that re-reads a pending result —
   drawn as a centred card when openDrawer is given the 'dialog' look. Every
   other drawer keeps the shared right-hand panel and phone sheet.

   Built from what is already here: the shared .lb-drawer-backdrop (its tint
   and blur, untouched), the import dialog's card (.cc-modal__panel's radius,
   border and shadow) and this console's type. These selectors outrank both
   shared shapes — the panel and the 720px sheet — so one set of rules holds
   at every width, and a phone gets the same card with smaller margins, not
   a different design. The result's own wording and order are untouched:
   only how the sections are set changes, from grey capitals to the bold
   headings and dividers the owner's reference shows.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .lb-drawer[data-look="dialog"] {
  top: 0; right: 0; bottom: 0; left: 0;
  margin: auto;
  width: min(560px, calc(100vw - 48px));
  height: fit-content;
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow: hidden;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-xl, 16px);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 2px 8px rgba(0, 0, 0, 0.06);
  opacity: 0;
  transform: translateY(8px) scale(0.98);
  transition: opacity 200ms ease, transform 260ms cubic-bezier(.32, .72, .36, 1);
}
.lb-console .lb-drawer[data-look="dialog"][data-open="true"] { opacity: 1; transform: none; }
.lb-console .lb-drawer[data-look="dialog"] .lb-drawer__grip { display: none; }
.lb-console .lb-drawer[data-look="dialog"] .lb-drawer__head {
  padding: 24px 24px 4px;
  border-bottom: 0;
}
/* glass.css sets every h2 to 400 !important; this one title opts out. */
.lb-console .lb-drawer[data-look="dialog"] .lb-drawer__title {
  font-size: 1.5rem;
  font-weight: 600 !important;
  line-height: 1.25;
  color: var(--text, #111827);
}
/* The body is the dialog's one scroll, and its basis is its content — never
   the shared drawer's flex: 1, a 0% basis. The dialog is as tall as its
   content (fit-content), and WebKit sizes that from the body's basis: on an
   iPhone the 0% left a card the height of its header, its result clipped and
   nothing to scroll (owner, 5 October 2026). Held by the dialog's max-height,
   the body shrinks (min-height: 0) and scrolls under a finger. */
.lb-console .lb-drawer[data-look="dialog"] .lb-drawer__body {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 6px 24px 24px;
}
/* Needs Attention's actions stay the dialog's own footer; on a phone they
   wrap onto a second line rather than run off the card. */
.lb-console .lb-drawer[data-look="dialog"] .lb-drawer__foot { flex-wrap: wrap; }

/* Each section: a bold heading over plain text, a hairline under it. */
.lb-console .lb-drawer[data-look="dialog"] .cc-facts { gap: 0; margin: 0; }
.lb-console .lb-drawer[data-look="dialog"] .cc-fact,
.lb-console .lb-drawer[data-look="dialog"] .cc-attn-block {
  gap: 4px;
  margin: 0;
  padding: 14px 0;
  border-bottom: 1px solid var(--line, #e5e7eb);
}
/* Only the dialog's first section sits closer to the title: Needs
   Attention's draws more than one list of facts. */
.lb-console .lb-drawer[data-look="dialog"] .cc-facts:first-of-type > .cc-fact:first-child { padding-top: 8px; }
.lb-console .lb-drawer[data-look="dialog"] .cc-fact dt,
.lb-console .lb-drawer[data-look="dialog"] .cc-attn-h {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text, #111827);
}
.lb-console .lb-drawer[data-look="dialog"] .cc-attn-h { margin-bottom: 6px; }
.lb-console .lb-drawer[data-look="dialog"] .cc-fact dd,
.lb-console .lb-drawer[data-look="dialog"] .cc-attn-ul {
  font-size: 0.9375rem;
  line-height: 1.55;
}
/* The disposition leads, as a filled pill: the console's one red for a
   follow-up, its primary black for a decline and its Not answered gold for a
   call nobody answered, white on each. The pill is the value itself — the
   same label in the same <dd> — so only how the result is set changes. Only
   this dialog lifts it above the Summary: the result panel's own order is
   untouched, and the first section's smaller top space moves up with it. */
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone] { order: -1; padding-top: 8px; }
.lb-console .lb-drawer[data-look="dialog"] .cc-facts:has(> .cc-fact[data-tone]) > .cc-fact:first-child:not([data-tone]) {
  padding-top: 14px;
}
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="follow-up"] dd,
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="not-interested"] dd,
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="not-answered"] dd {
  align-self: flex-start;
  margin-top: 4px;
  padding: 6px 16px;
  border-radius: 999px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  color: #fff;
}
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="follow-up"] dd { background: #b91c1c; }
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="not-interested"] dd {
  background: var(--token-btn-action-bg, #101012);
}
/* The colour this console already gives Not answered (.cc-attn-status). */
.lb-console .lb-drawer[data-look="dialog"] .cc-fact[data-tone="not-answered"] dd { background: #a36800; }
/* The transcript is the last section, so no hairline closes it. Its turns
   sit in one bordered box that scrolls by itself, so a long call never
   makes the dialog taller than the window. */
.lb-console .lb-drawer[data-look="dialog"] #resultTranscript .cc-attn-block {
  padding-bottom: 0;
  border-bottom: 0;
}
/* Needs Attention's Call result ends with the owner's note, so there the
   transcript keeps its hairline and the note, the last section, draws none. */
.lb-console .lb-drawer[data-look="dialog"] [data-attn-item] #resultTranscript .cc-attn-block {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line, #e5e7eb);
}
.lb-console .lb-drawer[data-look="dialog"] [data-attn-item] > .cc-attn-block:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}
/* A Not answered record's saved reason ("The call was not answered.") is
   its disposition's caption: under the pill, above the hairline. */
.lb-console .lb-drawer[data-look="dialog"] [data-attn-item] > .cc-facts:has(+ .lb-hint) > .cc-fact:last-child {
  padding-bottom: 8px;
  border-bottom: 0;
}
.lb-console .lb-drawer[data-look="dialog"] [data-attn-item] > .cc-facts + .lb-hint {
  margin: 0;
  padding: 0 0 14px;
  border-bottom: 1px solid var(--line, #e5e7eb);
}
.lb-console .lb-drawer[data-look="dialog"] .cc-result-turns {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 12px 14px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 10px);
}
/* The time beside the speaker's name, not centred on a long turn. */
.lb-console .lb-drawer[data-look="dialog"] .cc-result-turns .cc-event { align-items: baseline; }

/* A phone: the same card, nearly the full width with the console's 16px
   gutter each side, and one scroll — the dialog's — rather than a box
   scrolling inside a box under a thumb. A long result takes about seven
   tenths of the screen, so the page still shows around it; a short one is
   only as tall as its content (owner, 5 October 2026). */
@media (max-width: 560px) {
  .lb-console .lb-drawer[data-look="dialog"] {
    width: calc(100vw - 32px);
    max-height: 72vh;
    max-height: min(72dvh, calc(100dvh - 32px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)));
  }
  .lb-console .lb-drawer[data-look="dialog"] .lb-drawer__head { padding: 18px 18px 2px; }
  .lb-console .lb-drawer[data-look="dialog"] .lb-drawer__title { font-size: 1.375rem; }
  .lb-console .lb-drawer[data-look="dialog"] .lb-drawer__body { padding: 4px 18px 20px; }
  .lb-console .lb-drawer[data-look="dialog"] .cc-result-turns {
    max-height: none;
    overflow-y: visible;
    padding: 12px;
  }
}

/* ── Small screens ───────────────────────────────────────────────────── */

/* The table scrolls inside its wrapper on small screens. */
@media (max-width: 560px) {
  .lb-console .cc-attn-switch .cc-tab { padding-inline: 10px; font-size: 0.8rem; }
  .lb-console .cc-attn-card { flex-basis: 100%; }
  .lb-console .cc-attn-card--compact { flex: 0 1 256px; }
  .lb-console .cc-attn-scroll { max-height: 70vh; }
}

@media (pointer: coarse) {
  .lb-console .cc-camp-detail__leads .cc-attn-status,
  .lb-console .cc-attn-detail--follow-up .cc-attn-status--need_follow_up { min-height: 44px; }
  .lb-console .cc-attn-detail--not-answered .cc-attn-status--not_answered { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-attn-card { transition: none; }
}

@media (forced-colors: active) {
  .lb-console .cc-attn-card { border: 1px solid; }
  .lb-console .cc-tab--underline.is-on { border-bottom: 2px solid; }
  .lb-console .cc-attn-switch .cc-tab.is-on { border: 2px solid; }
  .lb-console .cc-attn-status { border: 1px solid; }
}

/* ══════════════════════════════════════════════════════════════════════
   CAMPAIGN DETAIL — THE OWNER'S LAYOUT (6 October 2026)

   The page behind a Campaign card, laid out after the owner's reference
   and built from what is already here: Needs Attention's table, pills and
   View details (its rules above carry this page's selector, so the two
   pages share one definition), the console's one card shadow, and the
   platform's semantic colours, read as tokens with their values as the
   fallback — the pattern this file already uses for --lds-color-green.
   Every rule is scoped to this page's own classes.
   ══════════════════════════════════════════════════════════════════════ */

/* ── The header's state pill ─────────────────────────────────────────────
   Sized from the line it sits on (.page-sub), as the reference draws the
   two at one size, and one width for the short words, as its Completed and
   Ongoing share one. Completed is the platform's success green; Ongoing —
   running, by the Dashboard's rule — its indigo; every other state its
   inactive grey. White on that green measures about 2.2:1, the trade the
   saved-language dots (.cc-v3__dot) already state; the word is the state,
   so the fill is never the only signal. */
.lb-console .lb-page-head .page-sub .cc-camp-state {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 7.5em;
  min-height: 1.6em;
  box-sizing: border-box;
  /* With the space after it, the reference's ~10px to the words. */
  margin-right: 6px;
  padding: 0 1em;
  border-radius: 999px;
  background: var(--lds-color-gray, #8E8E93);
  color: #fff;
  line-height: 1.2;
  white-space: nowrap;
  vertical-align: middle;
}
.lb-console .lb-page-head .page-sub .cc-camp-state[data-state="completed"] {
  background: var(--lds-color-green, #34C759);
}
.lb-console .lb-page-head .page-sub .cc-camp-state[data-state="running"] {
  background: var(--lds-color-indigo, #5856D6);
}

/* ── One row of five cards: the four counters, then the Notes ───────────
   The reference's row: Leads, Called, Still to do, Do not call and the
   Campaign's Notes, five cards of one width. The Notes card takes a fifth
   of the row less its share of the four gaps, so the counters' grid beside
   it divides the rest into four cards of exactly that width. */
.lb-console .cc-camp-detail__summary {
  --cc-camp-detail-gap: 24px;
  display: flex;
  align-items: stretch;
  gap: var(--cc-camp-detail-gap);
  margin: 0 0 24px;
}
.lb-console .cc-camp-detail__summary > .cc-stats {
  flex: 1 1 0;
  min-width: 0;
  gap: var(--cc-camp-detail-gap);
  margin: 0;
}
.lb-console .cc-camp-detail__summary > .cc-camp-notes {
  flex: 0 0 calc((100% - 4 * var(--cc-camp-detail-gap)) / 5);
  min-width: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
}

/* Each card: its label in the page's ink and in the words as written —
   not the shared small capitals — and the figure large beneath it, as the
   reference sets them. A counter's note keeps its small grey line. */
.lb-console .cc-camp-detail__summary .stat-card,
.lb-console .cc-camp-detail__summary > .cc-camp-notes {
  padding: 9px 19px 11px !important;
  border-radius: 12px;
}
.lb-console .cc-camp-detail__summary .cc-stat { display: block; }
.lb-console .cc-camp-detail__summary .stat-label,
.lb-console .cc-camp-detail__summary > .cc-camp-notes .section-title {
  margin: 0 !important;
  color: var(--text, #111827);
  font-size: 0.9375rem !important;
  font-weight: 400 !important;
  line-height: 1.2;
  letter-spacing: normal !important;
  text-transform: none !important;
}
.lb-console .cc-camp-detail__summary .stat-value {
  margin-top: 5px;
  font-size: 2.375rem !important;
  line-height: 1 !important;
}
.lb-console .cc-camp-detail__summary .cc-stat__note { margin-top: 4px; }

/* The Notes card sets its words as a counter sets its note. Beside the
   counters it adds no height of its own: the row is as tall as its
   counters, and long Notes scroll inside their card rather than
   stretching all five. */
.lb-console .cc-camp-detail__summary > .cc-camp-notes .section-head { margin: 0 0 4px; }
.lb-console .cc-camp-detail__summary > .cc-camp-notes .cc-card__body {
  flex: 1 1 auto;
  min-height: 2.7em;
  contain: size;
  overflow-y: auto;
  overscroll-behavior: contain;
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-detail__summary > .cc-camp-notes .cc-card__body > p {
  margin: 0;
  font-size: inherit;
  line-height: inherit;
  color: inherit;
}

/* Too narrow for five across: two counters a row (the shared .cc-stats
   rule) and the Notes under them at full width, as tall as their words. */
@media (max-width: 1180px) {
  .lb-console .cc-camp-detail__summary { flex-direction: column; }
  .lb-console .cc-camp-detail__summary > .cc-stats,
  .lb-console .cc-camp-detail__summary > .cc-camp-notes { flex: none; }
  .lb-console .cc-camp-detail__summary > .cc-camp-notes .cc-card__body {
    contain: none;
    min-height: 0;
    overflow-y: visible;
  }
}

/* ── The leads ────────────────────────────────────────────────────────── */
/* A row's first lines share one baseline — a name, a date, its pills and
   View details — so a note under one cell never lifts the pills beside it
   out of line. A row without notes sits as Needs Attention's do. */
.lb-console .cc-camp-detail__leads .lb-table td { vertical-align: baseline; }
/* No lead in view can be ticked, so the tick column is not drawn. */
.lb-console .cc-camp-detail__leads--nopick .cc-lead__pick-cell { display: none; }
/* A date never breaks inside itself. */
.lb-console .cc-camp-detail__leads .cc-lead__when { white-space: nowrap; }
/* Contacted: the platform's success green. Not contacted: its inactive
   grey. Both in Needs Attention's pill shape, white-lettered like its two. */
.lb-console .cc-camp-detail__leads .cc-attn-status--contacted {
  border-color: var(--lds-color-green, #34C759);
  background: var(--lds-color-green, #34C759);
  color: #fff;
}
.lb-console .cc-camp-detail__leads .cc-attn-status--not_contacted {
  border-color: var(--lds-color-gray, #8E8E93);
  background: var(--lds-color-gray, #8E8E93);
  color: #fff;
}
/* The one note a pill still has — Result's "being prepared" and its kin —
   starts a little under it, and wraps between words only, so a phrase is
   never split inside itself. */
.lb-console .cc-camp-detail__leads .cc-attn-status-cell .lb-row__sub {
  margin-top: 4px;
  overflow-wrap: normal;
}
/* The tick column is its 44px target and no more. */
.lb-console .cc-camp-detail__leads .lb-table .cc-lead__pick-cell {
  padding-left: 4px;
  padding-right: 0;
}
/* The row's actions stack at the column's end: View details, then the
   quieter Do not call under it — one column as narrow as the reference's
   single View details allows. */
.lb-console .cc-camp-detail__leads .cc-lead__act > .btn-sm {
  display: flex;
  width: fit-content;
  margin-left: auto;
}
.lb-console .cc-camp-detail__leads .cc-lead__act > .btn-sm + .btn-sm { margin-top: 4px; }

/* Under the leads: §20.6's sentence, alone on its line since the page lost
   its retry button (owner, 8 October 2026). The room under it is the
   floating Ask Agent launcher's corner (assistant.css: 24px from the
   corner, about 38px tall): at the end of a long list the launcher would
   otherwise sit over the sentence. */
.lb-console .cc-camp-detail__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  margin: 18px 0 44px;
}
.lb-console .cc-camp-detail__foot > .lb-hint { flex: 1 1 260px; margin: 0; }

/* A phone: the row reflows as it always has (CAMPAIGN DETAIL, earlier in
   this file). The table's cell rhythm above is for the table; a stacked
   row keeps its own, an empty cell is not drawn, and a pill starts its
   line. */
@media (max-width: 720px) {
  .lb-console .cc-camp-detail__leads .lb-table td { padding: 2px 0; }
  .lb-console .cc-camp-detail__leads .lb-table td:empty { display: none; }
  .lb-console .cc-camp-detail__leads .lb-table .cc-attn-status-cell,
  .lb-console .cc-camp-detail__leads .lb-table .cc-attn-act { text-align: left; }
  .lb-console .cc-camp-detail__leads .lb-table td.cc-attn-act { padding-top: 8px; }
  .lb-console .cc-camp-detail__leads .cc-lead__act > .btn-sm { margin-left: 0; }
}

@media (forced-colors: active) {
  .lb-console .lb-page-head .page-sub .cc-camp-state { border: 1px solid; }
}

/* ══════════════════════════════════════════════════════════════════════
   TRASH BIN — Gate 8 Package B, Master Plan v4 Part 25

   No approved Trash reference screenshot exists in the design pack, so this
   composes the shared system rather than inventing a look for it: the same
   section card, the same facts list the Needs Attention drawer uses, the
   same buttons, the same empty state. Every rule is scoped to .lb-console
   and prefixed cc-trash-, and no shared class is redefined.
   ══════════════════════════════════════════════════════════════════════ */

.lb-console .cc-trash-note { margin: 0 0 16px; }

/* Flex rather than a fractional grid track: `1fr` means "at least
   min-content", so one long unbreakable Campaign name would set the track's
   floor and starve the card beside it. This stylesheet forbids that
   everywhere. */
.lb-console .cc-trash-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* ── THE SAME TILE A LIVE CAMPAIGN GETS ───────────────────────────────
   Since the owner's Gate 5B correction the Trash Bin is a tab of the
   Campaigns page, and its cards are drawn as Campaign cards: the article
   carries .cc-camp-card and its body carries .cc-camp-card__tile, so the
   dark tile, the title, the overflow menu and the caption slot are the
   Campaign card's own rules. What is Trash-specific is only the caption's
   meaning — the countdown — and it sits under the tile where a Waiting
   card's queue place sits. */
.lb-console .cc-trash-card {
  /* A flex child defaults to min-width:auto, which is starvation by another
     name. */
  min-width: 0;
}

.lb-console .cc-trash-card__title {
  overflow-wrap: anywhere;
}

.lb-console .cc-trash-card__due { margin: 0; }

/* The countdown. Never colour alone: the word carries the meaning and the
   weight reinforces it, so a reader who cannot separate hues still reads
   "Past its 30 days" rather than a red number. */
.lb-console .cc-trash-due {
  font-size: 0.9rem;
  color: var(--muted, #6b7280);
  font-variant-numeric: tabular-nums;
}

.lb-console .cc-trash-due--over {
  font-weight: 600;
  color: var(--token-text, #111827);
}

/* The permanent-delete confirmation is the only compact, centred Trash dialog. */
.lb-console .cc-trash-purge-modal {
  position: fixed;
  inset: 0;
  z-index: 5000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(15, 23, 42, 0.28);
  backdrop-filter: blur(5px);
}
.lb-console .cc-trash-purge-modal__card {
  width: min(400px, 100%);
  padding: 24px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 16px;
  background: var(--card, #fff);
  box-shadow: 0 20px 60px rgba(15, 23, 42, 0.2);
}
.lb-console .cc-trash-purge-modal__card h2 {
  margin: 0 0 20px;
  color: var(--text, #111827);
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.4;
  overflow-wrap: anywhere;
}
.lb-console .cc-trash-purge-modal__actions {
  display: flex;
  gap: 10px;
}
.lb-console .cc-trash-purge-modal__actions button {
  flex: 1;
  min-height: 42px;
  justify-content: center;
}
.lb-console .cc-trash-purge-modal__actions .btn-danger {
  border-color: #b91c1c;
  background: #b91c1c;
  color: #fff;
}
.lb-console .cc-trash-purge-modal__actions .btn-danger:hover { background: #991b1b; }
.lb-console:has(.cc-trash-purge-modal) .cc-toasts { z-index: 5001; }

/* Step 2's locked Sol Review: one calm, single-column card. The surrounding
   Agent setup shell and its Sources view keep their existing styles. */
.lb-console .cc-biz__solreview {
  width: min(680px, 100%);
  margin: 12px auto 0;
  overflow: hidden;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 14px;
  background: var(--card, #fff);
}

.lb-console .cc-biz__solsection { padding: 0 24px; }
.lb-console .cc-biz__solsection:first-child { padding-top: 20px; }
.lb-console .cc-biz__solsection + .cc-biz__solsection { padding-top: 20px; }
.lb-console .cc-biz__solsection:last-of-type { padding-bottom: 18px; }
.lb-console .cc-biz__solsection h3 {
  margin: 0 0 8px;
  color: var(--token-text, #111827);
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.3;
}
.lb-console .cc-biz__solsection ul {
  margin: 0;
  padding-left: 22px;
  color: var(--token-text, #111827);
}
.lb-console .cc-biz__solsection li { margin: 3px 0; line-height: 1.45; }
/* The card has no footer: its Continue is the step header's, top right. */

@media (max-width: 560px) {
  /* One card per row. The card already wraps its own long values, so the
     page body never scrolls sideways at 375px or 430px. */
  .lb-console .cc-trash-card { flex-basis: 100%; }
}

@media (forced-colors: active) {
  .lb-console .cc-trash-card { border: 1px solid; }
  .lb-console .cc-trash-due--over { text-decoration: underline; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE COLD CALLING SIDEBAR, AND WHY THESE OVERRIDES ARE SCOPED

   The rules being corrected live in labbie-dashboard-desktop.css, which
   frontend/dashboard.html also loads. Editing them there would restyle that
   product's sidebar too, which nobody asked for — so every rule below is
   scoped to `body.lb-console` and reaches only this console. If the platform
   later wants this treatment everywhere, these move up into the shared sheet
   and this block goes away; it is deliberately shaped to be liftable.

   The shared sheet's own `.nav-item.active` is already neutral
   (rgba(0,0,0,0.05)). The warm tint comes from the DESKTOP sheet overriding
   it with #f1ece2 !important, so these have to be `!important` too — they
   are answering an `!important`, not winning an ordinary cascade.
   ══════════════════════════════════════════════════════════════════════════ */

/* Labels and icons in black, resting and active alike. The desktop sheet
   paints them #404656; a grey rail against black page text reads as disabled
   rather than as navigation. */
.lb-console .sidebar .nav-item,
.lb-console .sidebar .nav-item:hover,
.lb-console .sidebar .nav-item.active {
  color: #111827 !important;
}
.lb-console .sidebar .nav-item svg { color: inherit; }

/* The selected row: neutral light grey, no warm tint.
   OPAQUE on purpose. The desktop sheet makes the rail transparent over a
   #f4efe4 page, so a translucent black — which is what the shared sheet
   uses — would pick that beige straight back up. Written rgb() rather than
   hex because ui-check reads any #f…/#e… fill on a .lb-/.btn- selector as a
   yellow or orange control; this is neither, and the notation says which. */
.lb-console .sidebar .nav-item.active {
  background: rgb(241, 241, 243) !important;
}
.lb-console .sidebar .nav-item:hover {
  background: rgba(17,24,39,0.04) !important;
}

/* Sign out is a bordered card rather than a bare row, and the hairline that
   sat above it is gone — the border already separates it, and the two
   together read as a rule plus a box for no reason. The element is removed
   from the markup; this keeps it gone if any sheet re-declares it. */
.lb-console .sidebar .sb-logout-divider { display: none !important; }
.lb-console .sidebar .nav-item.logout {
  border: 1px solid var(--line, #e5e7eb) !important;
  background: var(--card, #fff) !important;
  color: #111827 !important;
  margin-top: 4px;
}
.lb-console .sidebar .nav-item.logout:hover {
  background: rgba(17,24,39,0.04) !important;
  color: #111827 !important;
}

/* ── Campaign stat tiles ────────────────────────────────────────────────
   The six labels in black. The shared .stat-label is --muted, which is right
   for a dense dashboard and too quiet for six tiles that ARE the page head. */
.lb-console .cc-camp-stat .stat-label { color: #111827; }

/* Not Contacted: the icon itself black. */
.lb-console .cc-camp-stat__icon[data-tone="neutral"] { color: #111827; }

/* All six stat icons are drawn at 32px, centred on the 24px slot (owner,
   8 October 2026): the five outcome tiles' filled badges (the i-stat-*
   symbols, which carry their own colours) and Total Campaign's target, which
   keeps its sidebar design at the same size. The grid and slot are unchanged. */
.lb-console .cc-camp-stat__icon .cc-i {
  width: 32px;
  height: 32px;
}
/* The target's outer ring fills less of its box than a badge's disc, so its
   box is larger for the two to read the same size (about 29px across), and
   its stroke is thinner so the line keeps its weight. */
.lb-console .cc-camp-stat__icon[data-tone="information"] .cc-i {
  width: 38px;
  height: 38px;
  stroke-width: 1.3;
}

/* The tone colours below are what an outcome icon drawn as a line would
   take; the i-stat-* badges set their own fills. */
.lb-console .cc-camp-stat__icon[data-tone="missed"] {
  color: #b91c1c;
}
.lb-console .cc-camp-stat__icon[data-tone="uninterested"] {
  color: #7C3AED;
}

/* ── The first-run megaphone ────────────────────────────────────────────
   Sized here rather than in the artwork so the empty state keeps its own
   rhythm. .lb-empty__icon is the shared 48px icon pill; this is artwork, so
   the pill's ground, radius AND hairline border all go — the owner's render
   is shown unframed, as supplied.

   The render's background is TRANSPARENT — the owner replaced the earlier
   file precisely to remove the near-white square that showed around it — so
   there is no ground to hide and nothing here may put one back: no fill, no
   border, no radius, no shadow.

   `contain` rather than `cover`. The artwork occupies a square region inside
   a wider transparent canvas, so `cover` happened to work on this particular
   export by cropping only empty pixels — a coincidence of one file's
   margins, not a rule. `contain` cannot clip whatever the canvas turns out
   to be, and the slot is given the canvas's own 1507:1044 proportion so the
   megaphone still fills it rather than sitting in letterbox bars. */
.lb-console .cc-camp-empty__art {
  /* The reference's proportion: the render about as wide as the heading
     under it, not wider than the sentence. */
  width: 160px;
  /* `height: auto` is load-bearing: .lb-empty__icon is the shared 48px icon
     pill, and an explicit height beats aspect-ratio. Without this the render
     is squashed into a 190x48 letterbox. */
  height: auto;
  aspect-ratio: 1507 / 1044;
  max-width: 100%;
  display: block;
  margin: 0 auto;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.lb-console .cc-camp-empty__art img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
}

/* The plus sits before the label on both Add Campaign buttons. */
.lb-console .cc-camp-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.lb-console .cc-camp-cta__plus { width: 16px; height: 16px; stroke-width: 2; }

/* ══════════════════════════════════════════════════════════════════════════
   THE CAMPAIGN BUTTON SHAPE — A ROUNDED RECTANGLE, NOT A CAPSULE

   The platform's .btn-dark is a 999px pill. That is right for the small
   inline controls it was drawn for and wrong at this size: at 44px tall a
   999px radius is a capsule with semicircular ends, which is not what the
   approved Campaign composition shows. So this is a RADIUS AND A SIZE, set on
   the Campaign CTA and on the creation card's own two actions, and nothing
   else about .btn-dark is touched — not its colour, not its type, not its
   states, and not the pill it still is everywhere else in the console.

   Both Add Campaign buttons — the page header's and the first run's — are the
   one campaignNewButton(), so they wear this together and cannot drift. The
   creation card's primary is given the same class for the same reason.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-camp-cta,
.lb-console .cc-camp-create__actions > .btn-quiet,
.lb-console .cc-camp-create__actions > .btn-dark {
  border-radius: 12px;
  min-height: 44px;
  /* !important because glass.css — a shared sheet this product does not own —
     sets .btn-dark's padding with !important of its own. Same reason the page
     header takes a `headClass` to change its title. Only the two properties
     that are actually contested are forced. */
  padding: 10px 20px !important;
  font-size: 14px !important;
  /* glass.css also sets .btn-dark's weight to 400 with !important, which
     left Add Campaign lighter than Cancel beside it. Forced here, on these
     Campaign buttons only, so the pair reads as one pair. */
  font-weight: 600 !important;
  line-height: 1.2;
}

/* The PAGE's Add Campaign — the header's and the first run's, one function —
   wears the Platform Design System's universal button geometry: 48px tall, a
   16px radius, 24px of padding, a 15px semibold label and a 24px icon with an
   8px gap (docs/design-system §8), read from its --lds-button-* tokens with
   each value as the fallback, since this console does not load tokens.css
   yet. The card's own footer keeps the 44px pair above, as approved. */
.lb-console .cc-camp-cta[data-new-campaign] {
  min-height: var(--lds-button-height, 48px);
  min-width: var(--lds-button-min-width, 120px);
  border-radius: var(--lds-button-radius, 16px);
  padding: 12px var(--lds-button-padding-inline, 24px) !important;
  gap: var(--lds-button-icon-gap, 8px);
  font-size: var(--lds-button-label-size, 15px) !important;
}
.lb-console .cc-camp-cta[data-new-campaign] .cc-camp-cta__plus {
  width: var(--lds-button-icon-size, 24px);
  height: var(--lds-button-icon-size, 24px);
}

/* The secondary: white ground, black text, a real border — a button that
   reads as a button, which a borderless grey label does not. */
.lb-console .cc-camp-create__actions > .btn-quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--lds-surface-raised, #fff);
  color: var(--lds-text-primary, #111827);
  border: 1px solid var(--lds-border-default, #d1d5db);
}
.lb-console .cc-camp-create__actions > .btn-quiet:hover:not(:disabled) {
  background: var(--lds-surface-hover, #f3f4f6);
}
.lb-console .cc-camp-create__actions > .btn-quiet:active:not(:disabled) {
  background: var(--lds-surface-active, #e5e7eb);
}

/* Pressed and disabled, stated once for every Campaign primary. Disabled is
   dimmed AND uninteractive: a button that still looks pressable but does
   nothing is the one state that reads as a broken page. */
.lb-console .cc-camp-cta:active:not(:disabled),
.lb-console .cc-camp-create__actions > .btn-dark:active:not(:disabled) {
  transform: translateY(1px);
}
/* ── DISABLED DIMS; BUSY DOES NOT ─────────────────────────────────────
   setBusy() sets `disabled` as well as aria-busy — deliberately, so a second
   submission cannot be issued — so every dimming rule has to exempt the busy
   case, or the button that is working looks exactly like the button that is
   refusing. The system's own button rules carry this exemption and these do
   too, in the same shape. */
.lb-console .cc-camp-cta:disabled:not([aria-busy="true"]),
.lb-console .cc-camp-create__actions > .btn-dark:disabled:not([aria-busy="true"]),
.lb-console .cc-camp-create__actions > .btn-quiet:disabled:not([aria-busy="true"]) {
  opacity: 0.55;
  cursor: not-allowed;
}
.lb-console .cc-camp-create__go[aria-busy="true"] { cursor: progress; }
.lb-console .cc-camp-cta:focus-visible,
.lb-console .cc-camp-create__actions > .btn-quiet:focus-visible,
.lb-console .cc-camp-create__actions > .btn-dark:focus-visible {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-camp-cta:active:not(:disabled),
  .lb-console .cc-camp-create__actions > .btn-dark:active:not(:disabled) {
    transform: none;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE CREATION CARD — Master Plan v4 §18

   Composition, not a new visual language. The card, the section labels, the
   fields, the checkboxes and both buttons are the platform's own; what these
   rules own is the arrangement — a name that reads as the card's heading,
   the four semantic sections under it in the reference's column, the
   operational schedule after them, and one row of actions at the end.

   ── THERE IS ONE SCROLL CONTEXT, AND IT IS THE PAGE ─────────────────────
   The card is as tall as its content and the page's own scroll carries it,
   which is why the actions can sit in normal flow at the end. The one
   bounded scroller inside it is the Lead List checklist, which §18.12 asks
   to be searchable "when many lists exist" — a control with a height, not a
   second page.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The name, which is the heading ────────────────────────────────────
   `min-width: 0` on both the row and the input is what actually contains a
   long name. Without it the input's default intrinsic width becomes a flex
   floor, the row grows past the card, and the page gets a horizontal
   scrollbar. With it the input stops at the row's width and scrolls its own
   text while it is being typed — the value is never touched. */
.lb-console .cc-camp-create__head {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  margin: 0 0 4px;
}
/* As wide as the name, so the pencil sits beside the words (4.1). The
   `size` attribute campaignNameSize() writes is the same measure for a
   browser without field-sizing; either way it never exceeds its row. */
.lb-console .cc-camp-create__name {
  flex: 0 1 auto;
  min-width: 0;
  width: auto;
  field-sizing: content;
  max-width: min(420px, 100%);
  margin: 0;
  /* 44px, like the pencil beside it. This field replaced a standard .lb-input
     with a borderless heading, and a heading's padding is not a tap target's:
     without this the one control that names the Campaign would be the
     shortest thing on the card, and on a phone it lands at about 32px. */
  min-height: 44px;
  padding: 4px 2px;
  border: 0;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  background: none;
  font: inherit;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--lds-text-primary, #111827);
  text-overflow: ellipsis;
}
/* --muted, not the dashboard's lighter grey: that one is below AA on white
   and this product does not put words in it. */
.lb-console .cc-camp-create__name::placeholder {
  color: var(--muted, #6b7280);
  font-weight: 600;
}
.lb-console .cc-camp-create__name:hover {
  border-bottom-color: var(--lds-border-subtle, #e5e7eb);
}
.lb-console .cc-camp-create__name:focus {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 2px;
  border-bottom-color: transparent;
}
/* The pencil is a <label> pointing at the input, so it needs the affordance
   of a button and none of the behaviour. 44px, because it is tapped. */
.lb-console .cc-camp-create__pencil {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  color: var(--lds-text-secondary, #6b7280);
  cursor: pointer;
}
.lb-console .cc-camp-create__pencil:hover {
  background: var(--lds-surface-hover, #f3f4f6);
  color: var(--lds-text-primary, #111827);
}
.lb-console .cc-camp-create__pencil .cc-i { width: 18px; height: 18px; }

/* ── The sections ─────────────────────────────────────────────────────── */
.lb-console .cc-camp-create__body { min-width: 0; }
.lb-console .cc-camp-create__section { margin-top: 18px; min-width: 0; }
.lb-console .cc-camp-create__label {
  margin: 0 0 8px;
  font-size: 0.92rem;
  /* glass.css — a shared sheet this product does not own — sets
     `h1, h2, h3 { font-weight: 400 !important }`, and these headings are
     <h2>: without the !important every one rendered at 400, lighter than
     the 500 field labels under it. Only this contested property is forced,
     on this card's headings alone, as the Campaign buttons already do. */
  font-weight: 600 !important;
  line-height: 1.3;
  /* Black: the primary labels of this card, the same ink as From and Until
     (Platform Design System --lds-color-black). */
  color: var(--lds-color-black, #000000);
}
.lb-console .cc-camp-create__sub {
  margin: 12px 0 6px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted, #6b7280);
}

/* ── The fields ───────────────────────────────────────────────────────
   Long text is contained rather than trimmed. A goal grows to a readable
   height and then scrolls inside itself; a picker holding a very long
   project name stays the width of its column and ellipses what will not
   fit, because a <select> that widens to its longest option is how a form
   acquires a horizontal scrollbar nobody asked for. */
.lb-console .cc-camp-create .lb-field,
.lb-console .cc-camp-create .lb-fieldrow { min-width: 0; }
.lb-console .cc-camp-create .lb-input,
.lb-console .cc-camp-create .lb-select,
.lb-console .cc-camp-create .lb-textarea {
  max-width: 100%;
  min-width: 0;
}
.lb-console .cc-camp-create .lb-select {
  text-overflow: ellipsis;
}
/* No resize handle (owner, 24 Sep): the two boxes keep one size, and long
   text scrolls inside them. Border, focus and scrolling are unchanged. */
.lb-console .cc-camp-create .lb-textarea {
  min-height: 76px;
  max-height: 280px;
  overflow-y: auto;
  resize: none;
}
/* ── The goal's and the Notes' word counts, inside their own fields ─────
   One bordered box holds the textarea and, in a row of its own under the
   text, the count at the bottom-right. The box wears .lb-textarea's border,
   radius and focus treatment (labbie-dashboard-components.css) and the
   textarea inside it gives its own border up, so there is still one border,
   the count never sits over typed text, and the textarea keeps scrolling on
   its own. The count is styled from the box, not by a class of its own: the
   input handler rewrites its className to lb-hint or lb-error as it counts.
   Instructions and Notes are the same field with different limits, so they
   share every rule here rather than each having a copy. */
.lb-console .cc-camp-goal__box,
.lb-console .cc-camp-notes__box {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-md, 10px);
  transition: border-color 160ms ease;
}
.lb-console .cc-camp-goal__box:focus-within,
.lb-console .cc-camp-notes__box:focus-within {
  border-color: rgba(17, 24, 39, 0.42);
  outline: 2px solid rgba(17, 24, 39, 0.55);
  outline-offset: 2px;
}
.lb-console .cc-camp-goal__box > .lb-textarea,
.lb-console .cc-camp-notes__box > .lb-textarea {
  border: 0;
  background: transparent;
}
.lb-console .cc-camp-goal__box > .lb-textarea:focus-visible,
.lb-console .cc-camp-notes__box > .lb-textarea:focus-visible { outline: none; }
.lb-console .cc-camp-goal__box > .lb-hint,
.lb-console .cc-camp-goal__box > .lb-error,
.lb-console .cc-camp-notes__box > .lb-hint,
.lb-console .cc-camp-notes__box > .lb-error {
  margin: 0;
  padding: 0 12px 8px;
  text-align: right;
}
/* The Notes box has one more thing in that bottom row: the paperclip at the
   left, opposite the count at the right (owner, 24 Sep). A grid rather than
   the goal box's column: the text takes the whole first row, and the
   paperclip and the count share the second, so neither can sit over typed
   text or over each other. The count keeps every rule above; in this row it
   is centred on the paperclip rather than padded under it. */
.lb-console .cc-camp-notes__box {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
}
.lb-console .cc-camp-notes__box > .lb-textarea { grid-column: 1 / -1; }
.lb-console .cc-camp-notes__box > .lb-hint,
.lb-console .cc-camp-notes__box > .lb-error {
  grid-column: 2;
  padding-bottom: 0;
}
/* The paperclip: the glyph alone, in the console's icon ink, with a target a
   finger can hit. Its ink starts on the same line the typed text starts on.
   It has no pill or circle of its own, only the focus ring, which surrounds
   it when the keyboard is on it. Disabled, it is visibly faded and does
   nothing. */
.lb-console .cc-camp-attach__clip {
  grid-column: 1;
  position: relative;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  width: 36px;
  height: 36px;
  /* The glyph sits at a fixed inset whatever the target's size, so its ink
     starts where the textarea's 12px text inset starts. */
  padding-left: 10px;
  margin: 0 0 2px;
  border-radius: 8px;
  color: var(--muted, #6b7280);
  cursor: pointer;
}
.lb-console .cc-camp-attach__clip .cc-i { width: 18px; height: 18px; }
.lb-console .cc-camp-attach__clip:hover { color: var(--text, #111827); }
.lb-console .cc-camp-attach__clip:focus-within {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 0;
}
.lb-console .cc-camp-attach__clip.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.lb-console .cc-camp-attach__clip.is-disabled:hover { color: var(--muted, #6b7280); }
@media (pointer: coarse) {
  .lb-console .cc-camp-attach__clip { width: 44px; height: 44px; }
}
/* A refusal from the server can be a paragraph. It wraps, it stays inside the
   card, and it never pushes the actions sideways. */
.lb-console .cc-camp-create .lb-error,
.lb-console .cc-camp-create .lb-notice {
  max-width: 100%;
  overflow-wrap: anywhere;
}
/* A section that cannot be filled in yet still stands in the column as a
   field — the same height, edge and radius as the controls around it — with
   its reason written inside. */
.lb-console .cc-camp-create__blocker {
  display: flex;
  align-items: center;
  margin: 0;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: var(--cc-camp-field-radius);
}

/* ── §18.3 — the Agent, fixed ─────────────────────────────────────────
   The reference's field shape, so the four sections read as one column,
   and no control inside it: a name, a readiness badge, a way to the Agent. */
/* One solid hairline and the Goal control's radius, ready or not: whether
   the Agent can call is said by the chip inside, in words, so the frame
   carries no state of its own (the old dashed frame did, and read as a
   different kind of control). --cc-camp-field-radius is shared by every
   picker-shaped row on this card. */
.lb-console .cc-camp-create { --cc-camp-field-radius: var(--radius-md, 10px); }
.lb-console .cc-camp-agent {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 8px 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
  background: var(--lds-surface-raised, #fff);
}
.lb-console .cc-camp-agent__avatar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--token-surface-secondary, #f3f4f6);
  color: var(--lds-text-primary, #111827);
}
.lb-console .cc-camp-agent__avatar .cc-i { width: 20px; height: 20px; }
/* The animated mark fills the avatar box instead of sitting at the symbol's
   20px, because it brings its own padding: the artwork spans ~203 of its
   320 viewBox, so 36px here draws a mark about 23px across — the size the
   symbol read at 20px. Square, so it cannot stretch; the circle, the row
   and the 52px minimum height are all untouched. */
.lb-console .cc-camp-agent__mark { display: block; width: 36px; height: 36px; }
.lb-console .cc-camp-agent__name {
  min-width: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-agent__link { margin-left: auto; font-size: 0.85rem; }

/* ── §18.4 — the Lead List checklist and the calling order ───────────── */
/* The picker: one outlined field in the Goal control's shape, which opens
   onto the checklist beneath it — the same checkboxes, never a <select>.
   With no lists it is the same field, saying so, and it does not open. */
.lb-console .cc-camp-picker {
  margin: 0;
  min-width: 0;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
  background: var(--card, #fff);
}
.lb-console .cc-camp-picker__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 44px;
  padding: 10px 12px;
  list-style: none;
  cursor: pointer;
  border-radius: var(--cc-camp-field-radius);
  color: var(--text, #111827);
}
.lb-console .cc-camp-picker__trigger::-webkit-details-marker { display: none; }
.lb-console .cc-camp-picker__trigger::marker { content: ""; }
.lb-console .cc-camp-picker__trigger:focus-visible {
  outline: 2px solid rgba(17, 24, 39, 0.55);
  outline-offset: 2px;
}
.lb-console .cc-camp-picker__value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lb-console .cc-camp-picker__value.is-placeholder { color: var(--muted, #6b7280); }
.lb-console .cc-camp-picker__chevron {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--muted, #6b7280);
  transition: transform 160ms ease;
}
.lb-console .cc-camp-picker[open] > .cc-camp-picker__trigger .cc-camp-picker__chevron {
  transform: rotate(180deg);
}
.lb-console .cc-camp-picker[open] > .cc-camp-picker__trigger {
  border-bottom: 1px solid var(--line, #e5e7eb);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.lb-console .cc-camp-picker__panel { padding: 8px 10px 10px; }
/* Inside the picker the field is the frame, so the list drops its own. */
.lb-console .cc-camp-picker .cc-camp-lists { border: 0; border-radius: 0; padding: 0; }
.lb-console .cc-camp-picker--empty {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 12px;
  background: var(--token-surface-secondary, #f9fafb);
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-picker--empty + .lb-hint { margin-top: 6px; }
/* An edit's Lead List (§14): the same field, read only. The lock says so
   beside the words, and the whole name is shown — wrapped onto a second
   line on a phone rather than cut off, because it is the one place the
   editor names the list it calls from. */
.lb-console .cc-camp-picker--locked {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  min-width: 0;
  padding: 10px 12px;
  background: var(--token-surface-secondary, #f9fafb);
  color: var(--text, #111827);
}
.lb-console .cc-camp-picker--locked + .lb-hint { margin-top: 6px; }
.lb-console .cc-camp-picker__lock { color: var(--muted, #6b7280); }
.lb-console .cc-camp-picker__locked-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-camp-picker__chevron { transition: none; }
}
.lb-console .cc-camp-lists__search { margin-bottom: 8px; }
.lb-console .cc-camp-lists {
  list-style: none;
  margin: 0;
  padding: 6px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--lds-border-subtle, #e5e7eb);
  border-radius: 12px;
}
.lb-console .cc-camp-lists__item .lb-check {
  width: 100%;
  min-height: 44px;
  align-items: center;
  /* The shared row wraps, which on a phone dropped a long list name onto a
     line of its own under an orphaned checkbox. Here the name wraps BESIDE
     its box instead. */
  flex-wrap: nowrap;
}
.lb-console .cc-camp-lists__item .lb-check__text {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-lists__count { color: var(--muted, #6b7280); }
.lb-console .cc-camp-lists__why {
  display: block;
  font-size: 0.8rem;
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-lists__item.is-unavailable .lb-check {
  cursor: not-allowed;
  opacity: 0.7;
}
.lb-console .cc-camp-order {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lb-console .cc-camp-order__item {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--lds-border-subtle, #e5e7eb);
  border-radius: 10px;
}
.lb-console .cc-camp-order__n {
  flex: 0 0 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  font-size: 0.78rem;
  font-weight: 600;
  background: #101012;
  color: #fff;
}
.lb-console .cc-camp-order__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-order__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0 !important;
}
.lb-console .cc-camp-order__btn .cc-i { width: 18px; height: 18px; }
.lb-console .cc-i--up { transform: rotate(180deg); }

/* ── §18.6 — the project rows, and what is nested inside the section ───
   The card shows the choice and a picker shows the catalogue (owner,
   7 October 2026), so the page is no longer as tall as the approved list is
   long. A row is a mark, the project's name and approved summary, and the
   one control that acts on it. Built from what is already here: the Agent
   row's mark surface, the attachment row's cross, and the shared ghost
   button, checkbox, input and badge. */
.lb-console .cc-camp-proj__head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  margin-bottom: 8px;
}
.lb-console .cc-camp-proj__head h2,
.lb-console .cc-camp-proj__head h3 { margin: 0; }
/* The heading and the line that counts what is chosen, stacked, with the
   section's one button beside them. */
.lb-console .cc-camp-proj__heading {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* "Additional Projects": this card's heading ink and weight, one level down,
   because it sits INSIDE the Primary Project section (§18.2). glass.css sets
   every h3 to 400 !important, as it does the h2s above it. */
.lb-console .cc-camp-proj__title {
  font-size: 0.92rem;
  font-weight: 600 !important;
  line-height: 1.3;
  color: var(--lds-color-black, #000000);
}
/* Change and Add projects: the shared ghost button, in this card's own
   rounded rectangle rather than a capsule. !important for the reason the
   card's two actions carry it: glass.css forces .btn-ghost's padding and
   font-size. */
.lb-console .cc-camp-proj__act {
  flex: 0 0 auto;
  margin-left: auto;
  min-height: 40px;
  border-radius: 12px;
  padding: 8px 16px !important;
  font-size: 14px !important;
  font-weight: 600;
  line-height: 1.2;
}
.lb-console .cc-camp-proj__act:focus-visible {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 2px;
}
.lb-console .cc-camp-proj__meta { margin: 0; }
.lb-console .cc-camp-proj {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.lb-console .cc-camp-proj__mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md, 10px);
  background: var(--token-surface-secondary, #f3f4f6);
  color: var(--lds-text-primary, #111827);
}
.lb-console .cc-camp-proj__mark .cc-i { width: 20px; height: 20px; }
.lb-console .cc-camp-proj--primary .cc-camp-proj__mark { width: 48px; height: 48px; }
.lb-console .cc-camp-proj__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lb-console .cc-camp-proj__name { font-weight: 600; overflow-wrap: anywhere; }
.lb-console .cc-camp-proj__sum {
  font-size: 0.85rem;
  color: var(--muted, #6b7280);
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-proj__none { color: var(--muted, #6b7280); }
/* The attachment row's cross, reused as it is; here it sits beside text that
   can be long, so it is told not to give up its width to it. */
.lb-console .cc-camp-proj__remove { flex: 0 0 auto; }
/* On the card a summary is held to two lines, so a long one cannot make the
   page tall again. The picker shows it whole. */
.lb-console .cc-camp-create .cc-camp-proj__sum {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* A chosen project that is no longer approved says so in the console's one
   red, the colour of the blocker under the button that it explains. */
.lb-console .cc-camp-proj.is-gone .cc-camp-proj__sum { color: #b91c1c; }
/* The reference's hairlines: under the Primary Project, between the chosen
   Additional Projects, and under them. Drawn by these blocks, never by the
   Notes host below, which keeps no frame of its own. */
.lb-console .cc-camp-proj--primary,
.lb-console .cc-camp-addl {
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line, #e5e7eb);
}
.lb-console .cc-camp-addl__list {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
}
.lb-console .cc-camp-addl__list > .cc-camp-proj { padding: 10px 0; }
.lb-console .cc-camp-addl__list > .cc-camp-proj:last-child { padding-bottom: 0; }
.lb-console .cc-camp-addl__list > .cc-camp-proj + .cc-camp-proj {
  border-top: 1px solid var(--line, #e5e7eb);
}
@media (pointer: coarse) {
  .lb-console .cc-camp-proj__act { min-height: 44px; }
}

/* ── §18.6 — the two pickers ──────────────────────────────────────────
   Both are the shared drawer in its centred 'dialog' look, unchanged. These
   rules are only what a searchable list needs inside it: the search stays
   put, the list is the one thing that scrolls, and the Additional picker's
   count and two buttons stay below it. */
.lb-console .cc-camp-pick {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
}
.lb-console .cc-camp-pick__search { position: relative; flex: 0 0 auto; }
.lb-console .cc-camp-pick__glass {
  position: absolute;
  top: 50%;
  left: 12px;
  transform: translateY(-50%);
  color: var(--muted, #6b7280);
  pointer-events: none;
}
.lb-console .cc-camp-pick__search .lb-input {
  min-height: 44px;
  padding-left: 38px;
}
.lb-console .cc-camp-pick__list {
  /* The containing block of what it scrolls: without it the rows' visually
     hidden text is placed against the dialog instead, far below its edge,
     and the dialog itself gains a scroll it should not have. */
  position: relative;
  flex: 0 1 auto;
  min-height: 0;
  max-height: 420px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.lb-console .cc-camp-pick__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lb-console .cc-camp-pick__row {
  display: flex;
  /* The shared checkbox row wraps, which would put its box on a line of its
     own above a long project. Here the project stays BESIDE its box. */
  flex-wrap: nowrap;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius-md, 10px);
  background: none;
  color: var(--lds-text-primary, #111827);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.lb-console .cc-camp-pick__row:hover { background: var(--lds-surface-hover, #f3f4f6); }
.lb-console .cc-camp-pick__row.is-on,
.lb-console .cc-camp-pick__row.is-primary { background: var(--token-surface-secondary, #f3f4f6); }
.lb-console .cc-camp-pick__row.is-on .cc-camp-proj__mark,
.lb-console .cc-camp-pick__row.is-primary .cc-camp-proj__mark { background: var(--lds-surface-raised, #fff); }
.lb-console .cc-camp-pick__row.is-primary { cursor: default; }
.lb-console button.cc-camp-pick__row:focus-visible {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: -2px;
}
.lb-console .cc-camp-pick__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.lb-console .cc-camp-pick__tick { flex: 0 0 auto; }
.lb-console .cc-camp-pick__none { margin: 0; padding: 16px 4px; }
.lb-console .cc-camp-pick__count {
  margin: 0 auto 0 0;
  align-self: center;
  font-size: 0.85rem;
  color: var(--muted, #6b7280);
}
/* The dialog that holds a picker: its body does not scroll, the list in it
   does. On a phone it may use the whole safe area rather than Call result's
   seven tenths of the screen, because a catalogue wants the room. */
.lb-console .lb-drawer[data-look="dialog"]:has(.cc-camp-pick) .lb-drawer__body {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@media (max-width: 560px) {
  .lb-console .lb-drawer[data-look="dialog"]:has(.cc-camp-pick) {
    max-height: calc(100vh - 32px);
    max-height: calc(100dvh - 32px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  }
  .lb-console .cc-camp-pick__list { max-height: none; }
  /* Beside a checkbox on a phone, the mark would leave a project's words a
     column a few words wide. It is decoration, so the words get its room. */
  .lb-console .cc-camp-pick__body > .cc-camp-proj__mark { display: none; }
  .lb-console .cc-camp-proj { gap: 10px; }
  .lb-console .cc-camp-proj--primary .cc-camp-proj__mark { width: 40px; height: 40px; }
}
/* Contained by Primary Project — §18.2's "not a fifth top-level field" — by
   sitting inside its section, under its choice, with no frame of its own:
   a second card inside the card would be one more box in a column of boxes,
   and its inset pushed the Notes field off the edge every other field keeps. */
.lb-console .cc-camp-project-ctx {
  margin-top: 16px;
  padding: 0;
  border: 0;
}
.lb-console .cc-camp-attach { margin-top: 8px; }
/* Campaign Detail's read-only Notes: the customer's own line breaks, and a
   long unbroken word wraps instead of pushing the page sideways. */
.lb-console .cc-camp-notes__text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-attach__list {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Nothing attached and nothing happening: no gap under the Notes box. */
.lb-console .cc-camp-attach:empty { display: none; }
.lb-console .cc-camp-attach__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.lb-console .cc-camp-attach__icon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  color: var(--muted, #6b7280);
}
/* "Word document — price-list.docx": the kind, then the whole name. A long
   name wraps inside the row rather than widening the card. */
.lb-console .cc-camp-attach__file {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
.lb-console .cc-camp-attach__type { color: var(--muted, #6b7280); }
.lb-console .cc-camp-attach__sep { color: var(--muted, #6b7280); }
.lb-console .cc-camp-attach__name { color: var(--text, #111827); }
.lb-console .cc-camp-attach__notes {
  flex-basis: 100%;
  font-size: 0.8rem;
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-attach__actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Remove: the cross alone, named for the file it removes. While the removal
   is in flight the shared busy spinner stands in for the cross. */
.lb-console .cc-camp-attach__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted, #6b7280);
  cursor: pointer;
}
.lb-console .cc-camp-attach__remove:hover { color: var(--text, #111827); }
.lb-console .cc-camp-attach__remove:focus-visible {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 0;
}
.lb-console .cc-camp-attach__remove[aria-busy="true"] .cc-i { display: none; }
@media (pointer: coarse) {
  .lb-console .cc-camp-attach__remove { width: 44px; height: 44px; }
}

/* ── §19.7 — the calling schedule, an operational control ─────────────
   Seven day toggles and two hours. Nothing is ticked by default, because the
   schedule domain refuses to default the days. */
.lb-console .cc-camp-days {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0 12px;
}
.lb-console .cc-camp-day {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 52px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--lds-border-default, #d1d5db);
  border-radius: 12px;
  background: var(--lds-surface-raised, #fff);
  color: var(--lds-text-primary, #111827);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.lb-console .cc-camp-day.is-on {
  background: #101012;
  border-color: #101012;
  color: #fff;
}
.lb-console .cc-camp-day:focus-within {
  outline: 2px solid var(--lds-focus-ring, #2563eb);
  outline-offset: 2px;
}
.lb-console .cc-camp-hours { max-width: 360px; }
/* From and Until are bold AND black (#000000, the owner's rule), at every
   width — the shared .lb-label is a grey, and a bold grey is not this. */
.lb-console .cc-camp-hours .lb-label {
  font-weight: 600;
  color: var(--lds-color-black, #000000);
}

/* ── The foot ─────────────────────────────────────────────────────────
   The blockers first, in words; then the server's refusal when there was
   one; then the two actions, wrapping rather than overflowing. Cancel
   stays first in the source so tabbing reaches it before the button that
   commits. */
.lb-console .cc-camp-create__foot {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--lds-border-subtle, #e5e7eb);
}
.lb-console .cc-camp-create__blockers,
.lb-console .cc-camp-create__missing {
  margin: 0 0 10px;
  padding-left: 18px;
  font-size: 0.85rem;
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-create__missing { color: var(--lds-text-primary, #111827); }
/* The one sentence about the unsaved draft: true, and quiet — a hairline
   rectangle on the fields' edges rather than a filled card of its own. */
.lb-console .cc-camp-create__draft {
  margin: 20px 0 0;
  padding: 10px 12px;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--muted, #6b7280);
  background: none;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
}
.lb-console .cc-camp-create__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
}

/* ── EDITING A CAMPAIGN (§13–§17) ─────────────────────────────────────
   The Add Campaign card again, so almost nothing here is new: the state
   line under the name, the Agent-updated notice's spacing, the leads the
   Campaign calls, and the line saying what Confirm will do. Composition of
   the console's own badges, notices, buttons and lead table only — no
   colour, radius or shadow of its own. */
.lb-console .cc-camp-edit__state {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
}
.lb-console .cc-camp-edit__state + .lb-notice { margin-top: 12px; }
.lb-console .cc-camp-edit__agent { margin-top: 10px; }
.lb-console .cc-camp-agent + .lb-notice { margin-top: 10px; }
.lb-console .cc-camp-edit__pending { margin: 0 0 8px; }
.lb-console .cc-camp-edit__leads .cc-lead-scroll { margin-top: 8px; }
/* A lead's own controls wrap under each other in a narrow cell instead of
   widening the table. */
.lb-console .cc-camp-edit__acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.lb-console .cc-camp-edit__table .lb-error { margin: 4px 0 0; }
.lb-console .cc-camp-edit__after { margin: 0 0 10px; }

/* ── "Analysing ···" ──────────────────────────────────────────────────
   Hidden until the button says it is busy, which is the shared setBusy()'s
   doing and nothing else's. The owner's file is 320x320 with its dots across
   the middle; `cover` in a wide, short box crops the transparent margins to
   exactly that band, so the animation sits on the button's baseline instead
   of making the button square. */
.lb-console .cc-camp-analysing,
.lb-console .cc-camp-analysing__still { display: none; }
.lb-console .cc-camp-create__go[aria-busy="true"] .cc-camp-analysing {
  display: block;
  width: 48px;
  height: 20px;
  object-fit: cover;
  object-position: center;
}
/* setBusy's own spinner would be a second thing saying the same thing. */
.lb-console .cc-camp-create__go[aria-busy="true"] .cc-spin { display: none; }
.lb-console .cc-camp-analysing__still { gap: 5px; align-items: center; }
.lb-console .cc-camp-analysing__still i {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}

/* An SVG animates itself inside <img> and no rule here can reach in to stop
   it — so reduced motion does not show that file at all. Three still dots
   take its place and the word Analysing never leaves. */
@media (prefers-reduced-motion: reduce) {
  .lb-console .cc-camp-create__go[aria-busy="true"] .cc-camp-analysing {
    display: none;
  }
  .lb-console .cc-camp-create__go[aria-busy="true"] .cc-camp-analysing__still {
    display: inline-flex;
  }
}

/* One column on a phone, and the actions become full-width rows in the order
   they are read. 560px is the same step the Campaign cards reflow at. */
@media (max-width: 560px) {
  .lb-console .cc-camp-create__name { font-size: 19px; max-width: 100%; }
  .lb-console .cc-camp-create__actions { gap: 10px; }
  /* Full width AND 44px tall. The shared buttons are sized for a mouse; a
     31px row is under the minimum target on a phone, and these two are the
     only way out of this card. */
  .lb-console .cc-camp-create__actions > .btn-quiet,
  .lb-console .cc-camp-create__actions > .btn-dark {
    flex: 1 1 100%;
    min-height: 44px;
  }
  /* "Analysing ···" on that full-width row (owner, 3 Oct). The owner's file
     draws its dots between x 83 and 235 of its 320px square, so at the 48px
     it is shown 12.45px is transparent on the left and 12.75px on the right:
     the dots read 20px from the word and the pair sat left of centre. Pulled
     in by that margin,
     the dots are the button's own 8px gap from the word and the pair is
     centred on what can be seen. A desktop keeps its spacing. */
  .lb-console .cc-camp-create__go[aria-busy="true"] .cc-camp-analysing {
    margin-inline: -12px;
  }
  /* A phone stacks the Agent's name, its readiness and Open Agent on ONE
     edge beside the avatar, instead of wrapping the chip back under the
     avatar while the link sits somewhere else. */
  .lb-console .cc-camp-agent {
    display: grid;
    grid-template-columns: 36px minmax(0, 1fr);
    column-gap: 10px;
    row-gap: 6px;
    align-items: center;
  }
  .lb-console .cc-camp-agent__avatar { grid-row: 1 / span 3; align-self: start; }
  .lb-console .cc-camp-agent > .cc-camp-agent__name,
  .lb-console .cc-camp-agent > .stat-badge,
  .lb-console .cc-camp-agent > .cc-camp-agent__link {
    grid-column: 2;
    justify-self: start;
    max-width: 100%;
  }
  .lb-console .cc-camp-agent__link { margin-left: 0; text-align: left; }
  .lb-console .cc-camp-hours { max-width: 100%; }
  /* An edit's lead row stacks (the lead table's own 720px rule); its
     controls then share the row's width, each still a 44px target on a
     touch screen through the console's coarse-pointer rule. */
  .lb-console .cc-camp-edit__acts > .btn-quiet,
  .lb-console .cc-camp-edit__acts > .btn-ghost,
  .lb-console .cc-camp-edit__acts > .btn-danger {
    flex: 1 1 auto;
    justify-content: center;
    min-height: 44px;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   ADD CAMPAIGN — THE CARD'S LOOK (owner's reference, 8 October 2026)

   Presentation only. Not one element, word, control or handler is added,
   removed or moved: these rules restyle what campaignFormHtml already
   draws, and coldcalling.js is not touched by them.

   ── SCOPED TO THE ADD CARD, AND ONLY TO IT ──────────────────────────────
   Every selector below starts at the card that holds the Add Campaign
   button — [data-campaign-create], which campaignCreateFootHtml writes and
   nothing else does. The Edit card is the same .cc-camp-create with the
   same sections, but its foot carries data-campaign-edit-confirm instead,
   so it matches none of this and keeps every rule above exactly as it was.
   So do the Campaign list, the two project pickers (they open in the shared
   drawer, outside the card) and every other screen. A browser without
   :has() draws the card as it was before this block.

   ── WHAT THE REFERENCE ASKS FOR ─────────────────────────────────────────
   One column of label-then-field groups on one rhythm; one voice for every
   label; one height, edge and radius for every control; no rule and no box
   that is not a field; and room around the whole. The values are the
   console's own — --line, --card, --muted, the 0.92rem label, the 0.9rem
   field text — and not the mock's pixels.
   ══════════════════════════════════════════════════════════════════════════ */
.lb-console .cc-camp-create:has([data-campaign-create]) {
  --cc-camp-field-radius: var(--lds-radius-sm, 8px);
  --cc-camp-add-control: 42px;
  --cc-camp-add-gap: 22px;
  /* !important for the reason .cc-camp-section's own padding carries it:
     the console sets every .section-card's padding with !important. */
  padding: 16px 32px 28px !important;
}

/* ── The name ─────────────────────────────────────────────────────────
   Its words start on the edge the labels start on (the field's own 2px
   inset is taken back), and the pencil sits beside them. Both keep their
   44px targets. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__head { margin-bottom: 0; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__name { margin-left: -2px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__pencil { margin-left: -4px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__pencil .cc-i { width: 16px; height: 16px; }
/* The name's ring is black, the ink of this card's labels, not the blue of a
   button's (owner, 8 October 2026). And there is none while the caret is in
   the name only because the card has just opened: campaignNameQuietFocus
   marks that, and unmarks it when the name is pressed, clicked or left. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__name:focus {
  outline-color: var(--lds-color-black, #000000);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__name[data-quiet-focus]:focus { outline: none; }
/* A Duplicate's notice under the name is a notice still, on the fields' radius. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__head + .lb-notice {
  margin-top: 4px;
  border-radius: var(--cc-camp-field-radius);
}

/* ── One rhythm ───────────────────────────────────────────────────────
   The same gap above every group, and the same 6px between a label and its
   field. The goal's Instructions and the project's Notes are groups of
   their own in the reference, so they take the gap too. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__section,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-project-ctx,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__section > .lb-field + .lb-field {
  margin-top: var(--cc-camp-add-gap);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__label,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__head { margin-bottom: 6px; }

/* ── One label voice ──────────────────────────────────────────────────
   Instructions and Notes are labelled in the section headings' own size,
   weight and ink instead of the small grey of a sub-field. What qualifies
   a label — "optional", "required — at least 10 words" — stays grey and
   light beside it. From and Until keep their own, smaller label: they are
   two halves of one control. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__section > .lb-field > .lb-label,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-project-ctx > .lb-field > .lb-label {
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--lds-color-black, #000000);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__section > .lb-field > .lb-label > .lb-hint,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-project-ctx > .lb-field > .lb-label > .lb-hint {
  font-size: inherit;
  font-weight: 400;
  line-height: inherit;
}
/* A badge beside a name or a heading sits on its line, not 7px under it. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent > .stat-badge,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__head > .stat-badge { margin-top: 0; }
/* "Required" beside "Primary Project" is centred on the heading's words
   (owner, 8 October 2026). The heading keeps no margin of its own inside
   this row — the row carries the 6px down to the field — or the row is
   taller than its words and the mark hangs below their middle. And the mark
   is the shared quiet badge, in its own type, drawn a little tighter with
   its word centred in it, so it reads as a note on the heading rather than
   a second control. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__head > .cc-camp-create__label { margin-bottom: 0; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__head > .stat-badge {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 8px;
  line-height: 1;
}

/* ── One control ──────────────────────────────────────────────────────
   The Agent row, the Lead List picker, the goal, the two project rows and
   the two hours are one height with one radius and one size of text, so the
   column reads as one form. --cc-camp-field-radius already reaches the
   Agent row, the picker and the blockers; the shared inputs and the two
   text boxes are given it here. */
.lb-console .cc-camp-create:has([data-campaign-create]) .lb-input,
.lb-console .cc-camp-create:has([data-campaign-create]) .lb-select {
  min-height: var(--cc-camp-add-control);
  padding-top: 0;
  padding-bottom: 0;
  border-radius: var(--cc-camp-field-radius);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-goal__box,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-notes__box,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-order__item {
  border-radius: var(--cc-camp-field-radius);
}
/* The trigger sits inside the picker's own 1px edge. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-picker__trigger {
  min-height: calc(var(--cc-camp-add-control) - 2px);
  padding-top: 0;
  padding-bottom: 0;
  font-size: 0.9rem;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-picker--empty,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__blocker {
  min-height: var(--cc-camp-add-control);
  padding-top: 8px;
  padding-bottom: 8px;
  font-size: 0.9rem;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-picker + .lb-hint,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-order + .lb-hint { margin-top: 6px; }
/* The two fields that open are the same field: one size of chevron, and
   "Choose a goal…" in the grey "Choose a lead list…" is in, until a goal is
   chosen. The goals in the opened list keep the field's ink. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-picker__chevron { width: 16px; height: 16px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .lb-select { background-size: 16px 16px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .lb-select:has(> option[value=""]:checked) { color: var(--muted, #6b7280); }
.lb-console .cc-camp-create:has([data-campaign-create]) .lb-select > option { color: var(--text, #111827); }

/* The Agent: the same row, at the control's height, and the name starts
   where every other field's words start. The mark is not shown on this card
   (owner, 8 October 2026: "remove the black avatar"): it is decoration,
   aria-hidden, and the Agent's name is written in the row. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent {
  min-height: var(--cc-camp-add-control);
  padding: 5px 12px;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent__avatar { display: none; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent__name { font-size: 0.9rem; }
/* The calling order: each chosen list is a row of the same height and text,
   and its two arrows fit inside it. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-order__item {
  min-height: var(--cc-camp-add-control);
  padding: 4px 5px 4px 10px;
  font-size: 0.9rem;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-order__btn { width: 32px; height: 32px; }

/* ── The two project rows ─────────────────────────────────────────────
   The reference draws each as a field under its label. They are still what
   they were — the chosen project with Choose or Change, the count with Add
   projects — in that field's shape: the hairlines that closed each block
   are gone, the mark is a glyph at the start of the row rather than a tile,
   and the row's one button sits inside its trailing edge. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj--primary {
  min-height: var(--cc-camp-add-control);
  padding: 5px 5px 5px 12px;
  gap: 10px;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl {
  padding-bottom: 0;
  border-bottom: 0;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__mark {
  width: 20px;
  height: 20px;
  border-radius: 0;
  background: none;
  color: var(--muted, #6b7280);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__mark .cc-i { width: 18px; height: 18px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__text { font-size: 0.9rem; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__sum { font-size: 0.8rem; }
/* !important where the shared ghost button's padding and size are forced,
   as the rule this refines already says. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__act {
  min-height: 30px;
  border-radius: 6px;
  padding: 4px 12px !important;
  font-size: 13px !important;
}
/* Additional Projects: its heading on a line of its own, as a label, and
   under it one field-shaped row holding the count and Add projects. The
   heading and the count are one stacked block in the markup; `contents`
   lets the row's grid place them, and the row's edge is drawn behind both
   without taking a click. Nothing is reordered. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl > .cc-camp-proj__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px;
  row-gap: 6px;
  margin-bottom: 0;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl .cc-camp-proj__heading { display: contents; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl .cc-camp-proj__title { grid-column: 1 / -1; grid-row: 1; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl .cc-camp-proj__meta {
  grid-column: 1;
  grid-row: 2;
  padding-left: 13px;
  font-size: 0.9rem;
  line-height: 1.3;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl .cc-camp-proj__act {
  grid-column: 2;
  grid-row: 2;
  margin: 6px 6px 6px 0;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl > .cc-camp-proj__head::after {
  content: "";
  grid-column: 1 / -1;
  grid-row: 2;
  align-self: stretch;
  min-height: var(--cc-camp-add-control);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
  pointer-events: none;
}
/* The chosen Additional Projects: one outlined list under that row. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl__list {
  margin-top: 8px;
  padding: 0 6px 0 12px;
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--cc-camp-field-radius);
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-addl__list > .cc-camp-proj { padding: 8px 0; }

/* ── The schedule ─────────────────────────────────────────────────────
   Its one sentence sits directly under its heading, then the days at the
   control's height. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__section--ops > .cc-camp-create__label { margin-bottom: 2px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-days { margin: 12px 0 14px; }
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-day { min-height: var(--cc-camp-add-control); }

/* ── The foot ─────────────────────────────────────────────────────────
   The sentence about the unsaved draft and the reasons under it are quiet
   text, not a box and a rule: the fields are the only outlined things on
   the card, and the two actions close it. */
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__draft {
  margin-top: var(--cc-camp-add-gap);
  padding: 0;
  border: 0;
  border-radius: 0;
  font-size: 0.78rem;
  line-height: 1.5;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__foot {
  margin-top: 12px;
  padding-top: 0;
  border-top: 0;
}
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__blockers,
.lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-create__missing {
  margin-bottom: 14px;
  padding-left: 16px;
  font-size: 0.78rem;
  line-height: 1.55;
}

/* A phone keeps the card's narrower inset, and the Agent row its stack of
   name, readiness and Open Agent — in one column now, with no mark beside
   it to keep a column for. */
@media (max-width: 560px) {
  .lb-console .cc-camp-create:has([data-campaign-create]) {
    --cc-camp-add-gap: 20px;
    padding: 8px 16px 18px !important;
  }
  .lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent {
    grid-template-columns: minmax(0, 1fr);
    padding: 10px 12px;
  }
  .lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-agent > * { grid-column: 1; }
}
/* A finger keeps the 44px every control here gave it. The fields are 46px,
   which is 44px inside their edge; a row's button is drawn a little shorter
   than that and its target fills the row's height above and below it; and
   the calling order's arrows are the 44px squares they were. */
@media (pointer: coarse) {
  .lb-console .cc-camp-create:has([data-campaign-create]) { --cc-camp-add-control: 46px; }
  .lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__act {
    position: relative;
    min-height: 34px;
  }
  .lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-proj__act::after {
    content: "";
    position: absolute;
    inset: -5px 0;
  }
  .lb-console .cc-camp-create:has([data-campaign-create]) .cc-camp-order__btn { width: 44px; height: 44px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   STEP 4 — SIMULATION CALL

   Composition, not a new visual language. The card, the header, the progress
   indicator and the footer are the onboarding shell every other step uses;
   the two buttons are the platform's own .btn-dark and .btn-ghost, whose
   height, radius, padding and typography are set centrally and are NOT
   restated here. What this file adds is the arrangement of one orb, one
   control, one state line and a transcript inside that card.

   ── THE ORB IS A MOUNT, AND THE ARTWORK IS NOT HERE ───────────────────────
   voice-orb.svg is the approved asset. It carries its own gradients, its own
   layer structure and its own ambient animation, and it is inlined into
   #simOrb at runtime so the application can reach the one group inside it that
   playback amplitude moves. None of that is restated below and none of it is
   overridden: this file must not become a second opinion about how the orb
   looks, which is exactly what a stand-in gradient or a local keyframe here
   would be. What these rules own is the box — exactly as wide as it is tall,
   clipped to a circle, and unchanged by any state.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── NOT `.cc-sim` ──────────────────────────────────────────────────────────
   This container was first given the class `.cc-sim`, which already belongs to
   the small grey "Simulation Call" pill on call records (see the rule near the
   top of this file: a 999px radius, a 1px border and a tinted ground). The two
   rules merged, and that pill's outline was stretched around this whole screen
   as one enormous faint oval. The screen's root has its own name so no rule
   written for something else can reach it again. */
.lb-console .cc-simcall {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  padding: 8px 0 4px;
}

/* One custom property drives BOTH dimensions, so the silhouette cannot stop
   being a circle at any width — that is the orb's first invariant, and this
   is where it is kept. The flat ground shows for as long as the asset takes to
   arrive, and is covered by it; it is not a picture of an orb. */
.lb-console .cc-sim__orb {
  --cc-sim-orb: 196px;
  width: var(--cc-sim-orb);
  height: var(--cc-sim-orb);
  flex: 0 0 auto;
  border-radius: 50%;
  overflow: hidden;
  background: var(--line);
  /* ── THE WHOLE ORB BREATHES WITH THE AGENT'S VOICE ──────────────────────
     public/voice-orb.js writes a uniform scale() on THIS element while the
     agent is audibly speaking. A transform does not reflow, so the control
     and the transcript below never move; one scale factor on both axes keeps
     the circle a circle. The artwork inside and its own ambient drift are not
     touched. Promoted to its own layer so the pulse is composited rather than
     repainting the filtered SVG each frame. */
  transform-origin: 50% 50%;
  will-change: transform;
}

.lb-console .cc-sim__orb > svg { display: block; width: 100%; height: 100%; }

.lb-console .cc-sim__controlrow {
  display: flex;
  justify-content: center;
  width: 100%;
}

/* Layout INSIDE the shared button. Its padding, radius and type are the
   platform's .btn-dark and are not restated; this only places the glyph beside
   the label at the standard 8px gap.

   ── A SLIM PILL, AND STILL A FULL-SIZE TARGET ─────────────────────────────
   Member A's reference for this control is a slim black pill: the height comes
   from the label and the button's own padding, not from a forced box. The
   chunkiness was two things added here — a `min-height: 44px`, and a 24px
   glyph box allowed to set the line height. The first is gone; the second is
   handled on the glyph below. The touch area is kept at 44px anyway, by an
   invisible extension above and below that takes pointer input without adding
   a pixel to the pill. */
.lb-console .cc-sim__control {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.lb-console .cc-sim__control::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

/* START and END share this one box, so the two faces are the same pill to the
   pixel: same height, same width of glyph, same gap, same padding.

   The box is the universal 24px icon size. Its drawing does not fill it — the
   waveform's bars run from 4.2 to 19.8 of 24 — so a negative block margin of
   4px lets the 24px box sit inside the label's line instead of pushing the pill
   8px taller around empty space. Nothing is clipped: the bars and the stop
   square are both inside the middle 16px. */
.lb-console .cc-sim__glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  margin-block: -4px;
}
.lb-console .cc-sim__glyph > svg { width: 100%; height: 100%; display: block; }

/* Both faces are in the DOM and one is hidden, so changing face never rebuilds
   the control and never drops focus mid-call. */
.lb-console .cc-sim__control[data-mode="start"] .cc-sim__glyph--stop,
.lb-console .cc-sim__control[data-mode="end"] .cc-sim__glyph--wave { display: none; }

/* The platform's only red, the one .lb-error and .lb-notice--error already
   use. It marks the stop control and nothing else on this screen. */
.lb-console .cc-sim__glyph--stop::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: #b91c1c;
}

/* ── NO VISIBLE STATE BOX ───────────────────────────────────────────────────
   Member A removed the bordered sentence that sat under the control ("Ready.
   Put your earphones on…", "Live. Earphones required…"). The state is still
   said — to assistive technology, through a live region that uses the shared
   .lb-visually-hidden utility and occupies no space — so a screen-reader user
   is still told Connecting, Live, Ending and why a start failed. Nothing about
   it is drawn, so it leaves no box and no gap. */

.lb-console .cc-sim__transcript { width: 100%; max-width: 720px; }

.lb-console .cc-sim__h {
  margin: 0 0 10px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.lb-console .cc-sim__log {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--card);
  padding: 6px 0;
  min-height: 132px;
  max-height: 46vh;
  overflow-y: auto;
}

.lb-console .cc-sim__empty {
  margin: 0;
  padding: 18px 20px;
  color: var(--muted);
  font-size: 0.84rem;
}

.lb-console .cc-sim__line {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 7px 20px;
}

/* A customer still speaking is one line that grows, and it is quieter than a
   line that was actually said. */
.lb-console .cc-sim__line--partial { opacity: 0.6; }

.lb-console .cc-sim__who {
  flex: 0 0 88px;
  font-size: 0.8rem;
  color: var(--muted);
}

/* ── NOT COLOUR ALONE ──────────────────────────────────────────────────────
   The agent's name is blue and the customer's is not, which is the reference's
   own treatment. The weight differs as well, so the two speakers are still
   distinguishable to somebody who cannot tell the colours apart (WCAG 1.4.1).
   The name itself is the real distinction: it is a name, and the other says
   Customer. */
.lb-console .cc-sim__line--agent .cc-sim__who { color: #2563eb; font-weight: 600; }
.lb-console .cc-sim__line--customer .cc-sim__who { color: var(--text); font-weight: 400; }

.lb-console .cc-sim__text {
  flex: 1;
  min-width: 0;
  color: var(--text);
  font-size: 0.88rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Quiet on purpose: the words the agent said are the line, and this is a note
   about how far the customer got through them. */
.lb-console .cc-sim__cut {
  margin-left: 8px;
  font-size: 0.75rem;
  font-style: italic;
  color: var(--muted);
}

.lb-console .cc-sim__actions { display: flex; justify-content: center; }

/* Why Continue can or cannot be pressed, in words rather than behind a
   tooltip, and pointed at by aria-describedby. Continue is in the header's
   corner, so the sentence sits under the call control it is about, centred
   with it and tucked closer than the column's own gap. */
.lb-console .cc-sim__gate {
  margin: -10px 0 0;
  font-size: 0.78rem;
  color: var(--muted);
  text-align: center;
}

/* ── A PHONE ───────────────────────────────────────────────────────────────
   The same hierarchy, not a shrunken copy of the desktop composition. The orb
   scales with the viewport between a floor and a ceiling and stays circular
   because both of its dimensions are still the one property. Back and
   Continue are the shared header row here, like every other setup step. */
@media (max-width: 720px) {
  .lb-console .cc-simcall { gap: 16px; }
  .lb-console .cc-sim__orb { --cc-sim-orb: clamp(132px, 46vw, 196px); }
  .lb-console .cc-sim__control { width: 100%; justify-content: center; }
  .lb-console .cc-sim__who { flex: 0 0 72px; }
  .lb-console .cc-sim__line { padding: 7px 14px; gap: 10px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   NOTIFICATIONS — THE CARD AND ITS ICON
   Last in the sheet on purpose: these come after every per-screen rule that
   places a notice, so the card's left padding (the icon's room) and its ink
   win. The colours and glyphs are the tokens defined beside the toast.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── The card ─────────────────────────────────────────────────────────────
   The Add Campaign card's draft sentence and field-shaped blockers are left
   as they are: the owner made them quiet text and a field on that card
   (8 October 2026), and a boxed notice there would undo it. :where() keeps
   the exclusion from raising this rule's weight above the per-screen rules
   that place a notice. */
.lb-console .lb-notice:not(:where(.cc-camp-create:has([data-campaign-create]) :is(.cc-camp-create__draft, .cc-camp-create__blocker))),
.lb-console .cc-modal__warn {
  position: relative;
  padding-left: 44px;
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: 14px;
  background-color: var(--token-surface-primary, #fff);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 6px 18px -8px rgba(16, 24, 40, 0.12);
  color: var(--text, #111827);
  text-align: left;
  --cc-note-color: var(--cc-note-info);
  --cc-note-glyph: var(--cc-glyph-info);
}
.lb-console .cc-modal__warn { padding: 12px 16px 12px 44px; }

.lb-console .lb-notice:not(:where(.cc-camp-create:has([data-campaign-create]) :is(.cc-camp-create__draft, .cc-camp-create__blocker)))::before,
.lb-console .cc-modal__warn::before,
.lb-console .cc-toast::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 13px;
  width: 18px;
  height: 18px;
  background-color: var(--cc-note-color, var(--cc-note-info));
  -webkit-mask: var(--cc-note-glyph, var(--cc-glyph-info)) center / contain no-repeat;
  mask: var(--cc-note-glyph, var(--cc-glyph-info)) center / contain no-repeat;
}
/* A prose notice opens with a heading on a taller first line. */
.lb-console .cc-v4__boundary::before { top: 20px; }

/* The billing status draws its own grey i-info; the card's icon replaces it. */
.lb-console .lb-notice > .lb-notice__icon { display: none; }

/* An error reads in ink on the white card like every other notice; the red
   is the icon and a faint edge, and the octagon says it without the hue. */
.lb-console .lb-notice--error { border-color: rgba(185, 28, 28, 0.14); }
.lb-console .lb-notice--error strong { color: var(--text, #111827); }

/* ── Which icon ─────────────────────────────────────────────────────────── */
.lb-console .lb-notice--error,
.lb-console .lb-notice[data-notice="error"],
.lb-console .cc-ll-refusal,
.lb-console #leadRemoveState,
.lb-console #campDeleteState,
.lb-console #campCancelState,
.lb-console #campPauseState,
.lb-console #ccProfileErrors,
.lb-console #agentSetupNotice,
.lb-console #setupNotice,
.lb-console .cc-toast:is([data-tone="error"], [data-tone="bad"]) {
  --cc-note-color: var(--cc-note-error);
  --cc-note-glyph: var(--cc-glyph-error);
}
.lb-console #storageNotice,
.lb-console #step3Notice,
.lb-console #step4Notice,
.lb-console #cEditStale,
.lb-console .cc-teach-refused,
.lb-console .cc-attn-blocked,
.lb-console .cc-v4__lock,
.lb-console .cc-camp-create__draft,
.lb-console .cc-camp-create__blocker,
.lb-console .lb-notice[data-ll-unfinished],
.lb-console .lb-notice[data-followup-due],
.lb-console .lb-notice[data-notice="warning"],
.lb-console .cc-modal__warn,
.lb-console .cc-toast[data-tone="warn"] {
  --cc-note-color: var(--cc-note-warning);
  --cc-note-glyph: var(--cc-glyph-warning);
}
.lb-console #staleNotice,
.lb-console .lb-notice[data-biz-reconnect],
.lb-console .lb-notice[data-notice="connection"] {
  --cc-note-color: var(--cc-note-warning);
  --cc-note-glyph: var(--cc-glyph-connection);
}
.lb-console .cc-toast:is([data-tone="ok"], [data-tone="good"]) {
  --cc-note-color: var(--cc-note-success);
  --cc-note-glyph: var(--cc-glyph-success);
}
