/* styles.css — the operator console's own screen layout.
 *
 * WHAT CHANGED, AND WHY
 * This file used to be the console's entire theme: 166 lines, one light-only
 * :root block, no dark mode, no skin engine, and about twenty hardcoded hex
 * values sitting outside the tokens it claimed were "one edit to re-skin".
 * Every tenant admin, meanwhile, runs core/admin/skin/ — glass and editorial,
 * pre-paint attribute stamping, per-member light/dark. An operator tool that
 * looks nothing like the product quietly teaches you the product is something
 * else, so the console now runs the SAME engine, compiled into /skin/ by
 * control-plane/scripts/generate-skin.js.
 *
 * THE STRUCTURE, and the one rule that keeps dark mode free:
 *
 *   1. THE TOKEN BLOCK (everything above END FALLBACK PALETTE). The console
 *      names its own tokens --cp-*, then maps them onto the engine's tokens
 *      once per skin. Because the engine's palettes already carry all three
 *      dark states (bare gated root = light, [data-theme='dark'] = explicit,
 *      and a prefers-color-scheme duplicate guarded with
 *      :not([data-theme='light'])), a mapping written here inherits every one
 *      of them and this file needs no @media query of its own.
 *
 *      The --cp-* names exist rather than using --ink/--muted directly for a
 *      specific reason: those names are generic enough to collide, which is why
 *      core gates them to [data-skin='glass'] and asserts it in a test. Reading
 *      them through a namespaced alias is what lets ONE primitive rule below
 *      serve both skins instead of being written twice.
 *
 *   2. EVERYTHING BELOW is layout and primitives, written ONCE, in tokens only.
 *      No colour literal appears below the fence — functions/test/
 *      skin-parity.test.js fails the build if one does.
 *
 * SURFACES. glass.css owns the frosted recipe (.gl-rail / .gl-panel / .gl-work)
 * and this file never restates it — the shell just puts those classes on the
 * rail and the scrolling body, and editorial.css flattens them again on its
 * side. Cards inside the working surface are drawn with hairlines and spacing,
 * which is the glass idiom (one work surface per screen) and the editorial one
 * (rules and whitespace, not boxes) at the same time.
 */

/* ==========================================================================
   THE TOKEN BLOCK — the only region in this file allowed a colour literal.
   ========================================================================== */

/* FALLBACK PALETTE. skin/theme.js stamps data-skin pre-paint, so in practice
 * one of the two mappings below always wins. This block is what renders if the
 * script is blocked or fails: today's light console, readable, rather than
 * unstyled text. It is deliberately the LOWEST specificity here. */
:root {
  --cp-ink: #16181d;
  --cp-body: #3a3e45;
  --cp-muted: #6b7280;
  --cp-rule: #e7e8ea;
  --cp-rule-strong: #d3d6db;
  --cp-raise: #f7f8f9;
  --cp-ground: #f4f5f7;
  --cp-panel: #ffffff;
  --cp-accent: #4f46e5;
  --cp-on-accent: #ffffff;
  --cp-accent-2: #0f766e;
  --cp-font: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --cp-font-display: var(--cp-font);
  --cp-font-mono: 'SFMono-Regular', ui-monospace, Menlo, monospace;
  --cp-radius: 12px;
  --cp-radius-sm: 8px;
  --cp-pad: 22px;
}

/* THE STATUS TRIO. `ok` and `danger` exist in both engine palettes and are
 * mapped below; `warn` does not exist in either, and the console needs it for
 * every "not wrong, but look at this" state — provisioning, incomplete, stale,
 * a draft invoice. So it is declared here with the engine's own three-state
 * pattern spelled out by hand, exactly as an app is expected to do when it must
 * use a literal: light on the bare root, explicit dark, and a
 * prefers-color-scheme duplicate guarded so an explicit Light still wins. */
