/* Valken Operator Console — design tokens, layout, and components.
   Plain CSS, no build step, no preprocessor: every class here is
   referenced directly from console/web/*.js. Light is the default; dark
   redefines the same token names under prefers-color-scheme, so nothing in
   the component rules below ever needs to know which mode is active. */

:root {
  color-scheme: light dark;

  --bg: #f6f4ee;
  --surface: #ffffff;
  --surface-sunken: #efece2;
  --border: #dcd6c7;
  --border-strong: #b9b09a;

  --text: #201d17;
  --text-muted: #5c5748;
  --text-faint: #8a8371;

  --accent: #1f6f5c;
  --accent-strong: #14493d;
  --accent-soft: #e1efe9;
  --accent-contrast: #ffffff;

  --positive: #1f6f5c;
  --negative: #a4302b;
  --negative-soft: #f6e3e0;

  /* Staleness on the Status page is deliberately its own colour, distinct
     from --negative/--negative-soft: a stale platform is not necessarily a
     failing one (see status.js's renderPlatformBanners — a reachable
     platform can still be showing stale checks, and a failing one gets its
     own --negative banner already). Collapsing the two onto the same red
     would make "something is broken" and "this might be out of date" read
     as the same severity, which they aren't. */
  --warning: #96650f;
  --warning-soft: #f5e9d3;

  /* Chart categorical series (Revenue / Cost) — the dataviz skill's default
     validated palette, slots 1 & 2 (blue, orange): CVD/contrast/lightness
     checked against this file's own --surface in both modes (see
     .superpowers/sdd/billing-charts.md), not eyeballed. Kept separate from
     --positive/--negative on purpose — revenue and cost are two categories,
     not a good/bad status pair, so they don't borrow the status tokens. */
  --chart-series-1: #2a78d6;
  --chart-series-2: #eb6834;

  --focus-ring: #1f6f5c;

  --font-sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  --radius: 3px;
  --shadow-panel: 0 8px 24px rgba(20, 18, 12, 0.18);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #17150f;
    --surface: #201d16;
    --surface-sunken: #14120d;
    --border: #3a3325;
    --border-strong: #554a35;

    --text: #f2ede1;
    --text-muted: #b6ac96;
    --text-faint: #85795e;

    --accent: #57bb9d;
    --accent-strong: #7ed1b6;
    --accent-soft: #1f342d;
    --accent-contrast: #0d1512;

    --positive: #57bb9d;
    --negative: #e07567;
    --negative-soft: #3a211c;

    --warning: #e0ac4a;
    --warning-soft: #3a2c14;

    --chart-series-1: #3987e5;
    --chart-series-2: #d95926;

    --focus-ring: #57bb9d;

    --shadow-panel: 0 12px 32px rgba(0, 0, 0, 0.5);
  }
}

/* ---- Reset & base ---------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
ul,
figure {
  margin: 0;
}

ul {
  list-style: none;
  padding: 0;
}

a {
  color: inherit;
}

input,
select,
button,
textarea {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.text-muted {
  color: var(--text-muted);
}

/* ---- App shell --------------------------------------------------------- */

#app {
  min-height: 100vh;
}

