/* Layout and components. Geometry follows the design handoff: sidebar 196px,
 * top bar 46px, page padding 13px 14px, card gap 11px (20px in Ledger), cell
 * padding 9px, control height 28px, grid rows 32/27px with a 31px header.
 *
 * The AG Grid block near the bottom restyles the vendored Quartz theme through
 * the same custom properties, so the three themes carry the grid with them
 * instead of each needing its own grid theme.
 */

* { box-sizing: border-box; }

/* `hidden` is a UA style (`display: none`), and ANY author display rule beats
 * it. `.ctl` is `display: inline-flex`, so every `node.hidden = true` in
 * shell.setControls() and in the screens was silently a no-op: the top bar
 * showed Search, Filters, the columns count and Export on screens that had
 * declared none of them, and the back caret on screens with nothing to go back
 * to. It cost nothing on a wide bar and was invisible; at 375px the dead
 * controls pushed Refresh off the end of the scrolling strip. Found while
 * implementing the narrow control set, which cannot work without it. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--color-accent); text-decoration: none; }

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

.mono { font-family: var(--font-mono); font-size: 12px; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.muted { color: var(--color-neutral-500); }
.wrap { overflow-wrap: anywhere; }
.tone-ok { color: var(--ok); }
.tone-warn { color: var(--warn); }
.tone-bad { color: var(--bad); }
.tone-muted { color: var(--color-neutral-500); }

.micro {
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-neutral-500);
}

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

.shell {
  display: grid;
  grid-template-columns: 196px 1fr;
  height: 100%;
  overflow: hidden;
}

.sidebar {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--panel);
  border-right: 1px solid var(--color-neutral-900);
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 13px;
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  border: 1px solid var(--color-accent);
  border-radius: var(--r2);
  color: var(--color-accent);
  font-size: 13px;
}

.brand-word { font-size: 13px; font-weight: 500; }

/* The nav is its own scroll region so brand, theme switcher and account stay
 * pinned at any viewport height. */
.nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: 8px;
}

.nav-group {
  padding: 12px 13px 4px;
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-neutral-600);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5.5px 13px;
  font-size: 12.5px;
  color: var(--color-neutral-300);
  border-left: 2px solid transparent;
}

.nav-item i { font-size: 15px; }
.nav-item:hover { background: rgba(255, 255, 255, 0.04); }
.th-day .nav-item:hover { background: rgba(0, 0, 0, 0.04); }

.nav-item.on {
  border-left-color: var(--color-accent);
  background: var(--color-accent-200);
  color: var(--color-text);
}

.theme-switch {
  display: flex;
  gap: 4px;
  padding: 8px 10px;
  border-top: 1px solid var(--color-neutral-900);
}

.theme-switch .ctl { flex: 1; padding: 0 6px; font-size: 11px; }

.account {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-top: 1px solid var(--color-neutral-900);
}

.account-name { font-size: 12px; color: var(--color-neutral-400); overflow: hidden; text-overflow: ellipsis; }

.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 46px;
  padding: 0 14px;
  background: var(--bar);
  border-bottom: 1px solid var(--color-neutral-900);
}

.topbar-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.topbar h1 { margin: 0; font-size: 15px; font-weight: 500; white-space: nowrap; }
.crumb { font-size: 11.5px; color: var(--color-neutral-500); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* The control strip owns all remaining width and scrolls rather than wrapping. */
.controls {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
  justify-content: flex-start;
  overflow-x: auto;
  scrollbar-width: thin;
}

.controls > :first-child { margin-left: auto; }

/* Below 1140px the Group-by control folds away: it is duplicated in the grid's
 * own tool rail, so it is the safest thing to lose first. */
.bar-compact .group-by { display: none; }

.ctl {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 9px;
  font-family: inherit;
  font-size: 12px;
  color: var(--color-neutral-300);
  background: transparent;
  border: 1px solid var(--color-neutral-800);
  border-radius: var(--r2);
  cursor: pointer;
  white-space: nowrap;
}

.ctl:hover:not(:disabled) { border-color: var(--color-neutral-700); color: var(--color-text); }
.ctl:disabled { opacity: 0.45; cursor: not-allowed; }
.ctl.on { color: var(--color-accent); border-color: var(--color-accent); background: var(--color-accent-200); }
.ctl-icon { padding: 0 7px; }
.ctl-quiet { border-color: transparent; }
.ctl-primary { color: var(--color-accent); border-color: var(--color-accent); }
.ctl-danger { color: var(--bad); border-color: var(--bad); margin-right: auto; }
.ctl-static { cursor: default; }

.search { width: 196px; }
.bar-compact .search { width: 96px; }

.input { width: 100%; height: 28px; background: var(--fibg); border-color: var(--filine); color: var(--color-text); }
select.input { padding-right: 4px; }

.menu-wrap { position: relative; }

.menu {
  position: absolute;
  right: 0;
  top: 32px;
  z-index: 40;
  min-width: 160px;
  padding: 4px;
  background: var(--pop);
  border: 1px solid var(--color-neutral-800);
  border-radius: var(--r2);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.28);
}

