/* Dashboard-only styles. Loaded AFTER ../assets/tokens.css + ../assets/components.css + ../assets/site.css
   (decision 65): this file holds only what is genuinely specific to the dashboard -- the data-source bar, the
   view-routing tab strip, the "data freshness" stamp, the confidence bar, the explain-trace list, the run page's "what moved where" lists, and small
   dashboard-only additions layered on top of shared components (the run-now/glossary drawer widths).
   Everything the shared stylesheets already provide (buttons, cards, badges, banners, tooltips, popovers,
   tables, form controls, the overlay/dialog/drawer shell, the site header/footer) is reused as-is, not
   reimplemented here. Master decisions 16: the Simple/Detailed mode bar is gone -- every view always renders
   its full content. */

/* ------------------------------------------------------------ page layout */
.wrap { max-width: var(--content-max); margin: 0 auto; padding: 0 var(--space-4); }
main.wrap { padding-block: var(--space-5) var(--space-7); min-width: 0; }
body { overflow-wrap: anywhere; } /* narrow-screen safety: a long song/artist name must not force horizontal scroll */

.page-title { padding: var(--space-4) 0 var(--space-2); }
.page-title h1 { font-size: var(--text-2xl); }
.page-title p { color: var(--color-text-secondary); margin: var(--space-1) 0 0; font-size: var(--text-sm); }

[data-testid="runs-table"] td[data-label="Details"] { white-space: nowrap; }
@media (min-width: 721px) { [data-testid="runs-table"] td.cell-main { min-width: 22rem; } } /* the schedule / start / finish lines */
.num { text-align: right; white-space: nowrap; } /* extends assets/components.css's .num (tabular-nums) */

/* decision 31: a persistent, unmissable label of the active data source (not just the dropdown's own value). */
.source-current { font-size: var(--text-sm); font-weight: var(--weight-bold); color: var(--color-text); background: var(--color-elevated); display: inline-block; padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill); margin: 0; }

.source-bar[hidden] { display: none; }
.source-bar { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-4); align-items: flex-end; padding: var(--space-3) var(--space-4); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); margin: var(--space-3) 0; }
.source-bar .grow { flex: 1 1 18rem; }
.source-note { font-size: var(--text-sm); color: var(--color-text-secondary); margin: 0; flex-basis: 100%; }

/* Open local files (decision 31): a drop zone that also works as a plain file picker and with the keyboard. */
.files-drop { border: 2px dashed var(--color-muted); border-radius: var(--radius-lg); padding: var(--space-4); text-align: center; cursor: pointer; background: var(--color-bg); }
.files-drop:hover, .files-drop:focus-visible { border-color: var(--color-accent); outline: none; }
.files-drop.is-drag { border-color: var(--color-accent); background: var(--color-hover); }
.files-drop input[type="file"] { pointer-events: none; }

/* One slim row above the tabs: which source is showing, a button that unfolds the source picker, the glossary. */
.dash-bar { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-2) 0 var(--space-3); }
.dash-bar .btn { white-space: nowrap; }
.dash-bar .source-current { margin-right: auto; }

/* min-height reserves the tab bar's real height (matches the touch target) before JS populates <ul id="tabs">,
   so the initial empty bar doesn't grow once data.js/views.js fill it in -- avoids a layout shift on first load.
   This is a page-view router (hash-based, first-match navigation), not the shared ARIA .tablist/.tab component
   (which is unused elsewhere on the site too) -- the two serve different purposes, so it stays its own thing. */
.tabs { position: sticky; top: 0; z-index: 20; background: var(--color-bg); border-bottom: 1px solid var(--color-border); margin: var(--space-3) 0 0; min-height: var(--touch-target); }
.tabs ul { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-1); overflow-x: auto; }
.tabs a {
  display: inline-flex; align-items: center; min-height: var(--touch-target); padding: 0 var(--space-4); white-space: nowrap;
  color: var(--color-text-secondary); text-decoration: none; border-bottom: 3px solid transparent; font-weight: var(--weight-medium);
  transition: color var(--motion-fast) var(--motion-ease), background var(--motion-fast) var(--motion-ease);
}
.tabs a:hover { color: var(--color-text); background: var(--color-hover); }
.tabs a[aria-current="page"] { color: var(--color-text); border-bottom-color: var(--color-accent); }