:root { --cp-warn: #a4690b; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) { --cp-warn: #e2a33c; }
}
:root[data-theme='dark'] { --cp-warn: #e2a33c; }

/* The modal scrim is a fixed dark wash in BOTH themes: it exists to push the
 * page back, and a scrim derived from the text colour would turn white in dark
 * mode and blind everybody. */
:root { --cp-scrim: rgba(10, 12, 18, 0.55); }

/* ----------------------------------------------------------- glass mapping */
:root[data-skin='glass'] {
  --cp-ink: var(--ink);
  --cp-body: var(--body);
  --cp-muted: var(--muted);
  --cp-rule: var(--hairline);
  --cp-rule-strong: var(--field-border);
  --cp-raise: var(--nav-active);
  --cp-ground: transparent;          /* the blob stack is the ground here */
  --cp-panel: transparent;           /* .gl-work is already the surface */
  --cp-accent: var(--brand-a-fill);
  --cp-on-accent: var(--on-brand);
  --cp-accent-2: var(--brand-b-fill);
  --cp-ok: var(--ok);
  --cp-danger: var(--danger);
  --cp-font: var(--font-chrome);
  --cp-font-display: var(--font-display);
  --cp-font-mono: var(--font-chrome);
  --cp-radius: var(--r-panel);
  --cp-radius-sm: var(--r-field);
  --cp-focus: var(--focus-ring);
}

/* ------------------------------------------------------- editorial mapping */
:root[data-skin='editorial'] {
  --cp-ink: var(--ed-ink);
  --cp-body: var(--ed-body);
  --cp-muted: var(--ed-muted);
  --cp-rule: var(--ed-rule);
  --cp-rule-strong: var(--ed-rule-strong);
  --cp-raise: var(--ed-bg-raise);
  --cp-ground: var(--ed-bg);
  --cp-panel: var(--ed-bg);
  --cp-accent: var(--ed-brand);
  --cp-on-accent: var(--ed-on-brand);
  --cp-accent-2: var(--ed-brand-b);
  --cp-ok: var(--ed-ok);
  --cp-danger: var(--ed-danger);
  --cp-font: var(--ed-font-body);
  --cp-font-display: var(--ed-font-display);
  --cp-font-mono: var(--ed-font-body);
  --cp-radius: var(--ed-r);
  --cp-radius-sm: var(--ed-r);
  --cp-focus: var(--ed-brand);
}

/* Focus and the two status colours need a value under the fallback too. */
:root { --cp-focus: var(--cp-accent); --cp-ok: #17794a; --cp-danger: #c02434; }

/* END FALLBACK PALETTE */

/* ==========================================================================
   From here down: tokens only. One rule per primitive, both skins.
   ========================================================================== */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  font-family: var(--cp-font);
  color: var(--cp-body);
  background: var(--cp-ground);
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------- layout */
/* [data-frame] is the engine's app-frame hook. Under glass it is an inset
   bordered surface with a padded gutter around the rail and the work area;
   under editorial it is the page ground itself. Only the box model is declared
   here — both skins paint it. */
.cp {
  display: flex;
  height: 100%;
  min-height: 0;
}
[data-skin='glass'] .cp {
  padding: var(--pad-frame);
  gap: var(--pad-frame);
}
.cp[data-auth='pending'] { visibility: hidden; }

.cp__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* min-height:0, NOT overflow:hidden — an overflow here crops the shadow off
     every glass panel inside it (glass.css says so at length). */
  min-height: 0;
}
[data-skin='glass'] .cp__main { gap: var(--gap-panel); }

.cp__topbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 4px 14px 6px;
}
[data-skin='editorial'] .cp__topbar {
  padding: 20px 28px 16px;
  border-bottom: 1px solid var(--cp-rule);
}
.cp__topbar h1 {
  margin: 0;
  font-size: 21px;
  font-weight: 700;
  color: var(--cp-ink);
  letter-spacing: -0.2px;
}
[data-skin='editorial'] .cp__topbar h1 { font-size: 26px; }
/* #cp-subtitle, not `.cp__topbar p` — the appearance popover lives in the
   topbar and its explanatory paragraphs were being truncated to one ellipsised
   line by a selector meant for the page subtitle. */
#cp-subtitle {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--cp-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cp__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px;
}
[data-skin='editorial'] .cp__body { padding: 24px 28px 40px; }

/* Rail head / foot content. The rail itself is the engine's — width, motion,
   hover-expand, the collapsed 64px state and the label reveal all come from
   rail.css (glass) and editorial.css (editorial). Only what goes INSIDE it is
   the console's. */