.menu-item {
  display: block;
  width: 100%;
  padding: 6px 8px;
  font: inherit;
  font-size: 12px;
  text-align: left;
  color: var(--color-text);
  background: transparent;
  border: 0;
  cursor: pointer;
}

.menu-item:hover { background: var(--row-hover); }

.page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--page-gap);
  padding: 13px 14px;
  overflow-y: auto;
}

/* The head is one line that must survive a long config version. Without
 * min-width:0 a flex item refuses to shrink below its content, so the muted
 * "N campaigns · config <version>" span forced the row to wrap one word per
 * line at phone widths. The muted span owns the slack and ellipsises; the
 * label and the action button keep their size. */
.screen-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.screen-head > * { min-width: 0; }
.screen-head .muted { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.screen-head .source-label, .screen-head .ctl { flex: none; }
.screen-head .ctl:last-child { margin-left: auto; }

/* --- cards, KPIs --------------------------------------------------------- */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-neutral-900);
  border-radius: var(--r);
}

/* Ledger: rules instead of boxes. */
.th-ledger .card { border: 0; border-top: 1px solid var(--hair); background: transparent; }

.card-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 11px 0; }
.card-body { display: flex; flex-direction: column; gap: 9px; padding: 9px 11px 11px; }

.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--page-gap); }

/* The strip wraps rather than clips. A flex item does not shrink below its own
 * content, so six tiles in a ~540px page -- a 768px tablet, or a browser window
 * split beside an editor -- pushed the sixth past `.shell { overflow: hidden }`
 * and the freshness figure was simply cut off, with nothing to scroll to. With
 * a 140px basis they reflow onto a second row instead. At 1280px all six still
 * fit one row at identical widths, so the desktop is unchanged. Found in the
 * W7 width sweep, not reported from the phone. */
.kpis { display: flex; flex-wrap: wrap; gap: 0; }

.kpi {
  flex: 1 1 140px;
  min-width: 0;
  padding: 9px 14px;
  border-left: 1px solid var(--color-neutral-900);
}

.kpi .micro { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.kpi:first-child { border-left: 0; padding-left: 0; }
.th-ledger .kpi { border-left-color: rgba(255, 255, 255, 0.1); }
.kpi-figure { font-size: var(--kpi-size); font-weight: var(--kpi-weight); font-variant-numeric: tabular-nums; }
.kpi-note { font-size: 11px; color: var(--color-neutral-500); }

/* --- mini tables --------------------------------------------------------- */

.mini { display: flex; flex-direction: column; }

/* Tracks are minmax(0, …), never a bare fr. A bare 1fr track is min-content
 * wide at minimum, so a mono `source_profile_match` or a full campaign token
 * pushed its track open and the headers clipped mid-word instead of shortening.
 * The first column is the labelled one -- decision · reason, campaign token,
 * component name -- and gets twice the share. */
.mini-row {
  display: grid;
  grid-auto-flow: column;
  grid-template-columns: minmax(0, 2fr);
  grid-auto-columns: minmax(0, 1fr);
  align-items: center;
  gap: 9px;
  padding: 5px 0;
  border-bottom: 1px solid var(--line2);
  font-size: 12px;
}

/* Cells truncate with an ellipsis rather than overflowing their track. */
.mini-row > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-row .decision { max-width: 100%; min-width: 0; }
.mini-row .decision .pill { flex: none; }
.mini-row .decision-reason { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* An actions cell is buttons, not prose: ellipsising it would hide a control.
 * It wraps within its own track instead, which keeps the overflow off the page. */
.mini-row > .row-actions { overflow: visible; flex-wrap: wrap; justify-content: flex-end; }

.mini-row.clickable { cursor: pointer; }
.mini-row.clickable:hover { background: var(--row-hover); }

.mini-head {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-neutral-500);
  border-bottom-color: var(--color-neutral-800);
}

/* A disabled account stays legible -- it is still an identity in the audit log,
   not a deleted one -- but reads as inactive at a glance. */
.row-disabled { opacity: 0.55; }
.row-disabled .mono { text-decoration: line-through; }

.kv { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; border-bottom: 1px solid var(--line2); font-size: 12px; }
.kv-k { min-width: 150px; color: var(--color-neutral-500); }
.kv-v { overflow-wrap: anywhere; }

.dl { display: grid; grid-template-columns: 160px 1fr; gap: 4px 10px; margin: 0; font-size: 12px; }
.dl dt { color: var(--color-neutral-500); }
.dl dd { margin: 0; }

.pill {
  display: inline-block;
  padding: 1px 6px;
  font-size: 11px;
  border: 1px solid currentColor;
  border-radius: var(--r2);
}

/* Ledger renders statuses as coloured text: no background, no border, no
 * padding. */
.th-ledger .pill { border: 0; padding: 0; }

.pill.tone-ok { color: var(--ok); }
.pill.tone-warn { color: var(--warn); }
.pill.tone-bad { color: var(--bad); }

/* Console brackets its statuses. */
.th-console .pill::before { content: '[ '; }
.th-console .pill::after { content: ' ]'; }

.decision { display: inline-flex; align-items: baseline; gap: 6px; }
.decision-reason { color: var(--color-neutral-500); }

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

.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  font-size: 11.5px;
  background: var(--color-accent-200);
  border: 1px solid var(--color-neutral-800);
  border-radius: var(--r2);
}

