/* Strickle — base element styling, the kit's global elements, and the small token-valued
   utility set. Layer order is declared once in tokens.css (loaded first); kit-partial
   styling lives in components/<partial>.css (one file per shared/ partial — enforced by
   test/design/css_lint_test.rb). Every value here comes from tokens.css via var(--…). */

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

  /* Reserve the scrollbar's width even on a page too short to scroll, so content never shifts
     sideways the moment a list grows past the viewport (classic scrollbars; overlay scrollbars
     take no width either way). */
  html {
    scrollbar-gutter: stable;
  }

  body {
    margin: 0;
    background-color: var(--page);
    color: var(--text);
    font: var(--type-body);
    -webkit-font-smoothing: antialiased;
  }

  h1, h2, h3, h4 {
    margin: 0;
    letter-spacing: var(--tracking-h1);
    text-wrap: balance;
  }
  h1 { font: var(--type-h1); }
  h2 { font: var(--type-h2); }
  h3 { font: var(--type-h2); }

  p, ul, ol, dl, dd, figure {
    margin: 0;
  }
  p {
    text-wrap: pretty;
  }

  /* Turbo view transitions run on the house easing; browsers without support (or without
     the layout's opt-in meta) fall back to the instant swap. */
  ::view-transition-old(root), ::view-transition-new(root) {
    animation-duration: var(--duration-normal);
    animation-timing-function: var(--ease-smooth);
  }
  ul, ol {
    padding: 0;
    list-style: none;
  }

  a {
    color: var(--accent);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-smooth);
  }
  a:hover {
    color: var(--accent-hover);
  }

  code, pre, kbd {
    font: var(--type-mono);
  }

  turbo-frame {
    display: block; /* custom elements default to inline; frames are layout containers here */
  }

  summary {
    color: var(--accent);
    font-weight: 600;
  }
  summary:hover {
    color: var(--accent-hover);
  }

  input[type="checkbox"], input[type="radio"] {
    accent-color: var(--accent);
  }

  ::selection {
    background-color: var(--accent-tint);
  }

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

  /* Native buttons/inputs inherit nothing by default — pull them into the type system.
     Buttons also lose the UA chrome (Preflight used to do this): the kit classes own
     background/border/padding. */
  button, input, select, textarea {
    font: inherit;
    color: inherit;
  }
  button {
    background: none;
    border: 0;
    padding: 0;
  }

  /* Preflight is gone with Tailwind; restore the pointer cursor on every interactive
     control — <button>, form submits, button_to, [role=button], disclosure summaries. */
  button:not(:disabled),
  [type="button"]:not(:disabled),
  [type="submit"]:not(:disabled),
  [type="reset"]:not(:disabled),
  [role="button"]:not(:disabled),
  summary {
    cursor: pointer;
  }

  /* Reveal height the right way: animate disclosure content between 0 and natural height
     via ::details-content — no max-height hack, no JS; unsupported browsers open instantly. */
  @supports (interpolate-size: allow-keywords) {
    :root {
      interpolate-size: allow-keywords;
    }
    details::details-content {
      block-size: 0;
      opacity: 0;
      overflow-x: visible; /* don't clip focus rings on nested inputs */
      overflow-y: clip;
      transition: block-size var(--duration-slow) var(--ease-smooth),
        opacity var(--duration-slow) var(--ease-smooth),
        content-visibility var(--duration-slow) var(--ease-smooth);
      transition-behavior: allow-discrete;
    }
    details[open]::details-content {
      block-size: auto;
      opacity: 1;
    }
  }
}

