/* Shell: the document frame every page shares.
 *
 * Built only on the semantic tokens in tokens.css — no colour literal belongs
 * in this file, and tests/test_dashboard_shell.py asserts it. Retheming is
 * therefore one file plus the contrast audit.
 *
 * The v1 stylesheet is imported beneath as a legacy layer so every page keeps
 * working while the pages are rebuilt one at a time. Each page task retires
 * its own legacy rules; the final task removes this import.
 */

@layer legacy, shell;

@import url("/static/dashboard.css") layer(legacy);

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

  body {
    margin: 0;
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: var(--text-base);
    line-height: 1.5;
  }

  /* Numbers that can change never reflow their column. */
  .num,
  td.num,
  .mono {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
  }

  h1 {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: 650;
    letter-spacing: -0.01em;
  }

  h2 {
    margin: 0 0 var(--space-3);
    font-size: var(--text-lg);
    font-weight: 600;
  }

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

  a {
    color: var(--accent);
  }

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

  .shell-topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    background: var(--surface-sunken);
    border-bottom: var(--border-width) solid var(--border);
  }

  .shell-main {
    padding: var(--space-5) var(--space-5) var(--space-7);
  }

  @media (max-width: 767px) {
    .shell-topbar,
    .shell-main {
      padding-inline: var(--space-4);
    }
  }

  /* --- surfaces ---------------------------------------------------------- */

  .card {
    background: var(--surface-raised);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
  }

  .card + .card {
    margin-top: var(--space-4);
  }

  .well {
    background: var(--surface-sunken);
    border-radius: var(--radius);
    padding: var(--space-3);
  }

  /* --- controls ---------------------------------------------------------- */

  .btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
    color: var(--text);
    font: inherit;
    text-decoration: none;
    cursor: pointer;
  }

  .btn:hover {
    border-color: color-mix(in oklch, var(--accent) 50%, var(--border));
  }

  .btn[aria-pressed="true"],
  .btn.is-selected {
    border-color: var(--accent);
    background: color-mix(in oklch, var(--accent) 14%, transparent);
  }

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

  .btn-danger {
    border-color: color-mix(in oklch, var(--danger) 55%, var(--border));
    background: color-mix(in oklch, var(--danger) 12%, transparent);
    color: var(--danger);
  }

  .btn:disabled,
  .btn[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
  }

  /* One visible focus style for everything, never removed. */
  :where(a, button, input, select, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius);
  }

  input,
  select {
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-sunken);
    color: var(--text);
    font: inherit;
  }

  /* --- status roles ------------------------------------------------------ */

  .pos,
  .is-long {
    color: var(--long);
  }

  .neg,
  .is-short {
    color: var(--short);
  }

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

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

  .chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    border-radius: 999px;
    border: var(--border-width) solid var(--border);
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: var(--text-xs);
  }

  .chip-warn {
    border-color: color-mix(in oklch, var(--warn) 55%, var(--border));
    background: color-mix(in oklch, var(--warn) 12%, transparent);
    color: var(--warn);
  }

  /* A sentence plus a field list is not a pill: inline-flex would lay them out
     as two columns and overflow the rounded border at narrow widths. */
  .callout {
    display: block;
    padding: var(--space-3);
    border-radius: var(--radius);
    border: var(--border-width) solid var(--border);
    background: var(--surface-sunken);
    font-size: var(--text-sm);
    line-height: 1.5;
  }

  .callout-warn {
    border-color: color-mix(in oklch, var(--warn) 55%, var(--border));
    background: color-mix(in oklch, var(--warn) 10%, transparent);
    color: var(--warn);
  }

  .callout .mono {
    overflow-wrap: anywhere;
  }

  .chip-accent {
    border-color: color-mix(in oklch, var(--accent) 55%, var(--border));
    background: color-mix(in oklch, var(--accent) 12%, transparent);
    color: var(--accent);
  }

  /* --- motion ------------------------------------------------------------ */

  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