.chip-op { color: var(--color-accent); }
.chip-value { font-family: var(--font-mono); }
.chip-x { display: inline-flex; padding: 0; background: none; border: 0; color: inherit; cursor: pointer; font-size: 11px; }

.banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 11px;
  border-left: 2px solid currentColor;
  background: var(--flowbg);
  font-size: 12px;
}

.banner-text { display: flex; flex-direction: column; gap: 2px; color: var(--color-text); }
.banner.tone-ok { color: var(--ok); }
.banner.tone-warn { color: var(--warn); }
.banner.tone-muted { color: var(--color-neutral-500); }
.banner.tone-info { color: var(--color-accent); }
.banner .ctl { margin-left: auto; }

.source-label {
  padding: 1px 6px;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--warn);
  border: 1px solid currentColor;
  border-radius: var(--r2);
}

.note { margin: 0; font-size: 11.5px; color: var(--color-neutral-500); }
.hint { font-size: 11px; color: var(--color-neutral-600); }
.warn-inline { font-size: 11px; color: var(--warn); }

.empty { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 40px 20px; text-align: center; }
.empty-title { font-size: 13px; }
.empty-detail { font-size: 11.5px; color: var(--color-neutral-500); max-width: 520px; }

.loading { display: flex; align-items: center; gap: 8px; padding: 24px; font-size: 12px; color: var(--color-neutral-500); }
.loading.warming { color: var(--warn); }

.spinner {
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--color-neutral-800);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.error-panel { display: flex; flex-direction: column; gap: 4px; padding: 16px; border-left: 2px solid var(--bad); font-size: 12px; }

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 60;
  padding: 8px 14px;
  font-size: 12px;
  background: var(--pop);
  border: 1px solid var(--color-accent);
  border-radius: var(--r2);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.28);
}

/* --- chart --------------------------------------------------------------- */