@layer elements {
  /* ---- App scaffolding: the chrome wrapper, the content column, page headers. ---- */
  .app-layout {
    display: block;
  }
  @media (min-width: 768px) {
    .app-layout {
      display: flex;
    }
  }
  .app-content {
    flex: 1;
    min-width: 0;
  }
  .main-container {
    max-width: var(--container);
    margin-inline: auto;
    padding: var(--space-5) var(--space-4);
  }
  @media (min-width: 768px) {
    .main-container {
      padding-inline: var(--space-6);
    }
  }
  /* Centered narrow column for the auth + OAuth-consent screens. Declared after
     .main-container so its width clamp wins when both apply (same layer + specificity). */
  .auth-card {
    max-width: var(--column-narrow);
    margin-inline: auto;
    margin-top: var(--space-6);
  }
  .page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
  }
  /* The one line under a page title: secondary ink, held to a scannable width. Headers
     without actions skip .page-header (it would set h1 and subtitle side by side) but keep
     its bottom margin, so every page starts at the same rhythm. */
  .page-subtitle {
    margin-top: var(--space-1);
    max-width: var(--prose);
    color: var(--text-secondary);
  }

  /* ---- Wordmark — flat accent (the gradient's successor). No mark exists: the product
     name set in the UI face IS the mark. ---- */
  .lucide-icon {
    display: inline-flex;
    flex-shrink: 0;
    width: var(--icon);
    height: var(--icon);
  }
  .lucide-icon svg { width: 100%; height: 100%; stroke-width: 1.6; }

  .wordmark {
    font: var(--type-wordmark);
    letter-spacing: var(--tracking-wordmark);
    color: var(--accent);
  }
  .wordmark-lg {
    font: var(--type-wordmark-lg);
    letter-spacing: var(--tracking-wordmark);
    color: var(--accent);
  }

  /* ---- Buttons — flat (the gradient era is over); one primary action per view.
     `ghost` is the quiet variant (Cancel, Dismiss, tertiary) and never carries red;
     `danger` is the one non-error use of red, reserved for irreversible actions — it wears
     red in the label at rest, takes the wash + red border on hover, and only fills solid
     while pressed, so a destructive control reads as destructive before it is clicked. ---- */
  .btn-primary, .btn-secondary, .btn-ghost, .btn-danger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 8px 14px;
    font: var(--type-control);
    transition: background-color var(--duration-normal) var(--ease-smooth),
      color var(--duration-normal) var(--ease-smooth),
      border-color var(--duration-normal) var(--ease-smooth),
      transform var(--duration-fast) var(--ease-smooth);
  }
  .btn-sm {
    padding: var(--space-1) var(--space-3);
    font: var(--type-control-sm);
  }
  /* Tactile press — every button is felt with a subtle scale-down. */
  .btn-primary:active, .btn-secondary:active, .btn-ghost:active, .btn-danger:active {
    transform: scale(0.98);
  }
  .btn-primary {
    background-color: var(--accent);
    color: var(--on-accent);
    box-shadow: var(--shadow-sm);
  }
  .btn-primary:hover {
    background-color: var(--accent-hover);
    color: var(--on-accent);
  }
  .btn-primary:active {
    background-color: var(--accent-active);
  }
  .btn-secondary {
    background-color: var(--surface);
    color: var(--text);
    border-color: var(--border-strong);
    box-shadow: var(--shadow-sm);
  }
  .btn-secondary:hover {
    background-color: var(--surface-sunken);
    color: var(--text);
  }
  .btn-ghost {
    color: var(--text-secondary);
  }
  .btn-ghost:hover {
    background-color: var(--surface-sunken);
    color: var(--text);
  }
  .btn-danger {
    background-color: var(--surface);
    color: var(--error-text);
    border-color: var(--border-strong);
    box-shadow: var(--shadow-sm);
  }
  .btn-danger:hover {
    background-color: var(--chip-err-bg);
    border-color: var(--error);
    color: var(--error-text);
  }
  .btn-danger:active {
    background-color: var(--error);
    border-color: var(--error);
    color: var(--on-accent);
  }
  /* Sunken and inert. Declared AFTER every variant: `.is-disabled` ties `.btn-danger` on
     specificity, so an unavailable destructive control only reads as unavailable if this wins on
     source order. */
  .btn-primary:disabled, .btn-secondary:disabled, .btn-ghost:disabled, .btn-danger:disabled,
  .is-disabled {
    background-color: var(--surface-sunken);
    color: var(--text-faint);
    border-color: var(--border);
    box-shadow: none;
    pointer-events: none;
    cursor: not-allowed;
  }
  .btn-block {
    width: 100%;
  }

  /* ---- Chips — the status/trust vocabulary (pills, sentence case). The wash pairs are
     the locked tint-80 values; .chip-outline is the neutral "structural" chip
     (hidden-on-share-links). Meaning per state lives in the helpers, not here. ---- */
  .chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    border-radius: var(--chip-radius);
    padding: 2px var(--space-2);
    font: var(--chip-font);
    white-space: nowrap;
  }
  .chip-ok {
    background-color: var(--chip-ok-bg);
    color: var(--chip-ok-text);
  }
  .chip-warn {
    background-color: var(--chip-warn-bg);
    color: var(--chip-warn-text);
  }
  .chip-err {
    background-color: var(--chip-err-bg);
    color: var(--chip-err-text);
  }
  .chip-err-solid {
    background-color: var(--error);
    color: var(--on-accent);
  }
  .chip-info {
    background-color: var(--chip-info-bg);
    color: var(--chip-info-text);
  }
  .chip-neutral {
    background-color: var(--surface-sunken);
    color: var(--text-secondary);
  }
  .chip-outline {
    border: 1px solid var(--border-strong);
    background-color: var(--surface);
    color: var(--text-secondary);
  }

  /* ---- Card — the only elevation; hairline border does the structure. ---- */
  .card {
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: var(--space-4);
  }
  .card-flush {
    padding: 0;
    overflow: hidden;
  }

  /* ---- Dialog — a native modal <dialog> for a short form (dialog_controller.js). The width is
     the auth card's; the browser's own max-width keeps a margin on a phone. Padding lives on
     .dialog-body so that a click on the <dialog> element itself can only be the backdrop. ---- */
  .dialog {
    width: var(--column-narrow);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background-color: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-md);
  }
  .dialog::backdrop {
    background-color: color-mix(in oklch, var(--text) 25%, transparent);
  }
  .dialog-body {
    padding: var(--space-5);
  }
  .dialog-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    margin-top: var(--space-5);
  }

  /* ---- Section micro-label — the small-caps muted eyebrow above a content block. ---- */
  .section-label {
    display: block;
    font: var(--type-label);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-bottom: var(--space-2);
  }

  /* ---- Stat figure — the big number on KPI tiles and scalar previews. ---- */
  .stat {
    font: var(--type-metric);
    letter-spacing: var(--tracking-h1);
    font-variant-numeric: tabular-nums;
  }

  /* ---- Fact list — the label|value pairs on detail surfaces (source detail, the run
     drawer, definition pages). ---- */
  .fact-list {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--space-2) var(--space-4);
  }
  .fact-list dt {
    color: var(--text-secondary);
  }
  .fact-list dd {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* ---- Two-up form row — collapses to a single column on narrow screens. ---- */
  .field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-4);
  }
  @media (max-width: 640px) {
    .field-row {
      grid-template-columns: 1fr;
    }
  }

  /* ---- Table — data renders in tabular figures (UI face; mono is provenance-only). ---- */
  .table {
    width: 100%;
    border-collapse: collapse;
    font: var(--type-body);
    font-variant-numeric: tabular-nums;
  }
  .table thead th {
    text-align: left;
    font: var(--type-label);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-secondary);
    background-color: var(--surface-sunken);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--border);
  }
  .table tbody td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
  }
  .table tr:last-child td {
    border-bottom: 0;
  }

  /* ---- Record lists collapse into a card per row below the chrome breakpoint — the same
     768px the sidebar and mobile nav flip at, so the page changes shape all at once.
     Opt-in (`.table-cards`), because the treatment only suits tables whose columns are
     KNOWN: an arbitrary-width result set read as N stacked label/value pairs per row is
     taller and harder to compare than scrolling it, so those keep `.scroll-x`.

     The label is a real `.cell-label` span in the cell, not `::before` on a `data-label`
     attribute: generated content is read inconsistently by screen readers, whereas a real
     span is `display: none` at wide widths (where `thead` carries the header) and visible at
     narrow (where `thead` is gone) — so exactly one of the two speaks at any width. ---- */
  .cell-label {
    display: none;
  }
  @media (max-width: 768px) {
    .table-cards thead {
      display: none;
    }
    /* The table itself goes with its children. Left as `display: table` it keeps its implicit
       role while the rows and cells lose theirs, so VoiceOver announces "table, N rows" and
       offers cell-navigation gestures that no longer land anywhere. */
    .table-cards,
    .table-cards tbody,
    .table-cards tr {
      display: block;
    }
    .table-cards tr {
      padding: var(--space-3) var(--space-4);
      border-bottom: 1px solid var(--border);
    }
    .table-cards tr:last-child {
      border-bottom: 0;
    }
    .table-cards tbody td {
      display: block;
      padding: var(--space-1) 0;
      border-bottom: 0;
    }
    .table-cards .cell-label {
      display: block;
      font: var(--type-label);
      letter-spacing: var(--tracking-label);
      text-transform: uppercase;
      color: var(--text-secondary);
    }
  }

  /* ---- Dashboard block grid — blocks flow into equal columns that adapt to the
     container; a block spans one cell (charts/tables read fine at ~17rem+). ---- */
  .block-grid {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  }

  /* ---- Spinner — token-colored; static ring under reduced motion (the copy beside it
     carries the meaning). ---- */
  .spinner {
    display: inline-block;
    width: var(--icon);
    height: var(--icon);
    border-radius: var(--chip-radius);
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    vertical-align: middle;
    animation: spinner-turn var(--duration-spin) linear infinite;
  }
  /* A class that sets `display` outranks the UA's `[hidden] { display: none }`, so a spinner
     toggled by the hidden attribute would otherwise spin forever in plain sight. */
  .spinner[hidden] {
    display: none;
  }
  @keyframes spinner-turn {
    to {
      transform: rotate(360deg);
    }
  }

  /* ---- Charts — server-rendered SVG marks, colored from tokens (token discipline holds
     inside SVG too). Missing data renders the 45° GAP hatch, never a fake zero. ---- */
  .chart-bar {
    fill: var(--chart-series);
  }
  .chart-line {
    stroke: var(--chart-series);
    fill: none;
  }
  .chart-point {
    fill: var(--chart-series);
  }
  .chart-grid {
    stroke: var(--chart-grid);
    stroke-width: 1;
  }
  .chart-zero {
    stroke: var(--border-strong);
    stroke-width: 1.5;
  }
  .chart-tick, .chart-axis-label {
    fill: var(--chart-axis);
    font: var(--type-small);
  }
  .chart-gap-stripe {
    stroke: var(--chart-gap);
    stroke-width: 4;
  }
}