.cp__mark {
  width: 26px;
  height: 26px;
  border-radius: var(--cp-radius-sm);
  flex-shrink: 0;
  background: var(--cp-accent);
  color: var(--cp-on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--cp-font-display);
  font-size: 12px;
  font-weight: 700;
}
.cp__mark--round { border-radius: 50%; font-size: 10px; }
.cp__wordmark b {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--cp-ink);
}
.cp__wordmark span {
  display: block;
  font-size: 9.5px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--cp-muted);
}
.cp__me { line-height: 1.25; min-width: 0; }
.cp__me b { display: block; font-size: 10.5px; font-weight: 700; color: var(--cp-ink); }
.cp__me span { display: block; font-size: 9.5px; color: var(--cp-muted); }

/* ------------------------------------------------------------- appearance */
.cp__appearance { position: relative; flex-shrink: 0; }
.cp__pop {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 400;
  width: 268px;
  padding: 16px 18px;
  border: 1px solid var(--cp-rule-strong);
  border-radius: var(--cp-radius);
  background: var(--cp-panel);
  box-shadow: 0 10px 34px var(--cp-scrim);
}
[data-skin='glass'] .cp__pop { background: var(--glass-work); backdrop-filter: var(--blur-work); -webkit-backdrop-filter: var(--blur-work); }
.cp__pop[hidden] { display: none; }
.cp__pop-sec + .cp__pop-sec { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--cp-rule); }
.cp__skinpick { display: flex; gap: 6px; margin-top: 8px; }
.cp__skinpick .btn { flex: 1; }
.cp__skinpick .btn[aria-pressed='true'] { border-color: var(--cp-accent); color: var(--cp-ink); }
.cp__hintline { margin: 9px 0 0; font-size: 10.5px; line-height: 1.5; color: var(--cp-muted); }

/* ------------------------------------------------------------ primitives */
/* A card is a grouping, not a second frosted pane: the working surface under it
   is already the surface. Under glass that means a hairline and a radius on the
   work pane; under editorial a rule and whitespace. One rule, both skins. */
.card {
  border: 1px solid var(--cp-rule);
  border-radius: var(--cp-radius);
  background: var(--cp-panel);
}
[data-skin='editorial'] .card { border-radius: 0; border-width: 1px 0 0; border-color: var(--cp-rule-strong); background: transparent; }
[data-skin='editorial'] .card:first-child { border-top-width: 0; }
.card--pad { padding: var(--cp-pad) 24px; }
.card__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px;
  padding: 16px 22px;
  border-bottom: 1px solid var(--cp-rule);
}
[data-skin='editorial'] .card__head { padding: 22px 0 12px; border-bottom-color: var(--cp-rule); }
/* A REAL <h2>, not a styled div. Every card head on this console is a section
   heading, and written as divs they left the document with one <h1> and no
   outline underneath it — nothing for a screen reader's heading list, or for
   "next section", to land on. The margin reset is what makes the swap a pure
   semantics change: the UA's 0.83em block margin is the only thing an <h2>
   adds that a <div> did not. The face needs no work either — glass gives
   :is(h1,h2,h3,h4) --font-display and editorial gives them --ed-font-display
   700 in --ed-ink, which is precisely what --cp-font-display / --cp-ink
   already resolve to under each skin, so both engines and this rule agree. */
.card__title { margin: 0; font-family: var(--cp-font-display); font-size: 15px; font-weight: 700; color: var(--cp-ink); }
.card__sub { font-size: 11.5px; color: var(--cp-muted); margin-top: 3px; line-height: 1.5; }

.grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.stack { display: flex; flex-direction: column; gap: 18px; }
.row { display: flex; align-items: center; gap: 10px; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

/* KPI tiles ARE secondary panels in the engine's vocabulary, so they carry
   .gl-panel in the markup and glass.css paints them. Everything here is the
   type, which both skins share. */
.kpi { padding: 16px 18px; border-radius: var(--cp-radius); }
[data-skin='editorial'] .kpi { border: 1px solid var(--cp-rule); background: var(--cp-raise); }
.kpi__label {
  font-family: var(--cp-font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--cp-muted);
}
.kpi__value {
  font-family: var(--cp-font-display);
  font-size: 27px;
  font-weight: 700;
  margin-top: 7px;
  letter-spacing: -0.6px;
  color: var(--cp-ink);
  line-height: 1.1;
}
.kpi__foot { font-size: 11.5px; color: var(--cp-muted); margin-top: 5px; line-height: 1.45; }

/* tables */
.tbl { width: 100%; }
.trow {
  display: grid;
  gap: 12px;
  padding: 12px 22px;
  font-size: 12.5px;
  align-items: center;
  border-top: 1px solid var(--cp-rule);
  color: var(--cp-body);
}
[data-skin='editorial'] .trow { padding: 12px 0; }
.trow:first-child { border-top: none; }
.thead {
  padding: 10px 22px;
  background: var(--cp-raise);
  font-family: var(--cp-font-display);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--cp-muted);
  text-transform: uppercase;
  border-top: none;
}
[data-skin='editorial'] .thead { padding: 10px 0; background: transparent; border-bottom: 1px solid var(--cp-rule-strong); }
.trow--hover:hover { background: var(--cp-raise); }

/* buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: var(--cp-radius-sm);
  padding: 9px 16px;
  font-family: var(--cp-font);
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  border: 1px solid transparent;
  white-space: nowrap;
  color: var(--cp-ink);
  background: transparent;
}
.btn--primary { background: var(--cp-accent); color: var(--cp-on-accent); border-color: transparent; }
.btn--primary:hover { filter: brightness(1.07); }
.btn--ghost { border-color: var(--cp-rule-strong); background: var(--cp-panel); }
[data-skin='glass'] .btn--ghost { background: var(--glass-panel); border-color: var(--glass-edge); }
.btn--ghost:hover { background: var(--cp-raise); }
.btn--danger { color: var(--cp-danger); border-color: var(--cp-rule-strong); background: transparent; }
.btn--danger:hover { background: color-mix(in srgb, var(--cp-danger) 12%, transparent); }
.btn--sm { padding: 6px 11px; font-size: 11.5px; }
.btn[disabled] { opacity: 0.5; cursor: default; }
/* THE CURRENT CHOICE in a two-state switch (Clients → Launch state). Pressed is
   not disabled: a greyed button reads as broken, and on a draft/live control
   "which one is on" is the entire message. Same treatment the skin picker has
   always used, promoted out of its private scope so there is one selected state
   in this console rather than two. Tokens only, so it inherits both skins and
   all three dark states without a second rule. */
.btn[aria-pressed='true'] { border-color: var(--cp-accent); color: var(--cp-ink); }
.btn:focus-visible, .field:focus-visible, [data-rail-item]:focus-visible {
  outline: 2px solid var(--cp-focus);
  outline-offset: 2px;
}

/* badges / pills */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--cp-font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.4px;
  padding: 3px 9px;
  border-radius: 999px;
  text-transform: uppercase;
  /* A status pill that wraps stops looking like a pill: the rounded fill breaks
     across two lines and the row grows. Let the column be narrow instead. */
  white-space: nowrap;
}
.pill--ok { background: color-mix(in srgb, var(--cp-ok) 16%, transparent); color: var(--cp-ok); }
.pill--warn { background: color-mix(in srgb, var(--cp-warn) 18%, transparent); color: var(--cp-warn); }
.pill--danger { background: color-mix(in srgb, var(--cp-danger) 15%, transparent); color: var(--cp-danger); }
.pill--muted { background: var(--cp-raise); color: var(--cp-muted); }
.pill--accent { background: color-mix(in srgb, var(--cp-accent) 14%, transparent); color: var(--cp-accent); }
.pill--dot::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* forms */
.label {
  display: block;
  font-family: var(--cp-font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--cp-muted);
  margin-bottom: 7px;
}
.field {
  width: 100%;
  background: var(--cp-raise);
  border: 1px solid var(--cp-rule-strong);
  border-radius: var(--cp-radius-sm);
  padding: 9px 12px;
  font-family: var(--cp-font);
  font-size: 13px;
  outline: none;
  color: var(--cp-ink);
}
.field:focus { border-color: var(--cp-accent); }
/* The native select arrow, deliberately. The engine sets `color-scheme` per
   skin AND per theme, which is what repaints UA widgets correctly in dark mode
   — a hand-drawn SVG chevron would be a colour literal that never changes. */
select.field { appearance: auto; }

/* toggle switch (entitlement flip) */
.switch { position: relative; display: inline-block; width: 40px; height: 23px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch__track {
  position: absolute;
  inset: 0;
  background: var(--cp-rule-strong);
  border-radius: 999px;
  transition: background 0.15s;
  cursor: pointer;
}
.switch__track::before {
  content: '';
  position: absolute;
  height: 17px;
  width: 17px;
  left: 3px;
  top: 3px;
  background: var(--cp-panel);
  border: 1px solid var(--cp-rule);
  border-radius: 50%;
  transition: transform 0.15s;
}
[data-skin='glass'] .switch__track::before { background: var(--glass-work); }
.switch input:checked + .switch__track { background: var(--cp-accent); }
.switch input:checked + .switch__track::before { transform: translateX(17px); }
.switch input:disabled + .switch__track { opacity: 0.5; cursor: not-allowed; }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--cp-focus); outline-offset: 2px; }