@layer shell {
  .shell-palette[hidden] { display: none; }

  .shell-palette {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: grid;
    place-items: start center;
    padding-top: var(--space-7);
  }

  .shell-palette-backdrop {
    position: absolute;
    inset: 0;
    background: color-mix(in oklch, var(--surface-sunken) 72%, transparent);
  }

  .shell-palette-box {
    position: relative;
    width: min(560px, calc(100vw - var(--space-6)));
    background: var(--surface-raised);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
  }

  .shell-palette-input {
    width: 100%;
    font-size: var(--text-lg);
  }

  .shell-palette-list {
    margin: var(--space-3) 0 0;
    padding: 0;
    list-style: none;
    max-height: 50vh;
    overflow-y: auto;
  }

  .shell-palette-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius);
    cursor: pointer;
  }

  .shell-palette-item[aria-selected="true"] {
    background: color-mix(in oklch, var(--accent) 16%, transparent);
  }
}

@layer shell {
  /* overflow-x: auto makes overflow-y compute to auto, so this wrapper is
     the sticky header's scroll container. Without a height it never scrolls
     and the header never sticks.

     .table-wrap is the inherited wrapper the real pages render; the macro's
     own .table-scroll would otherwise be the only one that got this. */
  .table-scroll,
  .table-wrap:has(.data-table) {
    overflow: auto;
    max-height: 70vh;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
  }

  .data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--text-sm);
  }

  .data-table th,
  .data-table td {
    padding: var(--space-2) var(--space-3);
    text-align: left;
    border-bottom: var(--border-width) solid var(--border);
    white-space: nowrap;
  }

  .data-table th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--surface-sunken);
    font-weight: 600;
  }

  .data-table th.num,
  .data-table td.num {
    text-align: right;
  }

  /* The symbol stays visible while a wide table scrolls sideways. */
  .data-table .col-pinned {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface-raised);
  }

  .data-table th.col-pinned {
    z-index: 3;
    background: var(--surface-sunken);
  }

  .data-table tbody tr:hover td {
    background: color-mix(in oklch, var(--accent) 7%, transparent);
  }

  .th-sort {
    all: unset;
    cursor: pointer;
    font: inherit;
    color: inherit;
  }

  /* all: unset resets outline-style, and this rule sits after the shared
     :where(...):focus-visible one with equal specificity, so it wins. The
     indicator has to be restored explicitly. */
  .th-sort:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius);
  }

  .data-table th[aria-sort] .th-sort::after {
    content: " ↓";
    color: var(--accent);
  }
}

@layer shell {
  .provenance {
    background: var(--surface-sunken);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-3);
  }

  .provenance-title {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
  }

  .provenance-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
    margin: 0;
  }

  .provenance-fact dt {
    font-size: var(--text-xs);
    color: var(--text-muted);
  }

  .provenance-fact dd {
    margin: 0;
    font-size: var(--text-sm);
  }

  .provenance-warning {
    margin: var(--space-3) 0 0;
    white-space: normal;
  }
}

@layer shell {
  .chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    min-height: 1.5em;
    padding: var(--space-2) 0;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-xs);
    color: var(--text-muted);
  }

  .chart-legend-level {
    color: var(--accent);
  }
}

@layer shell {
  .code-block {
    margin: var(--space-3) 0;
    padding: var(--space-3);
    background: var(--surface-sunken);
    border-radius: var(--radius);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }

  /* One scale across the terms, so a gain and a loss that nearly cancel read
     as two forces rather than as "nothing changed". */
  .waterfall {
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--space-2);
  }

  .waterfall-row {
    display: grid;
    grid-template-columns: minmax(7rem, 10rem) 1fr minmax(6rem, auto);
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-sm);
  }

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

  .waterfall-track {
    display: block;
    height: var(--space-3);
    background: var(--surface-sunken);
    border-radius: 999px;
    overflow: hidden;
  }

  .waterfall-bar {
    display: block;
    height: 100%;
    width: calc(var(--share) * 100%);
    border-radius: 999px;
    background: var(--long);
  }

  .waterfall-bar.is-short {
    background: var(--short);
  }

  .waterfall-value {
    text-align: right;
  }

  .diff-columns {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: var(--space-3);
  }

  .diff-list {
    margin: 0;
    padding-left: var(--space-4);
    font-size: var(--text-sm);
  }

  @media (max-width: 767px) {
    .waterfall-row {
      grid-template-columns: 1fr minmax(6rem, auto);
    }
    .waterfall-track { grid-column: 1 / -1; }
  }
}