.chart { display: flex; flex-direction: column; gap: 6px; }
.chart-svg { width: 100%; height: 236px; }
.chart-grid { stroke: var(--chart-grid); stroke-width: 1; }
.chart-axis { fill: var(--chart-axis); font-size: 9px; font-family: var(--font-body); }
.chart-crosshair { stroke: var(--color-neutral-700); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-dot { fill: var(--chart-clicks); }
.chart-legend, .chart-readout { display: flex; flex-wrap: wrap; gap: 12px; font-size: 11px; color: var(--color-neutral-500); }
.chart-legend-item, .chart-readout-item { display: inline-flex; align-items: center; gap: 5px; }
.chart-readout-bucket { color: var(--color-text); }
.chart-swatch { width: 8px; height: 2px; border-radius: 1px; }

/* --- grids --------------------------------------------------------------- */

.grid-wrap { display: flex; flex: 1; min-height: 380px; gap: 0; }
.grid-host { flex: 1; min-width: 0; height: 100%; min-height: 380px; }
.grid-summary { display: flex; align-items: center; gap: 10px; font-size: 11.5px; color: var(--color-neutral-500); }
.facets { display: flex; align-items: center; gap: 6px; }
.facet { text-transform: lowercase; }
.row-actions { display: inline-flex; gap: 4px; }
.group-row { font-size: 12px; }

.ag-theme-quartz.grid-host {
  --ag-background-color: var(--color-surface);
  --ag-foreground-color: var(--color-text);
  --ag-header-background-color: var(--head);
  --ag-header-foreground-color: var(--color-neutral-500);
  --ag-border-color: var(--color-neutral-900);
  --ag-secondary-border-color: var(--vline);
  --ag-row-border-color: var(--line2);
  --ag-odd-row-background-color: transparent;
  --ag-row-hover-color: var(--row-hover);
  --ag-selected-row-background-color: var(--row-selected);
  --ag-control-panel-background-color: var(--panel);
  --ag-side-bar-panel-width: 206px;
  --ag-font-family: var(--font-body);
  --ag-font-size: 12px;
  --ag-cell-horizontal-padding: 9px;
  --ag-borders: none;
  --ag-header-column-separator-display: none;
  --ag-input-focus-border-color: var(--color-accent);
  --ag-checkbox-checked-color: var(--color-accent);
  --ag-range-selection-border-color: var(--color-accent);
}

.ag-theme-quartz .ag-header {
  border-bottom: 1px solid var(--hair);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.ag-theme-quartz .ag-cell.num, .ag-theme-quartz .ag-header-cell.num .ag-header-cell-label { justify-content: flex-end; }
.ag-theme-quartz .ag-cell.num { font-variant-numeric: tabular-nums; }
.ag-theme-quartz .ag-cell.mono { font-family: var(--font-mono); }
.ag-theme-quartz .ag-floating-filter { background: var(--fibg); }
.ag-theme-quartz .ag-floating-filter-input .ag-input-field-input { border-color: var(--filine); }
.ag-theme-quartz .ag-status-bar { border-top: 1px solid var(--color-neutral-900); height: 29px; min-height: 29px; font-size: 11px; }
.ag-theme-quartz .ag-side-buttons { width: 36px; }
.ag-theme-quartz .ag-row-pinned { background: var(--sub); font-weight: 500; }

.stripes .ag-theme-quartz { --ag-odd-row-background-color: var(--row-stripe); }

/* --- detail panel -------------------------------------------------------- */

.detail {
  width: 312px;
  flex: none;
  overflow-y: auto;
  padding: 11px;
  background: var(--panel);
  border-left: 1px solid var(--color-neutral-900);
}

.detail-inner { display: flex; flex-direction: column; gap: 9px; }
.detail-head { display: flex; align-items: center; justify-content: space-between; }
.detail-head h2 { margin: 0; font-size: 13px; font-weight: 500; }
.detail-fields { display: flex; flex-direction: column; }
.detail-fields .kv-k { min-width: 120px; }
.params-box { padding: 8px; background: var(--flowbg); border: 1px solid var(--color-neutral-900); border-radius: var(--r2); }

/* --- flows, campaign detail ---------------------------------------------- */

.tabs { display: flex; gap: 2px; height: 31px; border-bottom: 1px solid var(--color-neutral-900); }
.tab { display: inline-flex; align-items: center; padding: 0 11px; font-size: 12px; color: var(--color-neutral-400); border-bottom: 2px solid transparent; }
.tab.on { color: var(--color-text); border-bottom-color: var(--color-accent); }
.tab-body { display: flex; flex-direction: column; gap: var(--page-gap); flex: 1; min-height: 0; }

.url-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.url-bar code { flex: 1; min-width: 240px; padding: 6px 8px; background: var(--flowbg); border: 1px solid var(--color-neutral-900); border-radius: var(--r2); overflow-wrap: anywhere; }

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

.flow-card { display: flex; flex-direction: column; gap: 6px; padding: 9px 11px; background: var(--flowbg); border: 1px solid var(--color-neutral-900); border-radius: var(--r2); }
.flow-default { border-style: dashed; }
.flow-head { display: flex; align-items: center; gap: 8px; }
.flow-index { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; font-size: 11px; color: var(--color-neutral-500); border: 1px solid var(--color-neutral-800); border-radius: var(--r2); }
.flow-foot { font-size: 11.5px; }
.arrow { color: var(--color-neutral-600); }

/* --- forms and drawer ---------------------------------------------------- */

.drawer-scrim { position: fixed; inset: 0; z-index: 50; display: flex; justify-content: flex-end; background: rgba(0, 0, 0, 0.42); }

.drawer {
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  height: 100%;
  background: var(--panel);
  border-left: 1px solid var(--color-neutral-800);
}

.drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 11px 14px; border-bottom: 1px solid var(--color-neutral-900); }
.drawer-head h2 { margin: 0; font-size: 14px; font-weight: 500; }
.drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; }
.drawer-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 11px 14px; border-top: 1px solid var(--color-neutral-900); }
.confirm-text { margin: 0; font-size: 12.5px; }

