/* DeployBot UI, components layer: cards, tiles, grid tables, badges and status, buttons, forms,
   flash messages and banners, disclosures, the confirm dialog, the pipeline timeline, the log and
   the dependencies table and card. */

@layer components {
  /* ---- cards */

  /* Surfaces are solid: glass (a translucent fill with a backdrop blur over the ambient layer)
     is kept for the top bar and the deploy review panel only, so nothing nests glass in glass. */
  .card,
  .disclosure {
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow);
  }

  .card {
    border-radius: var(--radius-2xl);
    padding: 18px 20px;
  }

  /* nested surfaces stay quiet: no second blur layer */
  .disclosure > .card,
  .card .card {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: none;
  }

  .card.tbl-card {
    padding: 0;
    overflow: hidden;
    position: relative;
  }

  .card.tbl-card .scroll {
    overflow-x: auto;
  }

  .card.log-card {
    padding: 0;
    overflow: hidden;
  }

  .eyebrow {
    font-size: var(--text-1);
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--muted);
  }

  .card-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-6);
  }

  .card-head .note {
    font-size: var(--text-3);
    color: var(--muted);
  }

  .card-head form {
    margin: 0;
  }

  .card .foot {
    border-top: 1px solid var(--border);
    margin-top: var(--space-7);
    padding-top: var(--space-5);
  }

  .tbl-card > .card-head {
    padding: 12px 18px;
    margin-bottom: 0;
    border-bottom: 1px solid var(--border);
  }

  /* ---- grid tables */

  .tbl-head,
  .tbl-row {
    display: grid;
    gap: var(--space-4);
    align-items: center;
    padding: 9px 18px;
    border-bottom: 1px solid var(--border);
    min-width: 900px;
    font-size: var(--text-4);
  }

  .tbl-head {
    padding: 10px 18px;
    font-size: var(--text-1);
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--muted);
  }

  .tbl-row:last-child {
    border-bottom: 0;
  }

  .tbl-row:hover {
    background: var(--hover-fill);
  }

  .tbl-row > * {
    min-width: 0;
  }

  .tbl-row .actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
  }

  .tbl-row .actions form {
    margin: 0;
  }

  .tbl-checks .tbl-head,
  .tbl-checks .tbl-row {
    grid-template-columns: 20px 170px 1fr;
    gap: var(--space-5);
    min-width: 0;
  }

  .tbl-checks .tbl-row > :last-child {
    overflow-wrap: anywhere;
  }

  /* Settings: notification channels and their recent deliveries */
  .tbl-channels .tbl-row {
    grid-template-columns: minmax(0, 1fr) 130px 60px auto;
    gap: var(--space-5);
  }

  .tbl-channels .tbl-row > :last-child {
    justify-content: flex-end;
  }

  .tbl-channels .tbl-row form {
    margin: 0;
  }

  .tbl-deliveries .tbl-row {
    grid-template-columns: 90px 90px 1fr auto;
    gap: var(--space-5);
  }

  .tbl-backups .tbl-row {
    grid-template-columns: 110px 1fr 90px auto;
    gap: var(--space-5);
  }

  .tbl-backups .tbl-row form {
    margin: 0;
  }

  /* Settings: sign-in locks */
  .tbl-locks .tbl-row {
    grid-template-columns: 80px minmax(0, 1fr) 140px auto;
    gap: var(--space-5);
  }

  /* the Settings tables sit in a card of their tab: they fit it rather than scroll, and on a
     phone the last column (actions, the outcome) takes a line of its own */
  :is(.tbl-channels, .tbl-deliveries, .tbl-backups, .tbl-locks) .tbl-row {
    min-width: 0;
  }

  @media (max-width: 640px) {
    :is(.tbl-channels, .tbl-deliveries, .tbl-backups, .tbl-locks) .tbl-row {
      grid-template-columns: minmax(0, 1fr) auto;
      gap: var(--space-2) var(--space-4);
    }

    :is(.tbl-channels, .tbl-deliveries, .tbl-backups, .tbl-locks) .tbl-row > :last-child {
      grid-column: 1 / -1;
      justify-content: flex-start;
    }
  }

  .tbl-locks .tbl-row form {
    margin: 0;
  }

  /* a section locked while config.json cannot be read */
  fieldset:disabled {
    opacity: .55;
  }

  .tbl-deps .tbl-head,
  .tbl-deps .tbl-row {
    grid-template-columns: minmax(140px, 1.4fr) 90px 90px 110px 90px 110px;
    min-width: 700px;
  }

  .tbl-import .tbl-head,
  .tbl-import .tbl-row {
    grid-template-columns: 28px minmax(150px, 1.2fr) minmax(130px, 1fr) minmax(160px, 1.3fr) 110px minmax(130px, 1fr) 112px minmax(150px, 1fr);
    min-width: 1170px;
    align-items: start;
  }

  .tbl-import input:not([type=checkbox]),
  .tbl-import select {
    height: 28px;
    font-size: var(--text-3);
    padding: 0 var(--space-3);
    margin-bottom: var(--space-1);
  }

  .tbl-import .row-error {
    background: color-mix(in oklch, var(--fail) 6%, transparent);
  }

  /* a folder whose details are still being read from git (deploybot.js loadImportRows) */
  .tbl-import .wait {
    grid-column: 3 / -1;
    align-self: center;
  }

  .tbl-import .is-failed .wait {
    color: var(--fail);
    font-size: var(--text-3);
  }

  .tbl-import .err {
    display: block;
    color: var(--fail);
    font-size: var(--text-3);
  }

  .tbl-row .mono,
  .tbl-row code {
    font-size: var(--text-3);
  }

  .badges {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    min-width: 0;
  }

  /* safety net: a badge never pushes past its cell, even if a long label slips through */
  .badges .badge {
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  .diag {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    user-select: text;
  }

  .target .type {
    color: var(--muted);
  }

  /* ---- badges + status */

  /* the tone classes set --tone once, on the element that carries them (badges, status words,
     outcome and check marks, timeline and job steps, flash messages); each family reads it */
  .ok {
    --tone: var(--ok);
  }

  .warn {
    --tone: var(--warn);
  }

  .fail {
    --tone: var(--fail);
  }

  .info {
    --tone: var(--info);
  }

  .skip {
    --tone: var(--skip);
  }

  .prod {
    --tone: var(--prod);
  }

  .accent {
    --tone: var(--accent);
  }

  .badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    font-size: var(--text-3);
    font-weight: 600;
    white-space: nowrap;
    line-height: 1.4;
    background: color-mix(in oklch, var(--muted) 16%, transparent);
    color: var(--muted);
    text-decoration: none;
  }

  a.badge:hover {
    text-decoration: none;
    filter: brightness(1.1);
  }

  /* a status pill without a tone (a disabled site's) takes the plain link hover colour. Before
     the layers a:hover outranked .badge on specificity; components now outrank base, so it is
     restated here */
  a.badge:not(.ok, .warn, .fail, .info, .skip, .prod, .accent):hover {
    color: var(--text);
  }

  .badge:is(.ok, .warn, .fail, .info, .skip, .prod, .accent) {
    background: color-mix(in oklch, var(--tone) 16%, transparent);
    color: var(--tone);
  }

  /* the brand amber is a fill colour; as text it is --accent-text */
  .badge.accent {
    color: var(--accent-text);
  }

  .badge-lg {
    padding: 3px 10px;
    font-size: var(--text-5);
  }

  .badge-xl {
    padding: 4px 11px;
    font-size: var(--text-4);
  }

  .badge.outline {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    font-weight: 600;
    font-size: var(--text-1);
    padding: 1px 7px;
  }

  .badge .dot {
    width: 7px;
    height: 7px;
  }

  .status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
  }

  .status:is(.ok, .warn, .fail, .info, .skip) {
    color: var(--tone);
  }

  a.status:hover {
    text-decoration: underline;
    color: inherit;
  }

  .dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
  }

  .status.info .dot,
  .dot.pulse {
    animation: db-pulse 1.2s ease-in-out infinite;
  }

  .status-lg {
    font-size: 15px;
  }

  .summary-ok {
    color: var(--ok);
    font-size: var(--text-3);
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
  }

  .summary-fail {
    color: var(--fail);
    font-size: var(--text-3);
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
  }

  .check-mark {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    font-size: 10px;
    font-weight: 700;
  }

  .check-mark:is(.ok, .fail, .warn) {
    background: color-mix(in oklch, var(--tone) 18%, transparent);
    color: var(--tone);
  }

  /* ---- buttons */

  .btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: 34px;
    padding: 0 var(--space-6);
    border-radius: 9px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-5);
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;

    &:hover {
      background: var(--raised);
      color: var(--text);
      text-decoration: none;
    }

    &:disabled {
      opacity: .5;
      cursor: default;
      pointer-events: none;
    }
  }

  .btn-primary {
    height: 38px;
    padding: 0 18px;
    border-radius: var(--radius-lg);
    border-color: transparent;
    background: var(--accent);
    color: var(--accent-ink);
    font-size: var(--text-7);
    font-weight: 600;

    &:hover {
      background: var(--accent);
      color: var(--accent-ink);
      filter: brightness(1.08);
    }

    &.tall {
      height: 42px;
    }
  }

  .btn-block {
    width: 100%;
  }

  .btn-small {
    height: 28px;
    padding: 0 var(--space-4);
    border-radius: 7px;
    font-size: var(--text-3);
    color: var(--muted);

    &:hover {
      color: var(--text);
      border-color: var(--muted);
      background: var(--surface);
    }
  }

  /* .btn-small comes after the filled buttons: keep their ink (it set muted text on amber) */
  .btn-small.btn-primary,
  .btn-small.btn-accent,
  .btn-small.btn-primary:hover,
  .btn-small.btn-accent:hover {
    color: var(--accent-ink);
    background: var(--accent);
    border-color: transparent;
  }

  .btn-xs {
    height: 26px;
    padding: 0 var(--space-4);
    border-radius: var(--radius-sm);
    font-size: var(--text-3);
    color: var(--muted);

    &:hover {
      color: var(--text);
      border-color: var(--muted);
      background: var(--surface);
    }
  }

  .btn-warn {
    border-color: color-mix(in oklch, var(--warn) 50%, transparent);
    color: var(--warn);
    font-weight: 600;

    &:hover {
      background: color-mix(in oklch, var(--warn) 12%, transparent);
      color: var(--warn);
    }
  }

  .btn-danger {
    border-color: color-mix(in oklch, var(--fail) 55%, transparent);
    color: var(--fail);
    font-weight: 600;

    &:hover {
      background: color-mix(in oklch, var(--fail) 12%, transparent);
      color: var(--fail);
    }
  }

  .btn-fill-warn {
    background: var(--warn);
    border-color: transparent;
    color: var(--fill-ink);
    font-weight: 600;

    &:hover {
      background: var(--warn);
      color: var(--fill-ink);
      filter: brightness(1.08);
    }
  }

  .btn-fill-danger {
    background: var(--fail);
    border-color: transparent;
    color: var(--fill-ink);
    font-weight: 600;

    &:hover {
      background: var(--fail);
      color: var(--fill-ink);
      filter: brightness(1.08);
    }
  }

  .btn-accent {
    background: var(--accent);
    border-color: transparent;
    color: var(--accent-ink);
    font-weight: 600;

    &:hover {
      background: var(--accent);
      color: var(--accent-ink);
      filter: brightness(1.08);
    }
  }

  /* the one filled amber action in the top bar (Add site). .btn.btn-glow outranks .btn-small and
     .btn-small:hover whatever the rule order. */
  .btn.btn-glow {
    height: 32px;
    padding: 0 var(--space-5) 0 var(--space-4);
    border-radius: 9px;
    font-size: var(--text-5);
    font-weight: 600;
    color: var(--accent-ink);
    border: 1px solid color-mix(in oklch, var(--accent) 70%, white);
    background: linear-gradient(180deg, color-mix(in oklch, var(--accent) 92%, white), color-mix(in oklch, var(--accent) 88%, oklch(0.5 0.15 50)));
    box-shadow: 0 0 0 1px rgba(0,0,0,.2), 0 6px 20px -6px color-mix(in oklch, var(--accent) 55%, transparent), inset 0 1px 0 rgba(255,255,255,.35);

    &:hover {
      color: var(--accent-ink);
      border-color: color-mix(in oklch, var(--accent) 70%, white);
      background: linear-gradient(180deg, color-mix(in oklch, var(--accent) 92%, white), color-mix(in oklch, var(--accent) 88%, oklch(0.5 0.15 50)));
      filter: brightness(1.06);
    }
  }

  .btn.btn-ghost {
    background: transparent;
    border-color: var(--glass-border);

    &:hover {
      background: var(--hover-fill);
    }
  }

  .btn-small.hover-danger:hover,
  .btn-xs.hover-danger:hover {
    color: var(--fail);
    border-color: var(--fail);
  }

  .btn[aria-busy="true"]::before {
    content: "";
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid var(--muted);
    border-top-color: transparent;
    animation: db-spin .8s linear infinite;
  }

  .btn-primary[aria-busy="true"]::before {
    border-color: var(--accent-ink);
    border-top-color: transparent;
  }

  small.error {
    display: block;
    margin-top: var(--space-2);
    font-size: var(--text-3);
    color: var(--fail);
  }

  .tbl-row small.error {
    white-space: normal;
  }

  /* ---- forms */

  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea {
    width: 100%;
    height: 38px;
    padding: 0 var(--space-5);
    border-radius: 9px;
    border: 1px solid var(--glass-border);
    background: color-mix(in oklch, var(--surface) 50%, transparent);
    color: var(--text);
    font-size: var(--text-6);
    line-height: 1.4;
  }

  input:not([type="checkbox"]):not([type="radio"]):disabled,
  select:disabled,
  textarea:disabled {
    opacity: .6;
  }

  textarea {
    height: auto;
    min-height: 76px;
    padding: var(--space-3) var(--space-5);
    resize: vertical;
  }

  textarea.pubkey {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    line-height: 1.5;
    overflow-wrap: anywhere;
    min-height: 64px;
    color: var(--muted);
  }

  input.mono,
  select.mono,
  textarea.mono {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-5);
  }

  input[type="checkbox"],
  input[type="radio"] {
    accent-color: var(--accent);
    width: 14px;
    height: 14px;
    margin: 0;
    flex: none;
  }

  .field {
    display: block;
    margin-bottom: var(--space-6);
  }

  .field > .label {
    display: block;
    font-size: var(--text-4);
    font-weight: 600;
    color: var(--muted);
    margin-bottom: var(--space-2);
  }

  .field > .label small {
    font-weight: 400;
  }

  .field .help,
  .help {
    display: block;
    font-size: var(--text-3);
    color: var(--muted);
    margin-top: var(--space-2);
    line-height: 1.5;
  }

  .help code {
    font-size: var(--text-2);
  }

  /* an input with buttons beside it (the site edit page's reload password: Show, Copy) */
  .secret-line {
    display: flex;
    gap: var(--space-3);
    align-items: center;
  }

  .secret-line input {
    flex: 1;
    min-width: 0;
  }

  .field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
    gap: 0 var(--space-7);
  }

  .check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    font-size: var(--text-5);
    margin-bottom: var(--space-4);
    line-height: 1.45;
    cursor: pointer;
  }

  .check input {
    margin-top: 3px;
  }

  .check .help {
    margin-top: 2px;
  }

  .check-group {
    margin-bottom: var(--space-6);
  }

  .form-actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-top: var(--space-3);
  }

  .form-actions .note {
    font-size: var(--text-3);
    color: var(--muted);
  }

  .auth input:not([type="checkbox"]):not([type="radio"]) {
    height: 40px;
  }

  /* segmented control: real radios hidden inside the labels */
  .seg {
    display: inline-flex;
    padding: 3px;
    border-radius: 9px;
    background: var(--well);
    border: 1px solid var(--border);
    margin-bottom: var(--space-6);
  }

  .seg label {
    position: relative;
    padding: 5px 14px;
    border-radius: 7px;
    font-size: var(--text-5);
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap;
  }

  .seg label:has(input:checked) {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow);
  }

  .seg label:has(input:focus-visible) {
    outline: 2px solid var(--focus);
    outline-offset: 1px;
  }

  .seg label:has(input:disabled) {
    opacity: .5;
    cursor: default;
  }

  .seg input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
  }

  /* switch: a real checkbox */
  .switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
    font-size: var(--text-5);
    font-weight: 500;
    color: var(--muted);
  }

  .switch input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
  }

  .switch .track {
    position: relative;
    width: 30px;
    height: 18px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--glass-border);
    background: color-mix(in oklch, var(--text) 12%, transparent);
    transition: background .2s;
    flex: none;
  }

  .switch .track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,.3);
    transition: left .2s;
  }

  .switch:has(input:checked) .track {
    background: var(--ok);
  }

  .switch:has(input:checked) .track::after {
    left: 14px;
  }

  /* a view preference (dashboard filters): amber, not the green of "on" */
  .switch-pref {
    white-space: nowrap;
  }

  .switch-pref:has(input:checked) .track {
    background: var(--accent);
  }

  .switch:has(input:focus-visible) .track {
    outline: 2px solid var(--focus);
    outline-offset: 1px;
  }

  .switch .on,
  .switch .off {
    display: none;
  }

  .switch:has(input:checked) .on {
    display: inline;
    color: var(--ok);
  }

  .switch:not(:has(input:checked)) .off {
    display: inline;
  }

  /* toggle chips (skip steps) and tag chips */
  .chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-3) 0;
  }

  .chip-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-4);
    border-radius: 7px;
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
    color: var(--muted);
    cursor: pointer;
  }

  .chip-toggle .type {
    opacity: .6;
  }

  .chip-toggle input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
  }

  .chip-toggle:has(input:checked) {
    border-color: var(--warn);
    color: var(--warn);
    background: color-mix(in oklch, var(--warn) 16%, transparent);
  }

  .chip-toggle:has(input:focus-visible) {
    outline: 2px solid var(--focus);
    outline-offset: 1px;
  }

  .tag {
    appearance: none;
    display: inline-block;
    padding: 1px 7px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    cursor: pointer;
    line-height: 1.5;

    &:hover {
      border-color: var(--accent);
      color: var(--accent-text);
    }
  }

  .tags {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
  }

  .tags .lbl {
    font-size: var(--text-4);
    color: var(--muted);
  }

  /* ---- flash, banners, reveal */

  .flash {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    padding: var(--space-4) var(--space-6);
    border-radius: var(--radius-lg);
    font-size: var(--text-5);
    margin-bottom: var(--space-8);
    border: 1px solid;
  }

  .flash::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
    background: currentColor;
  }

  .flash:is(.success, .info, .warn, .error) {
    background: color-mix(in oklch, var(--tone) 13%, transparent);
    border-color: color-mix(in oklch, var(--tone) 35%, transparent);
  }

  .flash:is(.success, .info, .warn, .error)::before {
    color: var(--tone);
  }

  /* .info and .warn take --tone from the tone classes; these two message types are named apart */
  .flash.success {
    --tone: var(--ok);
  }

  .flash.error {
    --tone: var(--fail);
  }

  .flash .body {
    flex: 1;
    min-width: 0;
  }

  .flash .body ul {
    margin-top: var(--space-1);
    padding-left: 18px;
  }

  .flash .x {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
  }

  .flash .x:hover {
    color: var(--text);
  }

  .banner {
    padding: var(--space-5) var(--space-7);
    border-radius: 12px;
    font-size: var(--text-5);
    margin-bottom: 18px;
    border: 1px solid;
    display: flex;
    gap: var(--space-4);
    align-items: center;
    flex-wrap: wrap;
  }

  .banner .grow {
    flex: 1;
    min-width: 0;
  }

  .banner.block {
    display: block;
  }

  .banner.block p + p {
    margin-top: var(--space-3);
  }

  .banner.block form {
    margin-top: var(--space-3);
  }

  :is(.banner-info, .banner-ok, .banner-warn, .banner-fail, .banner-skip) {
    background: color-mix(in oklch, var(--tone) 12%, transparent);
    border-color: color-mix(in oklch, var(--tone) 35%, transparent);
  }

  .banner-info {
    --tone: var(--info);
  }

  .banner-ok {
    --tone: var(--ok);
  }

  .banner-warn {
    --tone: var(--warn);
  }

  .banner-fail {
    --tone: var(--fail);
  }

  .banner-skip {
    --tone: var(--skip);
  }

  .banner.mono {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-5);
  }

  .banner a.strong {
    font-weight: 600;
  }

  .banner-info a.strong {
    color: var(--info);
  }

  .busy-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--info);
    animation: db-pulse 1.2s ease-in-out infinite;
    flex: none;
  }

  .reveal {
    background: color-mix(in oklch, var(--accent) 12%, transparent);
    border: 1px solid color-mix(in oklch, var(--accent) 40%, transparent);
    border-radius: var(--radius-lg);
    padding: var(--space-5) var(--space-6);
    margin-bottom: var(--space-6);
  }

  .reveal .title {
    font-size: var(--text-4);
    font-weight: 600;
    margin-bottom: var(--space-3);
  }

  .reveal .line {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    margin-bottom: var(--space-2);
  }

  .reveal .line input {
    height: 34px;
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
  }

  .reveal .lbl {
    font-size: var(--text-3);
    font-weight: 600;
    color: var(--muted);
    margin: var(--space-3) 0 var(--space-1);
  }

  .reveal .hint {
    font-size: var(--text-2);
    color: var(--muted);
    margin-top: var(--space-1);
  }

  /* ---- details */

  details.plain > summary {
    font-size: var(--text-4);
    font-weight: 500;
    color: var(--muted);
  }

  details.plain > summary:hover {
    color: var(--text);
  }

  details.plain[open] > summary {
    margin-bottom: var(--space-4);
  }

  details.plain .badge {
    margin-left: var(--space-2);
  }

  details.help-text {
    margin-top: var(--space-6);
  }

  details.help-text ol {
    padding-left: var(--space-8);
    font-size: var(--text-4);
    color: var(--muted);
    line-height: 1.7;
  }

  details.help-text p {
    font-size: var(--text-3);
    color: var(--muted);
    margin-top: var(--space-4);
  }

  details.help-text .pre {
    margin-top: var(--space-5);
  }

  details.section {
    margin-bottom: var(--space-6);
  }

  details.section > summary {
    font-size: var(--text-5);
    font-weight: 600;
    padding: var(--space-3) 0;
  }

  /* ---- confirm modal */

  dialog.confirm {
    max-width: 440px;
    width: calc(100% - 32px);
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-2xl);
    padding: 22px 24px;
    box-shadow: 0 30px 80px rgba(0,0,0,.4);
    animation: db-rise .2s ease both;
  }

  dialog.confirm::backdrop {
    background: rgba(0,0,0,.45);
    backdrop-filter: blur(3px);
  }

  dialog.confirm h2 {
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -.015em;
    margin-bottom: var(--space-3);
  }

  dialog.confirm .body {
    font-size: var(--text-6);
    color: var(--muted);
    line-height: 1.55;
  }

  dialog.confirm .actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-3);
    margin-top: 18px;
  }

  dialog.confirm .actions .btn {
    height: 36px;
  }

  /* ---- pipeline timeline */

  .timeline {
    display: flex;
    flex-direction: column;
  }

  .step {
    display: grid;
    grid-template-columns: 22px 1fr;
    gap: var(--space-5);
  }

  .step-rail {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .step-mark {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 1.5px solid var(--stone);
    background: color-mix(in oklch, var(--stone) 18%, transparent);
    color: var(--stone);
    display: inline-grid;
    place-items: center;
    font-size: var(--text-1);
    font-weight: 700;
    flex: none;
    --stone: var(--faint);
  }

  .step-line {
    flex: 1;
    width: 1.5px;
    background: color-mix(in oklch, var(--stone) 50%, var(--border));
    margin: 3px 0;
    --stone: var(--border);
  }

  .step:last-child .step-line {
    visibility: hidden;
  }

  .step:is(.ok, .warn, .fail, .info, .skip) .step-mark,
  .step:is(.ok, .warn, .fail, .info, .skip) .step-line {
    --stone: var(--tone);
  }

  .step.info .step-mark {
    animation: db-pulse 1.2s ease-in-out infinite;
  }

  /* a warning, partial or interrupted step: the caution triangle instead of a ringed glyph
     (stepGlyph(), format.js stepMark()); the outcome mark of pages.css draws it too */
  .step-mark:has(svg.caution) {
    border: 0;
    border-radius: 0;
    background: none;
  }

  svg.caution {
    display: block;
    width: 100%;
    height: 100%;
  }

  svg.caution .tri {
    fill: var(--caution);
    stroke: var(--caution);
    stroke-width: 2;
    stroke-linejoin: round;
  }

  svg.caution .bang {
    fill: none;
    stroke: var(--caution-ink);
    stroke-width: 1.8;
    stroke-linecap: round;
  }

  .step-body {
    padding-bottom: var(--space-6);
    min-width: 0;
  }

  .step-title {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-4);
  }

  .step-title .id {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-5);
    font-weight: 600;
  }

  .step-title .type {
    font-size: var(--text-2);
    color: var(--muted);
    margin-left: var(--space-2);
  }

  .step-title .dur {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    color: var(--muted);
    white-space: nowrap;
  }

  .step-sum {
    font-size: var(--text-4);
    color: var(--muted);
    margin-top: 2px;
    overflow-wrap: anywhere;
  }

  .step.fail .step-sum {
    color: var(--fail);
  }

  .step.pending .step-sum {
    color: var(--faint);
  }

  details.step-details {
    margin-top: var(--space-1);
  }

  details.step-details summary {
    font-size: var(--text-2);
    color: var(--muted);
  }

  details.step-details dl.meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 2px 14px;
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    margin: var(--space-2) 0;
  }

  details.step-details dl.meta dt {
    color: var(--muted);
  }

  details.step-details dl.meta dd {
    overflow-wrap: anywhere;
  }

  a.jump-to-log {
    font-size: var(--text-2);
  }

  .timeline-empty {
    font-size: var(--text-4);
    color: var(--muted);
  }

  .card .foot.kv {
    display: grid;
  }

  /* ---- log */

  .log-head {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding: var(--space-5) var(--space-7);
    border-bottom: 1px solid var(--border);
  }

  .log-head .file {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    color: var(--muted);
  }

  .log-head .tools {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
  }

  .live {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-2);
    font-weight: 600;
    color: var(--info);
  }

  .live .dot {
    animation: db-pulse 1.2s ease-in-out infinite;
  }

  .log-body {
    position: relative;
    max-height: 640px;
    overflow: auto;
    padding: var(--space-5) 0;
    background: var(--bg);
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
    line-height: 1.6;
  }

  .log-line {
    display: grid;
    grid-template-columns: 36px 64px 72px minmax(0, 1fr);
    gap: var(--space-4);
    padding: 0 var(--space-7);

    &:hover {
      background: var(--raised);
    }
  }

  .log-line .ln {
    text-align: right;
    color: var(--faint);
    user-select: none;
  }

  .log-line .lt {
    color: var(--faint);
  }

  .log-line .ls {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--muted);
  }

  .log-line .lx {
    overflow-wrap: anywhere;
    white-space: pre-wrap;
  }

  /* Wrap: off. The body scrolls sideways as one: its single column is as wide as the longest
     line (never narrower than the body), every line stretches to it, and the text column takes
     its content's width instead of scrolling inside each line. */
  .log-body.nowrap {
    display: grid;
    grid-template-columns: minmax(100%, max-content);
    align-content: start;
  }

  .log-body.nowrap .log-line {
    grid-template-columns: 36px 64px 72px max-content;
  }

  .log-body.nowrap .lx {
    white-space: pre;
    overflow-wrap: normal;
  }

  .log-line.log-header .lx {
    color: var(--muted);
    font-weight: 600;
  }

  .log-line.is-fail .lx {
    color: var(--fail);
  }

  .log-line.is-fail {
    background: color-mix(in oklch, var(--fail) 10%, transparent);
  }

  .log-line.is-ok .lx {
    color: var(--ok);
  }

  .log-line.is-flash {
    outline: 2px solid var(--info);
    outline-offset: -2px;
  }

  .log-caret {
    color: var(--info);
    padding: 0 16px 0 198px;
  }

  pre.log-raw {
    padding: var(--space-5) var(--space-7);
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
    line-height: 1.6;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    max-height: 640px;
    overflow: auto;
    background: var(--bg);
  }

  /* ---- forms: validation errors */

  /* A form field that failed validation (prc.errorFields via fieldError() in the view) */
  .field.field-error > .label {
    color: var(--fail);
  }

  .field.field-error input,
  .field.field-error textarea,
  .field.field-error select {
    border-color: var(--fail);
    box-shadow: 0 0 0 1px var(--fail);
  }

  /* ---- flash: messages shown in place */

  .flash.flash-inline {
    margin: 12px 18px 0;
  }

  .flash:focus {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
  }

  /* ---- dependencies table */

  /* Dependencies table: nested packages are indented by depth */
  .tbl-deps .depth-2 .dep-name {
    padding-left: var(--space-6);
  }

  .tbl-deps .depth-3 .dep-name {
    padding-left: 28px;
  }

  .tbl-deps .depth-4 .dep-name {
    padding-left: 42px;
  }

  /* admin view: a checkbox column in front (the rows `box update slug=` can be asked for) */
  .tbl-deps.tbl-deps-pick .tbl-head,
  .tbl-deps.tbl-deps-pick .tbl-row {
    grid-template-columns: 16px minmax(140px, 1.4fr) 90px 90px 110px 90px 110px;
    min-width: 730px;
  }

  .tbl-deps-pick input[type="checkbox"] {
    display: block;
  }

  .deps-result {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
    color: var(--muted);
  }

  .deps-result .status {
    font-size: var(--text-4);
  }

  /* Dependencies card: a running update / permissions reset, followed in place */
  .job-panel {
    border: 1px solid color-mix(in oklch, var(--info) 35%, transparent);
    background: color-mix(in oklch, var(--info) 7%, transparent);
    border-radius: 12px;
    padding: var(--space-5) var(--space-6);
    margin-bottom: var(--space-6);

    &:is(.is-ok, .is-warn, .is-fail) {
      border-color: color-mix(in oklch, var(--tone) 40%, transparent);
      background: color-mix(in oklch, var(--tone) 7%, transparent);
    }

    &.is-ok {
      --tone: var(--ok);
    }

    &.is-warn {
      --tone: var(--warn);
    }

    &.is-fail {
      --tone: var(--fail);
    }
  }

  .job-head {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    font-size: var(--text-5);
  }

  .job-head .grow {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* ---- a pipeline's steps in order (ol.flow): an arrow between each step and the next. The
     arrow is drawn in the step's left margin, not in the flex gap, so a step that wraps to a new
     line still has its arrow leading it. Pure decoration (the <ol> carries the order). */

  .flow {
    --flow-gap: 26px;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0;
    padding: 0;
  }

  .flow > li {
    position: relative;
  }

  .flow > li + li {
    margin-left: var(--flow-gap);
  }

  /* the shaft, then the head: a chevron of two borders turned 45 degrees */
  .flow > li + li::before,
  .flow > li + li::after {
    content: "";
    position: absolute;
    top: 50%;
    pointer-events: none;
  }

  .flow > li + li::before {
    left: calc(-1 * var(--flow-gap) + 6px);
    width: calc(var(--flow-gap) - 12px);
    height: 1.5px;
    margin-top: -0.75px;
    background: var(--faint);
  }

  .flow > li + li::after {
    left: -12px;
    width: 6px;
    height: 6px;
    border-top: 1.5px solid var(--faint);
    border-right: 1.5px solid var(--faint);
    transform: translateY(-50%) rotate(45deg);
  }

  @media (forced-colors: active) {
    .flow > li + li::before {
      background: CanvasText;
    }

    .flow > li + li::after {
      border-color: CanvasText;
    }
  }

  .job-steps {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    row-gap: var(--space-2);
    margin: var(--space-4) 0 var(--space-3);
    padding: 0;
  }

  .job-step {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-3);
    min-width: 0;
  }

  .job-step .step-mark {
    width: 18px;
    height: 18px;
    font-size: 10px;
  }

  .job-step .id {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-weight: 600;
  }

  .job-step .sum {
    color: var(--muted);
    overflow-wrap: anywhere;
  }

  .job-step.pending .id {
    color: var(--faint);
  }

  .job-step:is(.ok, .warn, .fail, .info, .skip) .step-mark {
    --stone: var(--tone);
  }

  .job-step.fail .sum {
    color: var(--fail);
  }

  .job-step.info .step-mark {
    animation: db-pulse 1.2s ease-in-out infinite;
  }

  .job-output[open] > summary {
    margin-bottom: var(--space-2);
  }

  .job-log {
    max-height: 280px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
  }

  .job-panel .help {
    margin-top: var(--space-3);
  }

  /* ---- status pill: a glyph and a label on the tone's tint (statusPill()); the glyph always
     comes with the colour, and `.more` is the "+1" for a second reason */

  .pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 24px;
    padding: 1px var(--space-4) 1px var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--text-5);
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    text-decoration: none;
    color: var(--muted);
    background: color-mix(in oklch, var(--muted) 12%, transparent);
  }

  .pill:is(.ok, .warn, .fail, .info, .skip) {
    color: var(--tone);
    background: color-mix(in oklch, var(--tone) 12%, transparent);
  }

  a.pill:hover {
    text-decoration: none;
    filter: brightness(1.1);
  }

  .pill .glyph {
    font-size: var(--text-3);
    line-height: 1;
  }

  .pill.info .glyph.running {
    animation: db-pulse 1.2s ease-in-out infinite;
  }

  .pill .more {
    padding: 0 5px;
    border-radius: var(--radius-pill);
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
    background: color-mix(in oklch, currentColor 14%, transparent);
  }

  .pill-lg {
    min-height: 28px;
    font-size: var(--text-6);
  }

  /* ---- environment label: Production / Staging, outlined (envLabel()) */

  .env-label {
    display: inline-flex;
    align-items: center;
    padding: 1px 6px;
    border: 1px solid currentColor;
    border-radius: 5px;
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-2);
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    line-height: 1.4;
    white-space: nowrap;
  }

  .env-label.production {
    color: var(--prod);
  }

  .env-label.staging {
    color: var(--info);
  }

  /* ---- filter chips: links (aria-current marks the active one) with a count */

  .chip-filter {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 32px;
    padding: 4px 12px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-5);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
  }

  .chip-filter:hover {
    color: var(--text);
    text-decoration: none;
    border-color: color-mix(in oklch, var(--accent) 50%, var(--border));
  }

  .chip-filter .count {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-3);
    color: var(--muted);
  }

  .chip-filter[aria-current] {
    border-color: var(--accent);
    background: color-mix(in oklch, var(--accent) 16%, var(--surface));
  }

  .chip-filter[aria-current] .count {
    color: var(--text);
  }

  /* ---- keyboard hint and consequence note */

  .kbd {
    display: inline-block;
    min-width: 18px;
    padding: 0 4px;
    border: 1px solid color-mix(in oklch, currentColor 35%, transparent);
    border-radius: 4px;
    font-family: var(--font-mono);
    font-variant-ligatures: none;
    font-feature-settings: "calt" 0;
    font-size: var(--text-1);
    font-weight: 600;
    line-height: 16px;
    text-align: center;
    opacity: .85;
  }

  /* the hints go when the shortcuts are switched off (shortcuts.js) */
  html.no-shortcuts .kbd {
    display: none;
  }

  /* ---- the deploy review modal: a <dialog> centred over the page (deploy-panel.js). The body
     scrolls between the review's head and foot, which stick to it as its header and footer bars
     (pages.css); the close button sits on the dialog, outside the scroll. */

  dialog.modal {
    width: min(1040px, calc(100vw - 32px));
    max-width: none;
    max-height: 88dvh;
    margin: auto;
    padding: 0;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-2xl);
    box-shadow: 0 30px 80px rgba(0,0,0,.4);
    overflow: hidden;
  }

  /* display only while open: a closed <dialog> must stay hidden */
  dialog.modal[open] {
    display: flex;
    flex-direction: column;
    animation: db-rise .2s ease both;
  }

  dialog.modal::backdrop {
    background: rgba(18, 16, 24, .32);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }

  .modal-close {
    position: absolute;
    top: var(--space-5);
    right: var(--space-5);
    z-index: 2;
    font-size: 18px;
  }

  .modal-body {
    flex: 1;
    min-height: 0;
    padding: 0 var(--space-9);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .modal-body .review {
    max-width: none;
  }

  .modal-body h1:focus {
    outline: none;
  }

  /* a phone gets the whole screen */
  @media (max-width: 767px) {
    dialog.modal {
      width: 100vw;
      height: 100dvh;
      max-height: none;
      border: 0;
      border-radius: 0;
    }
  }

  /* what pressing Save does, beside the button */
  .consequence {
    font-size: var(--text-5);
    color: var(--muted);
  }
}
