/*
 * Design tokens — the v2 redesign (Contact Hero - Redesign.dc.html), light + dark.
 *
 * Owned by Design. `--accent` is per-deployment and is set at runtime by
 * lib/theme.js. Everything else is a literal from the redesign's token sheet
 * (tokens/colors.css in the design project); the NAMES are the console's old
 * ones, so this restyle is a change of values, not a rename across 22 files.
 *
 * The shape of v2, in three rules the values below encode:
 *   1. A tinted canvas (--app-bg) with white cards ON it (--surface). The canvas
 *      does the separating, so cards carry a 1px --hairline and NO shadow; only
 *      things that float (popover, dialog) get one.
 *   2. One accent, used for exactly three things: the primary button, the active
 *      nav item / tab underline, and selection. Ink on white is the darker
 *      --accent-text. Status is a tint with a darker ink on top, never white on
 *      a hue — and amber / lavender are FILLS ONLY, they never carry text.
 *   3. Weight carries the type hierarchy, not size: 800 titles, 700 emphasis,
 *      600 row titles and controls, 500 body and meta. Mono is for identifiers
 *      only — references, numbers, keys.
 *
 * Rule: components reference variables, never literal colours, so a token change
 * lands everywhere at once.
 */

/*
 * The v2 redesign's two typefaces, VENDORED — Manrope and DM Sans.
 *
 * The design mock loads them from fonts.googleapis.com. The console may not:
 * it has to render on the LAN with the internet down, which is why nothing here
 * loads from a CDN. So the woff2 files live in `ui/vendor/fonts/` and are
 * declared below, and the console gets the mock's exact faces offline.
 *
 * Both are VARIABLE fonts — one file spans every weight, so Manrope's 800
 * headings in the mock are a real 800 rather than a synthesised bold. Only the
 * latin and latin-ext subsets are carried; the console is English, and the
 * Cyrillic, Greek and Vietnamese subsets would be weight it never renders.
 *
 * APPLIED since the v2 restyle's first stage — `--sans` below is Manrope. The
 * faces were vendored and declared a day ahead of that, deliberately unapplied,
 * so the console could not half-change between the two commits.
 */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('../vendor/fonts/manrope-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('../vendor/fonts/manrope-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../vendor/fonts/dmsans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../vendor/fonts/dmsans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root,
:root[data-theme='light'] {
  --app-bg: #f5f5fa;
  --surface: #ffffff;
  --surface-alt: #fafafd;
  --surface-sunken: #f7f7fb;

  --hairline: #e7e6f0;
  --hairline-soft: #f0eff6;

  --text: #17161f;
  --text-2: #5c5a6e;
  --text-3: #8a879c;
  --text-muted: #7a778f;

  /* The painted value, not just the declared one: lib/theme.js sets --accent-text
     from the active swatch on every load, so this literal must be the default
     accent's (Lavender) light text variant or the two disagree. #184. */
  --accent-text: #4a3fc0;
  --accent-tint: #ebe9fa;
  --accent-tint-soft: #f5f4fc;
  --badge-tint: #ebe9fa;

  --sel-bg: #f5f4fc;
  --hover-bg: rgba(23, 22, 31, 0.03);

  /* v2: the primary button IS the accent (the old console used ink). */
  --btn-bg: var(--accent);
  --btn-fg: #ffffff;

  --avatar-bg: #17161f;
  --avatar-fg: #ffffff;
  --avatar-agent-bg: var(--accent);

  --warn: #d69a3a;
  --warn-fg: #8a5a12;
  --warn-bg: #fdf1df;
  --fail: #d9655a;
  --fail-fg: #a8351f;
  --danger-tint: #fbe9e7;
  /* The solid destructive button. `--fail` is a TEXT colour and reads as one on
     both themes; a filled button needs a background that white sits on, which in
     dark mode `--fail`'s salmon does not (card #387). */
  --danger-bg: #a8351f;
  --danger-fg: #ffffff;
  --ok: #1f7a55;
  --ok-bg: #e6f4ee;

  --field: #ffffff;
  --field-sunken: #fafafd;
  --field-border: #e7e6f0;
  --field-hover: #c9c6d8;
  --field-focus: var(--accent);
  --field-shadow: none;

  --pill-bg: #f0eff6;
  --pill-fg: #5c5a6e;
  /* One channel chip has to work on any bubble band, so its ground is
     translucent white here — see the dark block for why that inverts. */
  --chip-band: rgba(255, 255, 255, 0.7);
  --bubble-band-agent: #e3e0f8;
  --bubble-band-person: #f0eff6;
  --bubble-body-agent: #ebe9fa;
  --bubble-body-person: #f7f7fb;

  /* Cards are bordered, not shadowed. --shadow is kept for the overlay pane. */
  --shadow: -18px 0 40px rgba(23, 22, 31, 0.10);
  --shadow-raised: 0 1px 2px rgba(23, 22, 31, 0.08);
  --shadow-popover: 0 12px 32px rgba(23, 22, 31, 0.14);
  --shadow-modal: 0 24px 64px rgba(23, 22, 31, 0.28);
  /* THE FOLD (card #506). Content scrolling under an opaque strip — a composer,
     a confirm button, a dialog header — reads as CUT unless the strip says the
     page carries on beneath it. Two forms of the one cue: cast UP onto the strip
     below a pane, and inset at a pane's own top edge where the header is not
     ours to shadow. Only drawn while there is something hidden that way. */
  --shadow-fold: 0 -10px 18px -14px rgba(23, 22, 31, 0.5);
  --shadow-fold-top: inset 0 10px 18px -14px rgba(23, 22, 31, 0.5);
  --scrim: rgba(23, 22, 31, 0.34);

  /* chart + surface extras */
  --bars-alt: #c9c4f0;
  --line-alt: #8a879c;
  --line-email: #4f8fd6;
  --area-fill: rgba(91, 79, 208, 0.13);
  --tip-bg: #17161f;
  --tip-fg: #ffffff;
}

/* Dark — derived from light and MEASURED: every ink clears 5:1 on every ground
   it sits on (worst 5.21:1, white on the accent fill). Three rules that came out
   of deriving it: the canvas stays DARKER than the cards, or "cards on a tint"
   flattens; the accent is two values here because white on it is every primary
   button and every agent avatar; chips invert their construction — deep ground,
   light ink — and the translucent chip band becomes a solid. */
:root[data-theme='dark'] {
  --app-bg: #14131b;
  --surface: #1b1a24;
  --surface-alt: #201f2a;
  --surface-sunken: #191823;

  --hairline: #2f2d3d;
  --hairline-soft: #262533;

  --text: #ece9f5;
  --text-2: #b3afc4;
  --text-3: #a5a1b8;
  --text-muted: #9d99b0;

  --accent-text: #a9a1f7;
  --accent-tint: #2a2740;
  --accent-tint-soft: #232038;
  --badge-tint: #2a2740;

  --sel-bg: #232038;
  --hover-bg: rgba(255, 255, 255, 0.04);

  --btn-bg: var(--accent);
  --btn-fg: #ffffff;

  /* Near-black is the light theme's avatar fill AND its ink; inverted it can
     only be one, so the fill becomes a neutral tile white sits on (8.6:1). */
  --avatar-bg: #3a3850;
  --avatar-fg: #ffffff;
  --avatar-agent-bg: var(--accent);

  --warn: #e0a24a;
  --warn-fg: #f0c184;
  --warn-bg: #3a2d18;
  --fail: #f0907c;
  --fail-fg: #f0907c;
  --danger-tint: #36201d;
  --danger-bg: #f0907c;
  --danger-fg: #1b1a24;
  --ok: #4fd0a8;
  --ok-bg: #15302a;

  --field: #201f2a;
  --field-sunken: #191823;
  --field-border: #2f2d3d;
  --field-hover: #454259;
  --field-shadow: none;

  --pill-bg: #262533;
  --pill-fg: #b3afc4;
  --chip-band: #1f1d2e;
  --bubble-band-agent: #302c4a;
  --bubble-band-person: #242332;
  --bubble-body-agent: #2a2740;
  --bubble-body-person: #191823;

  /* shadows go black, not tinted — a violet shadow on near-black is a smudge */
  --shadow: -18px 0 40px rgba(0, 0, 0, 0.45);
  --shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-popover: 0 12px 32px rgba(0, 0, 0, 0.5);
  --shadow-modal: 0 24px 64px rgba(0, 0, 0, 0.55);
  /* Deeper here for the same reason the others are: a 0.5 tinted shadow on a
     near-black strip is invisible, and an invisible cue is not a cue. */
  --shadow-fold: 0 -10px 18px -13px rgba(0, 0, 0, 0.85);
  --shadow-fold-top: inset 0 10px 18px -13px rgba(0, 0, 0, 0.85);
  --scrim: rgba(0, 0, 0, 0.6);

  --bars-alt: #5b529c;
  --line-alt: #a5a1b8;
  --line-email: #6fa6e4;
  --area-fill: rgba(123, 111, 240, 0.16);
  --tip-bg: #ece9f5;
  --tip-fg: #14131b;
}

/* The accent. v2's default is Lavender; lib/theme.js overrides it per device.
   There is no top bar in v2 — navigation lives in the sidebar — so the --bar-*
   tokens are gone. login.jsx and the boot splash read --accent directly. */
:root {
  --accent: #5b4fd0;
  --accent-edge: var(--accent);
}

/* Type — Manrope, APPLIED (the faces above were declared on 2026-09-11; this is
   the switch). Weight carries the hierarchy. Mono only for identifiers. The old
   --t-* names keep their meaning; the new ones are the redesign's additions. */
:root {
  --sans: 'Manrope', system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  --t-page: 800 20px/1 var(--sans);
  --t-record: 800 18px/1 var(--sans);
  --t-stat: 800 22px/1 var(--sans);
  --t-group: 800 14px/1.2 var(--sans);
  --t-title: 700 17px/1.35 var(--sans);
  --t-row-title: 600 14px/1.4 var(--sans);
  --t-callout: 500 14.5px/1.5 var(--sans);
  --t-body: 500 13.5px/1.5 var(--sans);
  --t-control: 600 13.5px/1 var(--sans);
  --t-secondary: 500 13px/1.4 var(--sans);
  --t-meta: 500 12.5px/1.4 var(--sans);
  --t-chip: 700 11px/1 var(--sans);
  --t-section: 700 10.5px/1 var(--sans);
  --t-field-label: 700 10.5px/1 var(--sans);
  --t-mono: 500 13px/1 var(--mono);
  --t-mono-sm: 500 12.5px/1 var(--mono);
  --tracking-tight: -0.02em;
  --tracking-label: 0.1em;

  --r-chip: 6px;
  --r-pill-sm: 7px;
  --r-input: 9px;
  --r-control: 10px;
  --r-tile: 11px;
  --r-card: 12px;
  --r-pane: 14px;
  --r-modal: 14px;
  --r-pill: 999px;

  --rail-full: 224px;
  --rail-collapsed: 64px;
  --gap-pane: 14px;
  --control-h: 38px;
  --control-h-sm: 34px;
  --control-h-xs: 30px;
}

body { font-variant-numeric: tabular-nums; }

.section-label {
  font: var(--t-section);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-3);
}