.view-head { margin-top: var(--space-5); }
.view-head h2 { font-size: var(--text-xl); }
.view-head h2:focus { outline: none; }
.question { color: var(--color-text-secondary); margin: var(--space-1) 0 var(--space-2); }
.stamp { font-size: var(--text-sm); color: var(--color-text-secondary); margin: 0 0 var(--space-4); display: flex; flex-wrap: wrap; gap: 0 var(--space-4); }
.stamp .lbl { color: var(--color-text-secondary); }
.stamp time { color: var(--color-text); }

/* ------------------------------------------------------------ KPI card grid (extends the shared .card) */
/* assets/components.css's .card has no bottom margin (it expects a .stack/.grid wrapper to supply the gap) and
   no min-width:0 -- this dashboard renders many standalone cards back-to-back outside of a .grid too, so it
   adds just those two properties rather than a parallel .card definition. Sub-headings inside a card use a
   smaller size than a page-level h3/h4 so a card full of <h3> sub-sections doesn't read as several page titles. */
.card { margin: 0 0 var(--space-4); min-width: 0; }
.card h3 { font-size: var(--text-lg); margin: 0 0 var(--space-2); }
.card h4 { font-size: var(--text-md); margin: var(--space-4) 0 var(--space-2); }
.grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 12.5rem), 1fr)); margin: 0 0 var(--space-4); }
.grid > .card { margin: 0; }
.metric-label { font-size: var(--text-sm); color: var(--color-text-secondary); margin: 0 0 var(--space-1); }
.metric-value { font-size: var(--text-2xl); font-weight: var(--weight-bold); line-height: var(--leading-tight); margin: 0 0 var(--space-1); }
.metric-sub { font-size: var(--text-sm); color: var(--color-text-secondary); margin: 0; }
.chips-title { margin: 0 0 var(--space-2); }
.chips-title + .chips { margin-bottom: var(--space-5); }
.run-now-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin: 0 0 var(--space-4); }
[data-card="last-run"] .metric-value, [data-card="next-run"] .metric-value { font-size: var(--text-lg); }
.callout { border-left: 4px solid var(--color-accent); background: var(--color-elevated); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin: 0 0 var(--space-4); }
.chips { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
.check-cell { min-width: var(--touch-target); text-align: center; }

/* ------------------------------------------------------------ additional interactive text (no shared equivalent) */
/* A song title that opens the Explain drawer, styled as inline text inside a table cell rather than a full
   button -- there is no "text-styled action" component in assets/components.css to reuse, so this stays a
   small dashboard-only addition (it is a real <button>, per decision 40, not an <a>). */
.link-btn { background: none; border: 0; padding: 0; font: inherit; color: var(--color-accent-text); text-align: left; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.link-btn:hover { color: var(--color-accent-hover); }
/* A sortable column header. Visually similar to assets/components.css's .th-sort, but this dashboard's sort
   is type-aware (numeric age, date, decision order, language) and state is tracked per view for the CSV
   export and "N more" paging, so it keeps its own small button/handler rather than ui.js's generic
   text-only sortBy(); kept visually consistent (bold, inline arrow) with .th-sort on purpose. */
.sort-btn { background: none; border: 0; padding: var(--space-1) 0; font: inherit; font-weight: var(--weight-bold); color: inherit; cursor: pointer; display: inline-flex; gap: var(--space-1); align-items: center; }
.sort-btn:hover { color: var(--color-accent-text); }

/* ------------------------------------------------------------ error box (no shared equivalent) */
.errorbox { background: var(--color-surface); border: 1px dashed var(--color-muted); border-radius: var(--radius-lg); padding: var(--space-5); margin: 0 0 var(--space-4); border-style: solid; border-color: var(--color-danger); }
.errorbox h3 { margin: 0 0 var(--space-2); font-size: var(--text-lg); }
.cmd { display: inline-block; max-width: 100%; overflow-x: auto; white-space: nowrap; vertical-align: bottom; }
.loading { color: var(--color-text-secondary); padding: var(--space-5) 0; }

/* ------------------------------------------------------------ card-on-mobile tables (extends .table-wrap/.table) */
.table-wrap.cards { border: 0; background: transparent; overflow: visible; }
tr.row-bad td:first-child { box-shadow: inset 4px 0 0 var(--color-danger); }
tr.row-warn td:first-child { box-shadow: inset 4px 0 0 var(--color-warning); }
.toolbar { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: flex-end; margin: 0 0 var(--space-3); }
.toolbar .grow { flex: 1 1 14rem; }
.toolbar .grow input { width: 100%; }
.result-count { font-size: var(--text-sm); color: var(--color-text-secondary); margin: 0 0 var(--space-2); }

@media (max-width: 47.99rem) {
  /* Phone: the six tabs become a 3 x 2 grid of comfortably sized touch targets (no sideways scrolling, no tiny
     text). The strip stops being sticky here, because two rows of tabs would eat a quarter of the screen. */
  .page-title { padding-top: var(--space-3); }
  .dash-bar .btn { padding-inline: var(--space-3); }
  .source-current { padding-inline: var(--space-2); }
  .page-title h1 { font-size: var(--text-xl); }
  .tabs { position: static; border-bottom: 0; margin-top: 0; min-height: 0; }
  .tabs ul { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); overflow: visible; }
  .tabs a { justify-content: center; padding: 0 var(--space-2); font-size: var(--text-sm); border: 1px solid var(--color-border); border-radius: var(--radius-pill); }
  .tabs a[aria-current="page"] { background: var(--color-elevated); border-color: var(--color-accent); }
  .view-head { margin-top: var(--space-4); }
  .src-prefix { display: none; }
  .view-head h2 { font-size: var(--text-lg); }
  .stamp { flex-direction: column; }
  .stamp > .lbl { display: none; }
  /* Overview KPI cards: two per row, tighter, smaller numbers */
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  .grid > .card { padding: var(--space-3); }
  .metric-value { font-size: var(--text-xl); }
  .metric-sub { font-size: var(--text-xs); }
  .callout { padding: var(--space-3); }
  /* Cards that carry less-important numbers fold them away on a phone; the full row is still in the Explain
     drawer / run details, and everything is shown on larger screens. */
  .stack td.cell-extra { display: none; }
  /* Inbox cards: song, plain-words status, where it goes. Labels only where they add meaning. */
  [data-testid="inbox-table"] td[data-label="Decision"],
  [data-testid="inbox-table"] td[data-label="Target"] { padding-left: 0; min-height: 0; }
  [data-testid="inbox-table"] td[data-label="Decision"]::before { display: none; }
  [data-testid="inbox-table"] td[data-label="Target"]::before { content: "To"; width: auto; position: static; margin-right: var(--space-2); }
  [data-testid="inbox-table"] td[data-label="Eligible on"] { padding-left: 0; }
  [data-testid="inbox-table"] td[data-label="Eligible on"]::before { content: "Eligible"; width: auto; position: static; margin-right: var(--space-2); }
  [data-testid="inbox-table"] tr[data-decision="will_move"] td[data-label="Eligible on"] { display: none; }
  /* Filter row: search on its own line, the two selects side by side */
  .toolbar .field { flex: 1 1 8rem; }
  .toolbar .grow { flex-basis: 100%; }
  .stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .stack, .stack tbody, .stack tr, .stack td { display: block; width: 100%; }
  .stack tr { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3); }
  /* Label in a fixed left column, value (which may be several inline pieces: badge + text, code + note) flowing
     in the right column. A flex row split such cells into narrow side-by-side fragments on phones. */
  .stack td { position: relative; display: block; border: 0; padding: var(--space-1) 0 var(--space-1) 40%; text-align: left; min-height: 1.75rem; overflow-wrap: anywhere; }
  .stack td::before { content: attr(data-label); position: absolute; left: 0; top: var(--space-1); width: 36%; color: var(--color-text-secondary); font-weight: var(--weight-medium); overflow-wrap: normal; hyphens: auto; }
  .stack td.num { text-align: left; }
  .stack td.cell-main { padding-left: 0; font-size: var(--text-md); }
  .stack td.cell-main::before { display: none; }
  .stack td .badge { white-space: normal; max-width: 100%; }
  .stack tr.row-bad, .stack tr.row-warn { border-left-width: 4px; }
  .stack tr.row-bad { border-left-color: var(--color-danger); }
  .stack tr.row-warn { border-left-color: var(--color-warning); }
  .stack tr.row-bad td:first-child, .stack tr.row-warn td:first-child { box-shadow: none; }
  .stack tbody tr:hover { background: var(--color-surface); }
}