@layer utilities {
  /* Token-valued layout/text utilities — deliberately small; anything richer belongs to a
     kit class. Spacing values come from the --space scale only. */
  .stack-1 { display: flex; flex-direction: column; gap: var(--space-1); }
  .stack-2 { display: flex; flex-direction: column; gap: var(--space-2); }
  .stack-3 { display: flex; flex-direction: column; gap: var(--space-3); }
  .stack-4 { display: flex; flex-direction: column; gap: var(--space-4); }
  .stack-5 { display: flex; flex-direction: column; gap: var(--space-5); }
  .stack-6 { display: flex; flex-direction: column; gap: var(--space-6); }
  .row { display: flex; align-items: center; gap: var(--space-3); }
  .row-2 { display: flex; align-items: center; gap: var(--space-2); }
  .row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
  .row-baseline { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
  .row-center { display: flex; align-items: center; justify-content: center; gap: var(--space-3); }
  .wrap { flex-wrap: wrap; }
  .grow { flex: 1; min-width: 0; }
  .shrink-0 { flex-shrink: 0; }

  .mt-1 { margin-top: var(--space-1); }
  .mt-2 { margin-top: var(--space-2); }
  .mt-3 { margin-top: var(--space-3); }
  .mt-4 { margin-top: var(--space-4); }
  .mt-5 { margin-top: var(--space-5); }
  .mt-6 { margin-top: var(--space-6); }
  .mb-1 { margin-bottom: var(--space-1); }
  .mb-2 { margin-bottom: var(--space-2); }
  .mb-3 { margin-bottom: var(--space-3); }
  .mb-4 { margin-bottom: var(--space-4); }
  .mb-5 { margin-bottom: var(--space-5); }
  .mb-6 { margin-bottom: var(--space-6); }
  .ml-auto { margin-left: auto; }
  .mt-auto { margin-top: auto; }

  .text-secondary { color: var(--text-secondary); }
  .text-faint { color: var(--text-faint); }
  .text-error { color: var(--error-text); }
  .text-warn { color: var(--warning-text); }
  .text-small { font: var(--type-small); }
  .text-center { text-align: center; }
  .font-medium { font-weight: 600; }
  .mono { font: var(--type-mono); }
  .numeric { text-align: right; font-variant-numeric: tabular-nums; }
  /* A numeric column has no right edge to align against once its row is a card, so the label and
     its value read against the card's left edge like every other pair. It lives here rather than
     beside the collapse in @layer elements because `.numeric` is a utility: a later layer wins
     however specific the earlier selector is. */
  @media (max-width: 768px) {
    .table-cards td.numeric { text-align: left; }
  }
  .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .break-anywhere { overflow-wrap: anywhere; }
  .max-w-prose { max-width: var(--prose); }
  .nowrap { white-space: nowrap; }

  .hidden { display: none; }
  .block { display: block; }
  .inline-block { display: inline-block; }
  .w-auto { width: auto; }
  .dimmed { opacity: 0.6; }
  .italic { font-style: italic; }
  .inset { border-left: 2px solid var(--border); padding-left: var(--space-4); }
  .divided > * + * { border-top: 1px solid var(--border); }
  .border-t { border-top: 1px solid var(--border); }
  .border-b { border-bottom: 1px solid var(--border); }
  .cell-tight { width: 1px; white-space: nowrap; }
  /* Shrink-to-fit only means something inside a table row; on a card the actions cell is a block
     that would otherwise be 1px wide with its button spilling out of it. */
  @media (max-width: 768px) {
    .table-cards td.cell-tight { width: auto; }
  }
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .scroll-x { overflow-x: auto; }
  .scroll-locked { overflow: hidden; } /* body scroll-lock while the drawer is open */

  .print-only { display: none; }
  @media print {
    .print-hidden { display: none; }
    .print-only { display: block; }
  }
}

/* Reduced motion — collapse transitions/animations to instant for users who ask for it.
   Covers the tactile press, disclosure reveals, drawer entrances, and anything later.
   (The one sanctioned !important use: it must beat every layered rule.) */
@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;
  }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation: none !important;
  }
}