.shell {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

/* Deliberately NOT justify-content: space-between. The sidebar is full
   viewport height, so space-between spreads its three children — brand, nav
   list, footer — across the whole column, parking the nav links at the
   vertical centre of the window. On a tall display that puts them hundreds
   of pixels below the brand with nothing in between, which reads as "this
   console has no navigation" even though the links are in the DOM. This was
   reported as "the side tabs are gone" when all five links were present and
   visible, just stranded mid-viewport. Links belong directly under the
   brand; only the footer gets pushed down, by its own margin-top: auto. */
.sidebar {
  flex: 0 0 232px;
  display: flex;
  flex-direction: column;
  padding: 28px 20px;
  background: var(--surface);
  border-right: 1px solid var(--border);
}

.view {
  flex: 1 1 auto;
  min-width: 0;
  padding: 40px clamp(24px, 4vw, 56px) 64px;
}

.brand {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 32px;
}

.brand-mark {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.brand-sub {
  font-size: 13px;
  color: var(--text-muted);
}

.nav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-link {
  display: block;
  padding: 9px 12px;
  border-radius: var(--radius);
  border-left: 2px solid transparent;
  text-decoration: none;
  font-size: 14px;
  color: var(--text-muted);
}

.nav-link:hover {
  color: var(--text);
  background: var(--surface-sunken);
}

.nav-link.active {
  color: var(--text);
  border-left-color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

.nav-footer {
  /* Takes the leftover column space itself, so the footer sits at the bottom
     while the nav links stay tucked under the brand. */
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.nav-user {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
}

.nav-user-email {
  color: var(--text);
  overflow-wrap: anywhere;
}

.nav-user-role {
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 11px;
}

/* ---- Login -------------------------------------------------------------- */

.login-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.login-card {
  width: min(380px, 100%);
  padding: 36px 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.login-card .brand {
  margin-bottom: 28px;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 20px;
}

/* ---- Forms & fields ------------------------------------------------------ */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
}

.field-inline {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.field-label {
  color: var(--text-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
input[type='date'],
select {
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.checkbox-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.checkbox-row {
  display: flex;
  gap: 16px;
}

.create-form,
.range-form,
.inline-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px;
}

.create-form {
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-width: 640px;
}

.inline-form {
  align-items: center;
}

.range-form {
  margin-top: 12px;
}

.range-controls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.preset-group {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.range-utc-notice {
  font-size: 12px;
  margin: 0;
}

.field-hint {
  font-size: 11px;
  color: var(--text-faint);
  white-space: nowrap;
}

.covered-period {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--text-muted);
}

/* ---- Rate card ---------------------------------------------------------------- */

.rate-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 20px;
  margin: 16px 0 32px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.rate-summary-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.rate-summary-item dt {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
}

.rate-summary-item dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 600;
}

.rate-summary-breakeven dd {
  color: var(--accent);
}

.rate-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 640px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.rate-group {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin: 0;
}

.rate-group legend {
  padding: 0 6px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.rate-group-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* ---- Buttons -------------------------------------------------------------- */

.btn {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  padding: 8px 16px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
}

.btn:hover {
  background: var(--surface-sunken);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

.btn-primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

.btn-secondary {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-strong);
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-muted);
}

.btn-ghost:hover {
  background: var(--surface-sunken);
  color: var(--text);
}

.btn-small {
  padding: 4px 10px;
  font-size: 12px;
}

/* ---- Banners -------------------------------------------------------------- */

.banner {
  padding: 12px 16px;
  border-radius: var(--radius);
  font-size: 13px;
  margin: 16px 0;
  border: 1px solid transparent;
}

.banner-error {
  background: var(--negative-soft);
  border-color: var(--negative);
  color: var(--negative);
}

.banner-notice {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-strong);
}

.banner-warning {
  background: var(--warning-soft);
  border-color: var(--warning);
  color: var(--warning);
}

/* ---- View header & sections ------------------------------------------------ */

.view-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.view-header h1 {
  font-size: 22px;
  font-weight: 700;
}

.section-title {
  margin-top: 40px;
  margin-bottom: 12px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  border-bottom: 1px solid var(--border);
  padding-bottom: 8px;
}

/* ---- Billing headline -------------------------------------------------------- */

.headline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 40px;
  margin-top: 24px;
  padding: 28px 0;
  border-bottom: 1px solid var(--border);
}

.headline-primary,
.headline-secondary {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.headline-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.headline-value {
  font-family: var(--font-mono);
  font-size: 44px;
  font-weight: 600;
  line-height: 1;
}

.headline-value-small {
  font-size: 28px;
}

.headline-context {
  font-size: 12px;
  color: var(--text-faint);
}

.figure-positive .headline-value {
  color: var(--positive);
}

.figure-negative .headline-value {
  color: var(--negative);
}

.distribution {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 18px;
  padding: 10px 0 24px;
  font-size: 13px;
  color: var(--text-muted);
}

.distribution-label {
  font-style: italic;
}

.distribution-stat {
  font-family: var(--font-mono);
}

.footnotes {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 13px;
}

.error-codes ul {
  margin-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-family: var(--font-mono);
  font-size: 12px;
}

/* ---- Tables ------------------------------------------------------------------ */

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.data-table th,
.data-table td {
  padding: 10px 14px;
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}

.data-table th {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  background: var(--surface-sunken);
}

.data-table tr:last-child td {
  border-bottom: none;
}

.data-table td.num,
.data-table th.num {
  text-align: right;
  font-family: var(--font-mono);
}

.data-table .empty-row {
  text-align: center;
  color: var(--text-faint);
  white-space: normal;
}

.mono-small {
  font-family: var(--font-mono);
  font-size: 12px;
}

.key-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.key-list li {
  display: flex;
  align-items: center;
  gap: 8px;
}

.status-active,
.status-disabled {
  display: flex;
  align-items: center;
  gap: 10px;
}

.status-disabled {
  color: var(--negative);
}

/* ---- One-time key panel -------------------------------------------------------- */

.key-panel-host {
  position: relative;
}

.key-panel {
  margin: 16px 0 28px;
  padding: 20px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-soft);
  box-shadow: var(--shadow-panel);
}

.key-panel-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
}

.key-panel-context {
  color: var(--text-muted);
  font-size: 12px;
}

.key-panel-warning {
  font-size: 13px;
  color: var(--accent-strong);
  margin-bottom: 14px;
}

.key-panel-value-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.key-panel-value {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 13px;
}

.key-panel-dismiss {
  display: block;
}

/* ---- Billing filters (partner + granularity) ------------------------------------ */

.filter-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px;
  margin-top: 12px;
}

/* ---- Billing charts --------------------------------------------------------------
   Marks, spacers, and interaction states follow the dataviz skill's specs
   (2px lines, >=8px markers with a 2px surface ring, hairline solid
   gridlines, a dashed threshold line, no color-only encoding). Every color
   here is a var(--…) token, resolved by the browser the same way as any
   other themed element, so light/dark need no JS branching. */

.chart-section {
  margin-top: 20px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.chart-section-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}

.chart-section-title {
  font-size: 14px;
  font-weight: 600;
}

.chart-section-note {
  font-size: 12px;
  color: var(--text-faint);
}

.chart-table {
  margin-top: 16px;
}

.chart-scroll {
  overflow-x: auto;
}

.chart-svg {
  display: block;
}

.chart-gridline {
  stroke: var(--border);
  stroke-width: 1;
}

.chart-reference-line {
  stroke: var(--text-faint);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
}

.chart-reference-label {
  fill: var(--text-muted);
  font-size: 11px;
}

/* The "above break-even" risk zone — a filled wash, not a saturated block,
   so the risk region reads from its own presence/absence (geometry) as
   much as from color; still fully legible with color removed. */
.chart-risk-zone {
  fill: var(--negative-soft);
}

.chart-line {
  stroke-width: 2;
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.chart-marker {
  stroke: var(--surface);
  stroke-width: 2;
}

.chart-tick-label,
.chart-x-label {
  font-family: var(--font-mono);
  font-size: 11px;
  fill: var(--text-faint);
}

.chart-crosshair {
  stroke: var(--border-strong);
  stroke-width: 1;
  pointer-events: none;
}

.chart-hit-column {
  fill: transparent;
  cursor: crosshair;
}

.chart-hit-column:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}

.chart-tooltip-group {
  pointer-events: none;
}

.chart-tooltip-bg {
  fill: var(--surface);
  stroke: var(--border-strong);
  stroke-width: 1;
}

.chart-tooltip-title {
  fill: var(--text);
  font-size: 12px;
  font-weight: 600;
}

.chart-tooltip-label {
  fill: var(--text-muted);
  font-size: 11px;
}

.chart-tooltip-value {
  fill: var(--text);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
}

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-muted);
}