/* ------------------------------------------------------------ confidence bar (Explain drawer; no shared equivalent) */
.bar { position: relative; height: 10px; border-radius: var(--radius-pill); background: var(--color-hover); overflow: hidden; min-width: 4rem; }
.bar > span { position: absolute; inset: 0 auto 0 0; width: var(--w, 0%); background: var(--color-accent); border-radius: var(--radius-pill); }

/* ------------------------------------------------------------ detail blocks (no shared equivalent) */
dl.kv { display: grid; grid-template-columns: minmax(8rem, max-content) minmax(0, 1fr); gap: var(--space-1) var(--space-4); margin: 0 0 var(--space-3); font-size: var(--text-sm); }
dl.kv dt { color: var(--color-text-secondary); }
dl.kv dd { margin: 0; }
details { margin: 0 0 var(--space-3); }
details > summary { cursor: pointer; font-weight: var(--weight-medium); padding: var(--space-2) 0; }
.copy-row { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.copy-row code { flex: 1 1 14rem; overflow-x: auto; white-space: nowrap; padding: var(--space-2) var(--space-3); }
.delta-up { color: var(--color-accent-text); }
.delta-down { color: var(--color-danger-text); }

/* ------------------------------------------------------------ run-now/glossary drawer width (extends .drawer) */
/* The Explain drawer's content (sentence-first narrative + technical trace + language and genre) needs more room than
   Configure's Versions/Schema drawers, so it keeps the wider 36rem it always had; everything else about the
   drawer (position, backdrop, slide-in, focus trap) is the shared .overlay/.overlay-side/.drawer component. */
#drawer-root .drawer { width: min(100%, 36rem); }
#run-now-body .field { margin-bottom: var(--space-3); }
#run-now-body ol.steps { padding-left: 1.25rem; display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
#run-now-body input[type="checkbox"] { margin-right: var(--space-2); }

/* ------------------------------------------------------------ explain drawer trace */
.trace { list-style: none; margin: 0; padding: 0; counter-reset: rule; }
.trace > li { counter-increment: rule; border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-3); margin: 0 0 var(--space-2); }
.trace > li.t-matched { border-color: var(--color-accent); }
.trace > li.t-matched_too_young { border-color: var(--color-info); }
.trace > li.t-not_reached_but_would_match { border-color: var(--color-warning); }
.trace .t-head { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; justify-content: space-between; }
.trace .t-name::before { content: counter(rule) ". "; color: var(--color-text-secondary); }
.trace .t-name { font-weight: var(--weight-bold); }
.trace ul { list-style: none; margin: var(--space-2) 0 0; padding: 0; font-size: var(--text-sm); }
.trace ul li { padding: var(--space-1) 0; display: flex; gap: var(--space-2); }
.trace .ct-ok { color: var(--color-success-text); font-weight: var(--weight-bold); flex: none; }
.trace .ct-no { color: var(--color-danger-text); font-weight: var(--weight-bold); flex: none; }
.conf { display: flex; gap: var(--space-2); align-items: center; }
.conf .bar { flex: 0 1 8rem; }