@keyframes dcRise {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
@keyframes dcFade { from { opacity: 0 } to { opacity: 1 } }
@keyframes dcPulse {
  0%, 100% { opacity: .25 }
  50%      { opacity: 1 }
}
/* The live-agent waveform. The most visible thing on the screen, so it only
   ever animates for a job whose status is genuinely `talking`. */
@keyframes dcWave {
  0%, 100% { transform: scaleY(.38) }
  50%      { transform: scaleY(1) }
}
/* `transform-box: fill-box` keeps each bar scaling about its OWN centre: in SVG
   `transform-origin: center` otherwise resolves against the nearest viewport.
   With today's geometry that changes nothing — all four bars are centred on
   y=12, which is the viewport centre too, so scaleY pivots on the same line
   either way (measured, 0px difference; the audit's "the outer bars slide"
   is retracted). Move a bar off-centre and it starts to matter, which is the
   reason to keep it. The prototype has it; this had lost it.
   Proof and how to re-run it: tests/waveform/README.md. */
.dc-wave rect { transform-box: fill-box; transform-origin: center; animation: dcWave 1.05s ease-in-out infinite; }
.dc-wave rect:nth-child(2) { animation-delay: .13s }
.dc-wave rect:nth-child(3) { animation-delay: .27s }
.dc-wave rect:nth-child(4) { animation-delay: .4s }

/* An infinite loop is exactly what this setting exists for. */
@media (prefers-reduced-motion: reduce) {
  .dc-wave rect { animation: none }
}

input, button, textarea, select {
  font-family: inherit;
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
}
input::placeholder { color: var(--text-3); opacity: 1 }

/* Buttons — v2. One class, and the label names the ACTION ("Pause", "Delete…",
   "Call Helen now"), never the mechanism. 34px tall in a record header, 38px in
   a toolbar (.ch-btn-lg), 30px in a section header (.ch-btn-xs). The primary is
   the accent with white on it; the secondary is a bordered field. */
.ch-btn {
  height: var(--control-h-sm); display: inline-flex; align-items: center; gap: 8px;
  font: var(--t-control); padding: 0 12px; border: 1px solid var(--field-border);
  border-radius: var(--r-control); background: var(--field); color: var(--text);
  cursor: pointer; white-space: nowrap;
}
.ch-btn:hover { border-color: var(--field-hover) }
.ch-btn-lg { height: var(--control-h); padding: 0 14px }
.ch-btn-xs { height: var(--control-h-xs); padding: 0 11px; font: var(--t-meta); font-weight: 700; border-radius: var(--r-chip) }
.ch-btn-primary {
  background: var(--btn-bg); color: var(--btn-fg); border-color: var(--btn-bg);
  font-weight: 700;
}
.ch-btn-primary:hover { border-color: var(--btn-bg); filter: brightness(1.06) }
.ch-btn-quiet { background: var(--accent-tint); color: var(--accent-text); border-color: transparent; font-weight: 700 }
.ch-btn-ghost { background: transparent; border-color: transparent; color: var(--text-2) }
.ch-btn:disabled { opacity: .55; cursor: default }
/* Destructive. The outline form sits in a record header and says what it does
   in its colour rather than shouting; the filled form is a dialog's confirm,
   where it IS the decision being made. */
.ch-btn-danger { color: var(--fail-fg) }
.ch-btn-danger:hover { border-color: var(--fail) }
.ch-btn-danger-primary {
  background: var(--danger-bg); color: var(--danger-fg); border-color: var(--danger-bg);
}
.ch-btn-danger-primary:hover { border-color: var(--danger-bg) }
/* A square glyph-only control. Its title is its only label. */
.ch-icon-btn {
  width: var(--control-h-sm); height: var(--control-h-sm); flex: none; display: grid; place-items: center;
  border: 1px solid var(--field-border); border-radius: var(--r-input); background: transparent;
  color: var(--text-2); cursor: pointer;
}
.ch-icon-btn:hover { border-color: var(--field-hover); color: var(--text) }
.ch-icon-btn-bare { border-color: transparent }

/* Dense clickable list row — the jobs list positions its own fields, but every
   other list on the console (recent jobs, attention) is a plain grid row. */
.ch-row:hover { background: var(--hover-bg) }

/* Sidebar nav item (v2 — there is no top bar). */
.ch-nav { border-radius: var(--r-input) }
.ch-nav:hover { background: var(--hover-bg) }

/* The mock-data warning, on a surface. One class now: the top bar it used to sit
   in is gone, and the sidebar footer and the login card are both light surfaces. */
.ch-badge-mock,
.ch-badge-mock-surface {
  font: var(--t-field-label); letter-spacing: var(--tracking-label); padding: 4px 7px;
  border-radius: var(--r-chip); background: var(--warn-bg); color: var(--warn-fg);
  text-transform: uppercase; white-space: nowrap;
}

/* Scrim, border and shadow all taken from the deck's dialog (#165): it draws
   `rgba(8,14,16,.34)` over the page, a 1px hairline on the dialog itself and a
   `0 24px 64px rgba(8,14,16,.34)` drop. Ours were `rgba(0,0,0,.42)`, no border
   and `0 18px 50px rgba(0,0,0,.28)`. The border is not only cosmetic — with
   box-sizing: border-box it takes the dialog's inner width from 560 to 558,
   which is what lands the New customer CTA on the deck's 530px. */
/* A DIALOG IS ABOVE THE PAGE, INCLUDING THE PAGE'S OWN RAISED BITS (card #506).
   Both are portalled to the end of <body> and were left at `z-index: auto`,
   which puts them BELOW anything on the page carrying a positive z-index —
   painting the draggable divider's grip straight through an open dialog, a 4px
   bar floating in the middle of the conversation. 60 clears everything the app
   raises (the highest is a popover at 10) and Dialog.Portal is the only portal
   in the console, so nothing else can be trapped underneath it. */
.ch-overlay { position: fixed; inset: 0; z-index: 60; background: var(--scrim); animation: dcFade .16s ease }
/* Centred WITHOUT transform: inset+margin:auto, so dcRise can animate transform
   freely. Centring via translate(-50%,-50%) would be replaced by the
   animation's own transform for its whole duration, and the dialog would fly in
   from the viewport centre — least acceptable on the one surface that rings a
   real phone. */
.ch-modal {
  /* GUTTER (#277). The deck's overlay is a flex centre with `padding: 24px`,
     so its gutter is a box the dialog lives inside and holds at every width.
     Radix renders Content BESIDE Overlay, not inside it, so padding on the
     overlay cannot reach the dialog; the equivalent that does is a max-width
     measured from the viewport — `calc(100% - 48px)`, 100% being this fixed
     box's containing block, i.e. the viewport. What it replaces was a CONSTANT
     (1440 - 48), which is the right number at one viewport and does nothing
     once the screen is narrow. Insetting the box (`inset: 24px`) looks like the
     same idea and is not: percentages still resolve against the full viewport,
     so the dialog runs 24px past the right edge.
     No min-width, for the same reason — the deck has no floor, and a 380px one
     put the dialog off the right of a 375px screen. */
  position: fixed; inset: 0; margin: auto; z-index: 61;
  width: fit-content; height: fit-content;
  background: var(--surface); color: var(--text); padding: 22px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-modal); box-shadow: var(--shadow-modal);
  max-width: min(calc(100% - 48px), 480px);
  /* overflow + the animation's duration and fill-mode are the deck's (#165 a10,
     rendered at 1440x900): every deck dialog is
     `overflow:hidden;animation:dcRise .28s cubic-bezier(.2,.7,.2,1) both`.
     overflow:hidden is what clips a header row's square corners to the 8px
     radius; ours was visible, and the animation ran .28s short with no
     fill-mode. */
  overflow: hidden;
  animation: dcRise .28s cubic-bezier(.2,.7,.2,1) both;
}

/* v2 surfaces. A pane is a white card on the canvas: hairline, 14px, no shadow.
   A popover floats, so it is the one thing on the page with a real shadow. */
.ch-pane {
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-pane);
  display: flex; flex-direction: column; overflow: hidden; min-height: 0;
}
.ch-popover {
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--r-card);
  box-shadow: var(--shadow-popover); padding: 6px; display: flex; flex-direction: column; gap: 1px;
}
.ch-menu-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: var(--r-pill-sm);
  font: var(--t-secondary); font-weight: 600; color: var(--text); cursor: pointer; white-space: nowrap;
}
.ch-menu-item:hover { background: var(--hover-bg) }
.ch-menu-item[aria-disabled='true'] { color: var(--text-3); cursor: default }

/* Visually hidden, still read aloud. Radix asks every dialog for a description;
   the New customer dialog has no visible subtitle (the prototype has none), and
   dropping the description rather than hiding it would trade a cosmetic match
   for a real accessibility regression. */
.ch-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