.chart-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.chart-legend-swatch {
  display: inline-block;
  width: 16px;
  height: 0;
  border-top: 2px solid;
}

.chart-legend-swatch-dashed {
  border-top-style: dashed;
}

/* One class per color role a legend swatch can show — charts.js picks
   among these instead of an inline style="border-top-color: …", which the
   console's CSP (no style-src, falling back to default-src 'self') blocks
   on every element, not just <script>/<style>. Keeps this the same fixed
   4-role palette the charts themselves use (see charts.js's
   SWATCH_ROLE_CLASS). */
.chart-legend-swatch-accent {
  border-top-color: var(--accent);
}

.chart-legend-swatch-series-1 {
  border-top-color: var(--chart-series-1);
}

.chart-legend-swatch-series-2 {
  border-top-color: var(--chart-series-2);
}

.chart-legend-swatch-reference {
  border-top-color: var(--text-faint);
}

/* ---- Status page ------------------------------------------------------------------ */

.status-controls {
  margin: 0;
}

.status-last-checked {
  margin: 8px 0 0;
  font-size: 12px;
}

.platform-banners {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Overrides .banner's own margin: platform-banners already supplies the
   gap between stacked banners, and the section itself sits between
   status-last-checked and the table with its own spacing below. */
.platform-banner {
  margin: 0;
}

/* The Status table's cells are white-space: nowrap by default (see
   .data-table th/td above), which is right for every other column here but
   would force the Status column's explanation text onto one unbroken line
   and blow the table out sideways. This is the one cell on the page that
   opts back into wrapping, bounded so a long vendor error still reads as a
   paragraph rather than stretching the row. */
.status-cell {
  white-space: normal;
  min-width: 200px;
  max-width: 340px;
}

.status-cell-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* The dot is a colour accent for at-a-glance scanning; the word next to it
   is what actually carries the status once colour is unavailable (print,
   greyscale) or unreliable (colour-blindness) — see the brief: "encoded in
   shape as well as colour — a dot plus a word." */
.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}