@media (max-width: 47.99rem) {
  dl.kv { grid-template-columns: minmax(0, 1fr); }
  dl.kv dt { margin-top: var(--space-2); }
}

/* narrow-screen safety: never let a badge, code chip or table cell force the page wider than the viewport */
code, .cmd code { overflow-wrap: anywhere; word-break: break-word; }
.stack td, .stack th, .grid > * { min-width: 0; }

/* No-token ("by hand") alternative inside the connect dialogs */
.manual-save, .manual-run { margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }

/* ------------------------------------------------------------ song titles are never cut off */
/* A title or artist always wraps onto as many lines as it needs, on a phone too: no ellipsis, no line clamp, no
   fixed height. (There is deliberately no text-overflow / -webkit-line-clamp anywhere in the dashboard.) */
.link-btn { display: inline; max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: left; }
.cell-main, .cell-main * { text-overflow: clip; }
#drawer-title { overflow-wrap: anywhere; white-space: normal; }
#drawer-title .sub { display: block; margin-top: var(--space-1); font-size: var(--text-sm); font-weight: var(--weight-regular); color: var(--color-text-secondary); }

/* ------------------------------------------------------------ run list + run page: what moved where */
.run-summary { margin-top: var(--space-1); color: var(--color-text-secondary); }
.moved-group { border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); margin: 0 0 var(--space-3); background: var(--color-surface); }
.moved-group h5 { margin: 0 0 var(--space-1); font-size: var(--text-md); overflow-wrap: anywhere; }
.moved-group p { margin: 0 0 var(--space-2); }
.moved-list { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }
.moved-song { overflow-wrap: anywhere; }
.stay-list { margin: 0 0 var(--space-3); padding-left: var(--space-5); }
.stay-list ul { margin: var(--space-1) 0 var(--space-2); padding-left: var(--space-5); }
details.tech { border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 0 var(--space-4); margin-top: var(--space-4); background: var(--color-surface); }
details.tech[open] { padding-bottom: var(--space-3); }
[data-banner="inbox-snapshot"], [data-banner="run-mismatch"] { margin-bottom: var(--space-3); }
.nowrap { white-space: nowrap; }