@layer shell {
  /* At phone width a wide table cannot scroll usefully, so each row folds into
     a card. Cells keep their header label, or a folded row loses which number
     is which. */
  @media (max-width: 767px) {
    .data-table[data-foldable] thead {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
    }

    .data-table[data-foldable],
    .data-table[data-foldable] tbody,
    .data-table[data-foldable] tr,
    .data-table[data-foldable] td {
      display: block;
      width: auto;
    }

    .data-table[data-foldable] tr {
      margin-bottom: var(--space-3);
      border: var(--border-width) solid var(--border);
      border-radius: var(--radius);
      background: var(--surface-raised);
    }

    .data-table[data-foldable] td {
      display: flex;
      justify-content: space-between;
      gap: var(--space-3);
      border-bottom: 0;
      white-space: normal;
    }

    .data-table[data-foldable] td[data-label]::before {
      content: attr(data-label);
      color: var(--text-muted);
      font-size: var(--text-xs);
    }

    .data-table[data-foldable] td.num {
      text-align: right;
    }

    /* Nothing is pinned once the row is a card. */
    .data-table[data-foldable] .col-pinned {
      position: static;
    }

    /* A table that cannot fold keeps its sideways scroll, inside its own
       wrapper, rather than pushing the page. */
    .table-scroll:has(.data-table[data-foldable]),
    .table-wrap:has(.data-table[data-foldable]) {
      overflow-x: visible;
      max-height: none;
      border: 0;
    }
  }
}

@layer shell {
  /* Three groups, not one pile: who you are, where you go, what you do. The
     job controls sit behind a disclosure so they do not push the content of
     every page down by seven rows of buttons. */
  .shell-topbar {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "brand nav tools" "actions actions actions";
    align-items: center;
  }

  .shell-brand { grid-area: brand; display: flex; align-items: center; gap: var(--space-2); }
  .shell-nav { grid-area: nav; display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center; }
  .shell-tools { grid-area: tools; display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: flex-end; }
  .shell-actions { grid-area: actions; }

  .shell-brand p { margin: 0; font-size: var(--text-xs); }

  .shell-logo { display: block; height: 28px; width: auto; }

  .shell-version {
    font-weight: 700;
    letter-spacing: 0.04em;
  }

  .shell-actions > summary {
    display: inline-flex;
    list-style: none;
    margin-top: var(--space-2);
  }

  .shell-actions > summary::-webkit-details-marker { display: none; }

  .shell-actions-forms {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding-top: var(--space-2);
  }

  .shell-inline-form { display: inline-flex; gap: var(--space-2); margin: 0; }

  @media (max-width: 767px) {
    .shell-topbar {
      grid-template-columns: 1fr;
      grid-template-areas: "brand" "nav" "tools" "actions";
      justify-items: start;
    }
    .shell-nav, .shell-tools { justify-content: flex-start; }
    /* Only the palette button can lose its label: it still reads "⌘K".
       A button with neither icon nor text is a blank square. */
    [data-palette-open] .shell-tool-label { display: none; }
  }
}

@layer shell {
  /* Inherited controls that were laid out for a wide screen. Measured at
     375px they pushed the page to 491px wide, which breaks every fixed
     overlay as well as the reading width. */
  @media (max-width: 767px) {
    .strategy-panel-actions,
    .strategy-switch,
    .actions,
    .panel-head {
      flex-wrap: wrap;
      max-width: 100%;
    }

    .strategy-switch a,
    .pill {
      white-space: normal;
    }
  }

  /* A page that scrolls sideways is a layout bug, not a feature. */
  html, body {
    overflow-x: clip;
  }
}