/* meters. The usage view lives on these, so they carry more meaning than a
   deploy progress bar: `over` is a client past the ceiling they are billed on,
   and `unknown` is a client who has not reported — which must never look like
   a client at zero. */
.bar { height: 7px; background: var(--cp-raise); border-radius: 999px; overflow: hidden; border: 1px solid var(--cp-rule); }
.bar > span { display: block; height: 100%; background: var(--cp-accent); border-radius: 999px; }
.bar--warn > span { background: var(--cp-warn); }
.bar--over > span { background: var(--cp-danger); }
.bar--unknown {
  background: repeating-linear-gradient(
    -45deg,
    var(--cp-raise) 0 6px,
    color-mix(in srgb, var(--cp-muted) 22%, transparent) 6px 12px
  );
}

/* misc */
.muted { color: var(--cp-muted); }
.mono { font-family: var(--cp-font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.num { font-variant-numeric: tabular-nums; }
.hint {
  font-size: 12px;
  color: var(--cp-body);
  line-height: 1.6;
  background: var(--cp-raise);
  border: 1px dashed var(--cp-rule-strong);
  border-radius: var(--cp-radius-sm);
  padding: 13px 15px;
}
.hint strong { color: var(--cp-ink); }
.todo {
  border-left: 3px solid var(--cp-warn);
  background: color-mix(in srgb, var(--cp-warn) 12%, transparent);
  border-radius: var(--cp-radius-sm);
  padding: 11px 14px;
  font-size: 12px;
  color: var(--cp-body);
  line-height: 1.55;
}
.todo strong { color: var(--cp-ink); }
.empty { padding: 40px 22px; text-align: center; color: var(--cp-muted); font-size: 13px; }

/* modal */
.modal {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--cp-scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.modal[hidden] { display: none; }
.modal__card {
  background: var(--cp-panel);
  border: 1px solid var(--cp-rule-strong);
  border-radius: var(--cp-radius);
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
}
[data-skin='glass'] .modal__card {
  background: var(--glass-work);
  -webkit-backdrop-filter: var(--blur-work);
  backdrop-filter: var(--blur-work);
  border-color: var(--glass-edge-solid);
  box-shadow: var(--shadow-work);
}
.modal__head {
  padding: 18px 22px;
  border-bottom: 1px solid var(--cp-rule);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.modal__head h3 { margin: 0; font-family: var(--cp-font-display); font-size: 16px; color: var(--cp-ink); }
.modal__body { padding: 20px 22px; display: flex; flex-direction: column; gap: 15px; }
.modal__foot { padding: 15px 22px; border-top: 1px solid var(--cp-rule); display: flex; justify-content: flex-end; gap: 10px; }
.x { background: none; border: none; cursor: pointer; color: var(--cp-muted); font-size: 22px; line-height: 1; padding: 0; }

/* toast (lib/ui.js mounts #cp-toast and adds no inline colours) */
#cp-toast {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 999;
  padding: 11px 18px;
  border-radius: var(--cp-radius-sm);
  border: 1px solid var(--cp-rule-strong);
  border-left: 3px solid var(--cp-accent);
  background: var(--cp-panel);
  color: var(--cp-ink);
  font-family: var(--cp-font);
  font-size: 12.5px;
  font-weight: 700;
  box-shadow: 0 8px 28px var(--cp-scrim);
  opacity: 0;
  transition: opacity 0.15s;
}
[data-skin='glass'] #cp-toast { background: var(--glass-work); -webkit-backdrop-filter: var(--blur-work); backdrop-filter: var(--blur-work); }
#cp-toast[data-kind='err'] { border-left-color: var(--cp-danger); }
#cp-toast[data-open='true'] { opacity: 1; }

.scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll::-webkit-scrollbar-thumb { background: var(--cp-rule-strong); border-radius: 4px; }

@media (max-width: 1180px) {
  .grid4 { grid-template-columns: 1fr 1fr; }
  .grid3, .grid2 { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .cp__topbar p { display: none; }
  .cp__body { padding: 16px; }
}