.status-dot-ok {
  background: var(--positive);
}

.status-dot-error {
  background: var(--negative);
}

.status-dot-not_configured {
  background: var(--text-faint);
}

.status-word {
  font-size: 13px;
  font-weight: 600;
}

.status-badge-ok .status-word {
  color: var(--positive);
}

.status-badge-error .status-word {
  color: var(--negative);
}

/* not_configured is a normal, unconfigured-coverage state, never a
   failure (see status-rows.js) — muted weight and colour keep it from
   reading as one next to a red or green row. */
.status-badge-not_configured .status-word {
  color: var(--text-muted);
  font-weight: 400;
}

.status-stale-tag {
  padding: 1px 7px;
  border-radius: var(--radius);
  background: var(--warning-soft);
  color: var(--warning);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.status-explanation {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--text-muted);
}

.age-value {
  font-family: var(--font-mono);
}

.age-stale {
  color: var(--negative);
  font-weight: 600;
}

/* ---- Misc ----------------------------------------------------------------------- */

.loading {
  color: var(--text-faint);
  padding: 24px 0;
}

@media (max-width: 720px) {
  .shell {
    flex-direction: column;
  }

  .sidebar {
    flex: none;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .nav-footer {
    /* The sidebar is a row here, so the auto top margin that pushes the
       footer down in the column layout would only mis-centre it. */
    margin-top: 0;
    flex-direction: row;
    align-items: center;
    border-top: none;
    padding-top: 0;
  }

  .view {
    padding: 24px;
  }
}