.form { display: flex; flex-direction: column; gap: 11px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.field { display: flex; flex-direction: column; gap: 4px; }
.field-inline { flex-direction: row; align-items: center; gap: 6px; }
.inline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.row-list { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.mappings, .kv-rows, .rule-cards, .filter-rows { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.mapping-row, .kv-row, .filter-row { display: flex; align-items: center; gap: 6px; }
.mapping-row .input, .kv-row .input { flex: 1; }

.rule-card { display: flex; flex-direction: column; gap: 6px; padding: 8px; background: var(--flowbg); border: 1px solid var(--color-neutral-900); border-radius: var(--r2); }
.rule-card.dragging { opacity: 0.5; }
.rule-head, .rule-condition { display: flex; align-items: center; gap: 6px; }
.rule-grip { cursor: grab; color: var(--color-neutral-600); }

.check-result { font-size: 11.5px; }
.sim-output { display: flex; flex-direction: column; gap: 6px; }

.issues { display: flex; flex-direction: column; gap: 4px; }
.issue { display: flex; gap: 10px; font-size: 12px; border-bottom: 1px solid var(--line2); padding: 3px 0; }
.issue-field { min-width: 240px; color: var(--warn); }

.appearance { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end; }

/* --- report -------------------------------------------------------------- */

.report-layout { display: grid; grid-template-columns: 260px 1fr; gap: var(--page-gap); flex: 1; min-height: 0; }
.report-builder { display: flex; flex-direction: column; gap: var(--page-gap); overflow-y: auto; }
.report-result { display: flex; flex-direction: column; min-height: 0; }
.metric-list { display: flex; flex-direction: column; gap: 4px; }

/* --- login --------------------------------------------------------------- */

.login-shell { display: block; }
.login { display: flex; align-items: center; justify-content: center; height: 100%; background: var(--color-bg); }
.login-card { display: flex; flex-direction: column; gap: 12px; width: 300px; padding: 22px; background: var(--color-surface); border: 1px solid var(--color-neutral-900); border-radius: var(--r); }
.th-ledger .login-card { border: 0; border-top: 1px solid var(--hair); }
.brand-login { padding: 0 0 6px; }
.login-error { font-size: 12px; color: var(--bad); }
.login-submit { justify-content: center; }

/* --- mobile / Mini App --------------------------------------------------- */

.mtabs { display: none; }

/* THE collapse rule, and it is a DESCENDANT selector for a reason.
 *
 * `narrow` is written onto <body> by state.js applyAppearance(); the shell
 * element is `#app.shell` INSIDE it. The rule here used to be
 * `.narrow.shell, .shell.narrow` -- two compound selectors that each require
 * both classes on the SAME element, which never happens. So the shell kept its
 * desktop `196px 1fr` template while `.narrow .sidebar { display: none }` took
 * the sidebar out of the layout, and `.main` auto-placed into the surviving
 * FIRST track: the whole application rendered in a 196px column with the 1fr
 * track empty. The descendant rules below still matched, which is why the page
 * looked half-mobile rather than obviously broken.
 *
 * Descendant, not a class mirrored onto #app, because every other rule in this
 * block is already `.narrow <descendant>` -- one mechanism, not two.
 * tests/narrow-shell.test.js guards the shape. See phase7-repairs/003. */
.narrow .shell { grid-template-columns: 1fr; }

.narrow .sidebar { display: none; }
/* Tall enough to hold 44px controls -- see the hit-target block below. */
.narrow .topbar { height: 54px; }
.narrow .topbar h1 { overflow: hidden; text-overflow: ellipsis; }
.narrow .controls .search { width: 120px; }
.narrow .page { padding: 10px 12px 14px; gap: 10px; padding-bottom: 76px; }
.narrow .row-2 { grid-template-columns: 1fr; }
.narrow .report-layout { grid-template-columns: 1fr; }
.narrow .dl { grid-template-columns: 1fr; }
.narrow .kv-k { min-width: 108px; }
.narrow .mini-row { font-size: 11.5px; }
.narrow .grid-wrap { min-height: 320px; }

/* The action button in a screen head drops to its own full-width row rather
 * than squeezing the title line. */
.narrow .screen-head { flex-wrap: wrap; }
.narrow .screen-head .ctl:last-child { flex: 1 0 100%; margin-left: 0; justify-content: center; }

/* 2x2 KPIs on mobile: Clicks · Visitors · Fallback % · Freshness (spec §5).
 *
 * Membership is a per-entry flag read by kpiStrip(), not a position. The rule
 * this replaces hid `:nth-child(n+5)` of a strip ordered CLICKS, VISITORS,
 * BOTS, ALLOWED, FALLBACKS, FRESHNESS -- so a phone showed Bots and Allowed and
 * hid the two the spec mandates, while the comment above it claimed otherwise.
 * `kpis-subset` is only present when a strip has actually been curated, so an
 * uncurated strip still shows everything rather than nothing. */
.narrow .kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.narrow .kpis-subset .kpi:not(.kpi-mobile) { display: none; }
.narrow .kpi { border-left: 0; border-top: 1px solid var(--color-neutral-900); padding: 8px 10px; }
/* The right-hand column of the 2x2, marked from the ordinal WITHIN the subset.
 * nth-child cannot do this: it counts the tiles that display:none removed. */
.narrow .kpi-mobile-right { border-left: 1px solid var(--color-neutral-900); }

/* Tiles whose phone figure differs from their desktop one render both and let
 * the breakpoint choose, so crossing it needs no re-render. */
.kpi-narrow { display: none; }
.narrow .kpi-wide { display: none; }
.narrow .kpi-narrow { display: block; }

/* The config editors are desktop-only: they are multi-section forms with rule
 * builders and condition chips, and no phone design exists for them. The
 * affordances carry `desktop-only` and the explanation carries `narrow-note`,
 * both switched by CSS so a live resize is enough -- this replaces a comment
 * here that described a read-only mobile drawer nothing implemented. */
.narrow .desktop-only { display: none; }
.narrow-note { display: none; }
.narrow .narrow-note { display: block; }

.narrow .drawer { width: 100%; }
/* The click detail is a full-screen push sheet on a phone. The bottom padding
 * clears the fixed tab bar so the last field is reachable. */
.narrow .detail { position: fixed; inset: 0; z-index: 45; width: 100%; padding-bottom: 76px; }

/* Hit targets ≥44px. 28px is a desktop density; the same controls are tapped
 * with a thumb here. */
.narrow .ctl { min-height: 44px; }
.narrow .ctl-icon { min-width: 44px; justify-content: center; }
.narrow .menu-item { min-height: 44px; display: flex; align-items: center; }

/* --- mobile card lists --------------------------------------------------- */

/* Below the breakpoint the tab screens render cards instead of grids: a grid at
 * 375px is a tool rail plus a horizontally scrolling stub of a table. */
.card-list { display: flex; flex-direction: column; }

.list-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 44px;
  width: 100%;
  padding: 9px 2px;
  border: 0;
  border-bottom: 1px solid var(--line2);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.list-card:hover { background: var(--row-hover); }
.list-card-top { display: flex; align-items: baseline; gap: 8px; }
.list-card-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-card-metric { flex: none; font-variant-numeric: tabular-nums; }
.list-card-sub { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 11.5px; }
.list-card-sub > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The totals wrap rather than clip. Three labelled figures do not fit one
 * 274px line at 320px, and `.shell { overflow: hidden }` means an overflowing
 * line is not scrolled to -- it is simply gone. */
.list-total { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 9px 2px; border-top: 1px solid var(--color-neutral-800); font-size: 12px; }
.list-total-label { flex: 1 0 auto; }
.list-total-item { flex: none; font-variant-numeric: tabular-nums; }

.narrow .mtabs {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: flex;
  height: 62px;
  padding-bottom: 6px;
  background: var(--bar);
  border-top: 1px solid var(--color-neutral-900);
}

.mtab {
  flex: 1;
  min-width: 64px;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  color: var(--color-neutral-500);
}

.mtab-icon i { font-size: 20px; }
.mtab .mtab-fill { display: none; }
.mtab.on { color: var(--color-accent); }
.mtab.on .ph { display: none; }
.mtab.on .mtab-fill { display: inline; }
.mtab-label { font-size: 9.5px; }

/* --- density ------------------------------------------------------------- */

.density-compact { font-size: 12px; }
.density-compact .ctl { height: 26px; }
.density-compact .card-body { padding: 7px 9px 9px; }
.density-compact .kpi { padding: 7px 11px; }
