@font-face {
  font-family: "Inter";
  src: url("./Inter-Variable.ttf") format("truetype");
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
}
:root {
  /* The theme toggle sets color-scheme on this element, so every
     light-dark() below switches with it. Left at 'light dark' the
     browser follows the device, which stays the default. */
  color-scheme: light dark;
  font: 15px/1.5 "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --accent: #FD6585;
  --accent-hover: #ff809b;
  --accent-text: light-dark(#a72d50, #ff94ac);
  --on-accent: #29131d;
  --text: light-dark(#2d2530, #f3eaf0);
  --background: light-dark(#f8f6f8, #18151c);
  --surface: light-dark(#fff, #231f28);
  --surface-subtle: light-dark(#fbf9fb, #29242e);
  --surface-hover: light-dark(#f5edf1, #352b37);
  --border: light-dark(#e5dce2, #463a48);
  --border-subtle: light-dark(#efe8ed, #352d3a);
  --control-border: light-dark(#b9aab4, #786679);
  --muted: light-dark(#70616b, #bcadbc);
  --selected: light-dark(#fff0f4, #432735);
  --selected-border: light-dark(#da5676, var(--accent));
  --badge: light-dark(#f0eaf0, #3a303e);
  --bay-line: light-dark(#cdc6b0, #7a7360);
  --bay-empty: light-dark(#dcd9d4, #39373a);
  --pavement: light-dark(#e7e5e1, #35343a);
  --pavement-edge: light-dark(#cfccc6, #46454c);
  --pavement-lane: light-dark(#f1efec, #2e2d33);
  --status-running-text: light-dark(#17633b, #8ed9ac);
  --status-running-background: light-dark(#e7f6ed, #1f3b2d);
  --status-running-border: light-dark(#98d4ae, #417d59);
  --status-failed-text: light-dark(#9d2935, #ffadb6);
  --status-failed-background: light-dark(#fff0f0, #42252f);
  --status-failed-border: light-dark(#e5abb0, #a36170);
  --status-pending-text: light-dark(#7a4b00, #ffd179);
  --status-pending-background: light-dark(#fff4d6, #463719);
  --status-pending-border: light-dark(#e3c06b, #8f6e2a);
  --info-background: light-dark(#fff0f4, #382530);
  --info-border: light-dark(#e7b4c2, #865065);
  --error-text: light-dark(#9d2935, #ffadb6);
  --error-background: light-dark(#fff0f0, #42252f);
  --error-border: light-dark(#e5abb0, #a36170);
  --danger-text: light-dark(#9d2935, #ffadb6);
  --danger-background: light-dark(#fff0f0, #42252f);
  --danger-hover: light-dark(#ffe3e5, #54303b);
  --danger-border: light-dark(#d76d78, #a36170);
  --backdrop: light-dark(rgb(25 15 24 / 55%), rgb(8 5 12 / 75%));
  /* For text sitting on a band's own colour (the zoomed-out skeleton),
     not on the neutral surface every other status colour above assumes.
     Fixed rather than light-dark(): the band colour is the same tint of
     the same hue in both themes, so the readable text on it doesn't need
     to change with the theme either. */
  --zone-band-on-low: #ffffff;
  --zone-band-on-medium: #241900;
  --zone-band-on-high: #2a0f1a;
  --zone-band-on-full: #ffffff;
  color: var(--text);
  background: var(--background);
}
* { box-sizing: border-box; }
body { margin: 0; }
[hidden] { display: none !important; }
a { color: var(--accent-text); text-underline-offset: 3px; }
button, input, select, textarea { font: inherit; }
button, input, select, textarea { border: 1px solid var(--control-border); border-radius: 6px; background: var(--surface); color: inherit; }
button { padding: 9px 15px; cursor: pointer; font-weight: 600; }
button:hover:not(:disabled) { background: var(--surface-hover); border-color: var(--accent-text); }
button:disabled { opacity: .5; cursor: default; }
button.primary { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
button.primary:hover:not(:disabled) { background: var(--accent-hover); }
button.danger { color: var(--danger-text); background: var(--danger-background); border-color: var(--danger-border); }
button.danger:hover:not(:disabled) { color: var(--danger-text); background: var(--danger-hover); border-color: var(--danger-text); }
button.quiet { background: transparent; }
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.with-icon { display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.button-icon { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; }
input, select, textarea { padding: 9px 11px; min-width: 0; }
input[type="checkbox"] { accent-color: var(--accent); width: 17px; height: 17px; }
textarea { width: 100%; resize: vertical; font: 13px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; tab-size: 2; }
:focus-visible { outline: 3px solid var(--accent-text); outline-offset: 3px; }
::placeholder { color: var(--muted); opacity: 1; }
label { display: block; font-weight: 600; }
h1, h2, h3, p { margin: 0; }
h1 { font-size: clamp(25px, 3vw, 32px); letter-spacing: -.8px; line-height: 1.2; }
h2 { font-size: 19px; letter-spacing: -.3px; }
h3 { font-size: 16px; letter-spacing: -.2px; }
.muted, .hint { color: var(--muted); }
.hint { font-size: 13px; }
.eyebrow { color: var(--accent-text); font-size: 11px; text-transform: uppercase; font-weight: 700; letter-spacing: 1.5px; margin-bottom: 8px; }
.masthead { min-height: 76px; background: var(--surface); border-bottom: 1px solid var(--border); padding: 16px 4vw; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px 20px; }
.brand { display: flex; align-items: center; gap: 12px; font-size: 19px; font-weight: 700; text-decoration: none; color: inherit; }
.brand-mark { width: 24px; height: 24px; flex-shrink: 0; color: var(--accent); }
#session { display: flex; align-items: center; gap: 16px; font-size: 13px; }
/* The theme toggle lives beside the session rather than inside it, so it
   is there on the login screen too, where there is no session yet. */
.masthead-actions { display: flex; align-items: center; gap: 12px; }
.icon-only { display: inline-flex; align-items: center; justify-content: center; padding: 9px; line-height: 0; }
main { max-width: 1440px; margin: auto; padding: 32px 4vw 64px; }
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.login { max-width: 450px; margin: 6vh auto; padding: 36px; }
.login h1 + p { margin-top: 12px; }
.login form { display: grid; gap: 9px; margin: 28px 0 20px; }
.login form label:not(:first-child) { margin-top: 10px; }
.login form > button { margin-top: 14px; }
.password-field { position: relative; }
.password-field input { width: 100%; padding-right: 44px; }
.password-toggle { position: absolute; top: 1px; right: 1px; bottom: 1px; display: flex; align-items: center; justify-content: center; width: 40px; padding: 0; border: 0; background: transparent; color: var(--muted); }
.password-toggle:hover:not(:disabled) { border-color: transparent; background: transparent; color: var(--accent-text); }
.password-toggle[aria-pressed="true"] { color: var(--accent-text); }
.password-toggle[aria-pressed="true"]::after { position: absolute; top: 50%; left: 50%; width: 18px; height: 2px; border-radius: 2px; background: currentColor; content: ""; transform: translate(-50%, -50%) rotate(-45deg); }
#api-location { overflow-wrap: anywhere; margin-top: 10px; }
.page-heading { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin-bottom: 18px; }
.dashboard-controls { display: flex; align-items: stretch; justify-content: flex-start; gap: 24px; margin-bottom: 24px; border-bottom: 1px solid var(--border); }
.operation-picker { width: min(100%, 320px); min-width: 0; padding-bottom: 12px; }
.page-heading .operation-picker { padding-bottom: 0; }
.operation-picker label { font-size: 12px; margin-bottom: 6px; }
.operation-picker select { width: 100%; }
.dashboard-tabs { display: flex; align-self: stretch; }
.dashboard-tabs button { min-width: 110px; padding: 12px 18px; border: 0; border-bottom: 3px solid transparent; border-radius: 0; background: transparent; color: var(--muted); }
.dashboard-tabs button:hover:not(:disabled) { background: var(--surface-hover); border-color: transparent; color: var(--text); }
.dashboard-tabs button[aria-selected="true"] { color: var(--accent-text); border-bottom-color: var(--accent); }
.tab-count { display: inline-flex; align-items: center; justify-content: center; min-width: 21px; height: 21px; margin-left: 5px; padding: 0 6px; border: 1px solid var(--border); border-radius: 999px; background: var(--badge); color: var(--text); font-size: 11px; line-height: 1; }
.inline { display: flex; align-items: center; gap: 10px; }
.workspace-grid { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: 24px; align-items: start; }
.jobs-panel { padding: 20px 12px 12px; }
.jobs-heading { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 5px 14px 10px; }
.jobs-panel h2 { font-size: 14px; }
.compact-action { padding: 5px 8px; font-size: 11px; }
#jobs { display: grid; gap: 7px; }
.job-button { display: block; text-align: left; padding: 14px 12px; border-color: transparent; border-radius: 6px; width: 100%; }
.job-button[aria-current="true"] { background: var(--selected); border-color: var(--selected-border); }
.job-button small { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 11px; font-weight: 400; color: var(--muted); margin-top: 6px; }
.job-button .status-chip { font-size: 10px; padding: 1px 7px; }
.editor-panel { overflow: clip; }
.editor-heading { padding: 24px; display: flex; align-items: start; justify-content: space-between; gap: 16px; border-bottom: 1px solid var(--border); }
.editor-title { min-width: 0; }
.editor-title > .status-chip { margin-top: 9px; }
.editor-heading .hint { margin-top: 8px; }
.editor-actions { display: grid; justify-items: end; gap: 10px; margin-left: auto; }
.editor-action-buttons { display: flex; align-items: center; justify-content: end; flex-wrap: wrap; gap: 10px; }
.job-enabled-control { display: flex; align-items: center; justify-content: end; gap: 10px; font-size: 12px; }
.header-action { border: 1px solid var(--control-border); border-radius: 6px; padding: 6px 10px; font: inherit; font-size: 12px; font-weight: 600; line-height: 1.5; color: inherit; background: var(--surface); text-decoration: none; white-space: nowrap; cursor: pointer; }
.header-action:hover { background: var(--surface-hover); border-color: var(--accent-text); }
.badge { font-size: 11px; font-weight: 600; background: var(--badge); padding: 4px 9px; border-radius: 4px; white-space: nowrap; }
.status-chip { display: inline-block; color: var(--text); background: var(--badge); border: 1px solid var(--border); border-radius: 999px; font-weight: 600; white-space: nowrap; }
.status-chip[data-status="RUNNING"] { color: var(--status-running-text); background: var(--status-running-background); border-color: var(--status-running-border); }
.status-chip[data-status="FAILED"] { color: var(--status-failed-text); background: var(--status-failed-background); border-color: var(--status-failed-border); }
.status-chip[data-status="PENDING"] { color: var(--status-pending-text); background: var(--status-pending-background); border-color: var(--status-pending-border); }
.operation-editor { overflow: hidden; }
.section-heading { padding: 20px 24px; border-bottom: 1px solid var(--border); }
.section-heading p { margin-top: 4px; }
.section-heading-row { display: flex; align-items: center; justify-content: space-between; gap: 18px; }
.operation-heading { min-height: 78px; display: flex; align-items: center; justify-content: space-between; gap: 18px; }
.operation-name-line { display: flex; align-items: center; gap: 12px; min-width: 0; }
.operation-name-copy { min-width: 0; }
.operation-name-line h2 { font-size: 22px; overflow-wrap: anywhere; }
.operation-code { margin-top: 2px; color: var(--muted); font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace; overflow-wrap: anywhere; }
.operation-name-editor { display: grid; gap: 6px; width: min(100%, 620px); }
.operation-name-editor label { font-size: 12px; }
.operation-name-edit-row { display: flex; align-items: center; gap: 10px; }
.operation-name-edit-row input { flex: 1; width: 100%; }
.operation-section { border-bottom: 1px solid var(--border); }
.operation-section-heading { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 16px 24px; border-bottom: 1px solid var(--border-subtle); background: var(--surface-subtle); }
.operation-section-heading p { margin-top: 3px; }
.operation-details-grid { display: block; }
.operation-field { display: grid; gap: 6px; padding: 18px 24px; }
.operation-field label { font-size: 12px; }
.operation-field input { width: 100%; }
.operation-group { border-top: 1px solid var(--border-subtle); padding: 18px 0 4px; }
.operation-group h3, .operation-group > .hint { padding: 0 24px; }
.operation-group > .hint { margin-top: 5px; }
.operation-live-group { border-top: 0; }
.operation-portals-group { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; padding: 18px 24px 4px; }
.operation-portals-group h3, .operation-portals-group > .hint { grid-column: 1 / -1; padding: 0; }
.operation-portals-group .operation-field { padding: 14px 0; }
.enabled-control.compact { padding: 15px 24px; font-size: 13px; }
#operation-read-only { margin: 14px 24px; }
.timeslots-table-wrap { width: calc(100% - 48px); max-width: 700px; max-height: none; margin: 18px auto 22px; border: 1px solid var(--border); border-radius: 8px; }
.timeslots-table { min-width: 560px; }
.timeslots-table th:nth-child(3), .timeslots-table th:nth-child(4), .timeslots-table td:nth-child(3), .timeslots-table td:nth-child(4) { width: 110px; white-space: nowrap; }
.timeslots-table th:nth-child(5), .timeslots-table td:nth-child(5) { width: 76px; white-space: nowrap; }
.timeslots-table td { max-width: none; }
.timeslot-actions { display: flex; gap: 4px; justify-content: flex-end; }
.timeslot-actions button { padding: 6px; }
.timeslot-editing-row td { padding: 6px 12px; }
.timeslot-editing-row input { width: 100%; font-family: inherit; }
.timeslot-add-row td { padding: 6px 12px; }
.timeslot-add-row button { width: 100%; justify-content: flex-start; border-style: dashed; background: transparent; color: var(--muted); }
.timeslot-add-row button:hover:not(:disabled) { color: inherit; }
.storage-area-metrics-status { margin: 18px 24px 0; }
.storage-area-metrics-status:empty { display: none; }
.storage-area-metrics { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; padding: 18px 24px 22px; }
.storage-area-metric { min-width: 0; padding: 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-subtle); }
.storage-area-metric > span { display: block; color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; }
.storage-area-metric strong { display: block; margin-top: 5px; font-size: 18px; line-height: 1.3; }
.storage-area-metric small { display: block; margin-top: 3px; color: var(--muted); font-size: 12px; }
.storage-area-actions { text-align: right; }
.storage-area-actions button { margin: 0 24px 22px; }
.danger-zone { border: 1px solid var(--danger-border); border-width: 1px 0; }
.danger-zone .operation-section-heading { color: var(--danger-text); background: var(--danger-background); border-bottom-color: var(--danger-border); }
.danger-zone .operation-section-heading .hint { color: inherit; }
.danger-zone-action { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 18px 24px; }
.danger-zone-action strong { display: block; font-size: 13px; }
.danger-zone-action .hint { margin-top: 3px; }
.operation-save-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 17px 24px; border-top: 1px solid var(--border); background: var(--surface-subtle); }
.operation-save-bar .inline { justify-content: end; }
.simulation-progress, .ingestion-progress { padding: 22px 24px 20px; border-bottom: 1px solid var(--border); background: linear-gradient(135deg, var(--surface-subtle), var(--selected)); }
.progress-heading { display: flex; align-items: start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.progress-heading .eyebrow { margin-bottom: 4px; }
.progress-percentage { color: var(--muted); font-size: 12px; font-weight: 600; white-space: nowrap; }
.progress-track { height: 10px; overflow: hidden; border-radius: 999px; background: var(--badge); box-shadow: inset 0 0 0 1px var(--border); }
.progress-track > span { display: block; width: 0; height: 100%; border-radius: inherit; background: var(--accent); transition: width .25s ease; }
.progress-track.indeterminate > span { width: 100%; opacity: .45; background: repeating-linear-gradient(135deg, var(--accent) 0 9px, transparent 9px 18px); }
.progress-scale { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-top: 13px; }
.progress-scale div { min-width: 0; }
.progress-scale div:nth-child(2) { text-align: center; }
.progress-scale div:last-child { text-align: right; }
.progress-scale span { display: block; color: var(--muted); font-size: 10px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; }
.progress-scale strong { display: block; margin-top: 3px; font-size: 12px; font-weight: 600; overflow-wrap: anywhere; }
#simulation-progress-estimate { margin-top: 14px; font-weight: 600; }
.ingestion-status { margin: -5px 0 14px; font-size: 13px; font-weight: 600; }
.ingestion-status[data-failed="true"] { color: var(--danger-text); }
.ingestion-remaining { margin: 16px 0; font-size: 14px; font-weight: 600; }
.ingestion-streams { width: 100%; border-collapse: collapse; font-size: 12px; text-align: left; }
.ingestion-streams th, .ingestion-streams td { padding: 10px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.ingestion-streams th { font-weight: 600; }
.ingestion-streams td:last-child, .ingestion-streams th:last-child { text-align: right; }
.ingestion-streams tr[data-disabled="true"] { color: var(--muted); }
.ingestion-progress-help { margin-top: 14px; }
#ingestion-progress-refresh { margin-top: 8px; }
@media (max-width: 540px) { .ingestion-progress { padding: 18px; } .ingestion-progress .progress-heading { flex-wrap: wrap; gap: 8px; } .ingestion-streams th, .ingestion-streams td { padding: 10px 4px; overflow-wrap: anywhere; } }
fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset:disabled { opacity: .8; }
.enabled-control { display: flex; align-items: center; gap: 10px; padding: 18px 24px; }
.config-toolbar { border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 14px 24px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(180px, 320px) minmax(0, 1fr); align-items: center; gap: 16px; }
.config-toolbar label { font-size: 12px; white-space: nowrap; }
.config-search { grid-column: 2; width: 100%; }
.config-search input { width: 100%; }
.config-json-toggle { grid-column: 3; justify-self: end; }
.config-search-empty { padding: 22px 24px; border-bottom: 1px solid var(--border-subtle); }
/* A section's contents step in from it, so which heading a setting belongs
   to is visible without reading the dotted path under its name.
   The indent lives on the group and the heading is pulled back out of it by
   the same amount, which means nesting accumulates on its own - a section
   inside a section indents twice - without a rule per level. The heading
   stays full-bleed at its own level, where its background reads as a bar
   across everything it covers. */
.field-group { padding-left: 20px; border-bottom: 1px solid var(--border); }
.field-group summary { margin-left: -20px; padding: 14px 24px; background: var(--surface-subtle); cursor: pointer; font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
.field-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(170px, 260px); gap: 18px; align-items: center; padding: 15px 24px; border-bottom: 1px solid var(--border-subtle); }
.field-row:last-child { border-bottom: 0; }
.field-label { font-size: 13px; }
.field-label small { display: block; font: 11px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace; color: var(--muted); overflow-wrap: anywhere; margin-top: 4px; }
.field-input { display: grid; gap: 5px; width: 100%; max-width: 260px; justify-self: end; }
.field-control, .field-control-row { min-width: 0; width: 100%; }
/* Under the input, not beside it. Sharing the row meant every nullable
   setting got a narrower input than every other one, for a control that is
   rarely used - so the input keeps the full column and the button takes a
   line of its own, against the right edge where it lines up with the input
   it belongs to. */
.field-control-row.has-clear { display: grid; gap: 6px; }
.field-input input, .field-input select { width: 100%; }
.field-input.boolean-input { justify-items: end; }
.field-input.boolean-input .inline { justify-content: end; }
.duration-input { display: grid; grid-template-columns: minmax(60px, 1fr) minmax(0, 2fr); gap: 10px; }
.duration-input input { display: block; margin-top: 5px; }
.field-input .clear-value { justify-self: end; padding: 1px 7px; font-size: 11px; line-height: 1.6; }
.input-adornment { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: stretch; overflow: hidden; border: 1px solid var(--control-border); border-radius: 6px; background: var(--surface); }
.input-adornment:focus-within { outline: 3px solid var(--accent-text); outline-offset: 3px; }
.input-adornment input { border: 0; border-radius: 0; background: transparent; }
.input-adornment input:focus-visible { outline: 0; }
.input-adornment-label { display: flex; align-items: center; padding: 0 10px; border-left: 1px solid var(--border); color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .4px; }
input.toggle { appearance: none; position: relative; width: 38px; height: 22px; flex-shrink: 0; padding: 0; margin: 0; border: 1px solid var(--control-border); border-radius: 20px; background: var(--badge); cursor: pointer; }
input.toggle::before { content: ""; position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; border-radius: 50%; background: var(--muted); }
input.toggle:checked { background: var(--accent); border-color: var(--accent); }
input.toggle:checked::before { left: 19px; background: var(--on-accent); }
input.toggle:indeterminate::before { left: 11px; }
input.toggle:disabled { cursor: default; opacity: .65; }
@media (forced-colors: active) { input.toggle { appearance: auto; } input.toggle::before { display: none; } }
#json-editor { padding: 20px 24px; }
#json-editor p { margin: 8px 0 14px; }
/* Sits with the buttons it explains. On its own above the settings it read
   as a warning about the whole panel; next to a disabled Review it reads as
   the reason that button cannot be pressed, which is what it is. */
.save-bar-status { display: grid; gap: 2px; min-width: 0; }
.save-bar-note { margin: 0; color: var(--muted); font-size: 12px; }
.save-bar-note[hidden] { display: none; }
.save-bar { position: sticky; z-index: 5; bottom: 0; display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 20px 24px; background: var(--surface-subtle); border-top: 1px solid var(--border); box-shadow: 0 -8px 18px rgb(20 12 18 / 8%); }
#change-count { font-size: 13px; color: var(--muted); }
.empty { padding: 36px 24px; color: var(--muted); }
#notice, .info { padding: 14px 18px; border: 1px solid var(--info-border); background: var(--info-background); border-radius: 6px; margin-bottom: 22px; overflow-wrap: anywhere; }
#notice[data-tone="error"], #save-notice { color: var(--error-text); border-color: var(--error-border); background: var(--error-background); }
.info { margin: 20px 24px 0; font-size: 13px; }
dialog { border: 1px solid var(--border); border-radius: 12px; padding: 28px; width: min(800px, 94vw); color: inherit; background: var(--surface); max-height: 90vh; }
dialog::backdrop { background: var(--backdrop); }
dialog h2 { font-size: 22px; }
dialog > p { margin: 10px 0 20px; }
dialog form > h2 + p { margin-top: 10px; }
.wide-dialog { width: min(860px, 94vw); }
.comparison-dialog { width: min(1120px, 96vw); }
.dialog-fields { display: grid; gap: 8px; margin-top: 24px; }
.dialog-fields label:not(:first-child) { margin-top: 10px; }
.dialog-fields .hint { margin-top: -2px; }
.storage-area-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 24px; }
.dialog-field { display: grid; gap: 6px; min-width: 0; }
.dialog-field label { font-size: 12px; }
.storage-area-name { grid-column: 1 / -1; }
.storage-area-options { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 18px; margin-top: 24px; padding: 18px; border: 1px solid var(--border); border-radius: 8px; }
.storage-area-options legend { padding: 0 7px; font-size: 12px; font-weight: 700; }
.storage-area-options label { display: flex; align-items: center; gap: 9px; font-size: 13px; }
.storage-area-relationships { margin-top: 20px; border: 1px solid var(--border); border-radius: 8px; }
.storage-area-relationships summary { padding: 14px 16px; cursor: pointer; font-weight: 600; }
.storage-area-relationships > p { padding: 0 16px; }
.storage-area-relationships .storage-area-grid { margin-top: 0; padding: 14px 16px 18px; }
.uuid-input { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
#create-simulation-notice, #create-storage-area-notice, #delete-operation-notice { padding: 12px; border: 1px solid var(--info-border); background: var(--info-background); border-radius: 6px; margin-top: 20px; overflow-wrap: anywhere; }
#create-simulation-notice[data-tone="error"], #create-storage-area-notice[data-tone="error"], #delete-operation-notice[data-tone="error"] { color: var(--error-text); border-color: var(--error-border); background: var(--error-background); }
.compare-picker { display: grid; grid-template-columns: max-content minmax(220px, 360px); align-items: center; gap: 16px; padding: 16px 18px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-subtle); }
.compare-picker label { font-size: 13px; }
.compare-picker select { width: 100%; }
.comparison-notice { padding: 12px; border: 1px solid var(--info-border); border-radius: 6px; margin-top: 18px; background: var(--info-background); overflow-wrap: anywhere; }
.comparison-notice[data-tone="error"] { color: var(--error-text); border-color: var(--error-border); background: var(--error-background); }
.comparison-summary { margin: 20px 0 10px; font-size: 13px; font-weight: 600; }
.comparison-scroll { max-height: 55vh; border: 1px solid var(--border); border-radius: 8px; }
.comparison-table { min-width: 700px; table-layout: fixed; }
.comparison-table th:first-child { width: 28%; }
.comparison-table td { max-width: none; }
.comparison-table td:first-child { font-weight: 600; color: var(--text); }
.comparison-table tbody tr.comparison-different td { background: var(--selected); }
.comparison-table tbody tr.comparison-different td:first-child { box-shadow: inset 3px 0 var(--accent); }
.comparison-value { display: block; margin: 0; font: inherit; white-space: pre-wrap; overflow-wrap: anywhere; }
.comparison-value.missing { color: var(--muted); font-style: italic; }
.table-scroll { overflow: auto; max-height: 50vh; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { background: var(--background); }
td { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; overflow-wrap: anywhere; white-space: pre-wrap; max-width: 260px; }
.dialog-actions { display: flex; justify-content: end; gap: 12px; margin-top: 24px; }
#save-notice { padding: 12px; border-radius: 6px; margin-top: 20px; }
@media (max-width: 850px) { .workspace-grid { grid-template-columns: 1fr; } #jobs { grid-template-columns: repeat(2, minmax(0, 1fr)); } .field-row { grid-template-columns: 1fr 1fr; } .storage-area-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 540px) { #jobs { grid-template-columns: 1fr; } }
@media (max-width: 540px) { main { padding: 22px 14px 40px; } .masthead { padding: 14px 16px; min-height: 64px; gap: 12px; } .brand { gap: 8px; font-size: 17px; } .masthead-actions { margin-left: auto; } #session { gap: 10px; } .login { padding: 24px; margin: 3vh auto; } .page-heading, .operation-heading, .save-bar, .operation-save-bar, .section-heading-row { align-items: start; flex-direction: column; } .operation-heading > #create-simulation, .section-heading-row > button { align-self: stretch; } .save-bar > .inline, .operation-save-bar > .inline { flex-wrap: wrap; } .dashboard-controls { align-items: stretch; flex-direction: column; gap: 4px; } .operation-picker { width: 100%; } .dashboard-tabs { width: 100%; } .dashboard-tabs button { flex: 1; min-width: 0; } .operation-details-grid, .storage-area-grid { grid-template-columns: 1fr; } .storage-area-name { grid-column: auto; } .operation-live-group { border-left: 0; border-top: 1px solid var(--border-subtle); } .operation-portals-group { grid-template-columns: 1fr; } .storage-area-options { grid-template-columns: 1fr 1fr; } .editor-heading { flex-wrap: wrap; } .editor-actions { width: 100%; margin-left: 0; justify-content: end; } .progress-scale { grid-template-columns: 1fr; gap: 8px; } .progress-scale div { display: flex; justify-content: space-between; gap: 14px; text-align: left !important; } .progress-scale strong { margin-top: 0; text-align: right; } .field-row { grid-template-columns: 1fr; gap: 10px; } .field-input { max-width: none; justify-self: stretch; } .config-toolbar { grid-template-columns: 1fr; } .config-search { grid-column: 1; grid-row: 1; width: 100%; } .config-json-toggle { grid-column: 1; grid-row: 2; justify-self: end; } .compare-picker { grid-template-columns: 1fr; gap: 6px; } .editor-heading, .simulation-progress, .field-row, .config-toolbar, .save-bar { padding: 18px; } #user-name { max-width: 100px; overflow: hidden; text-overflow: ellipsis; } }
@media (max-width: 540px) { .operation-name-line, .operation-name-edit-row, .danger-zone-action { align-items: stretch; flex-direction: column; } .operation-live-group { border-top: 0; } .timeslots-table-wrap { width: calc(100% - 36px); margin: 18px 18px; } .storage-area-metrics { padding: 18px; } .storage-area-actions button { display: flex; width: calc(100% - 36px); margin: 0 18px 18px; } }
@media (max-width: 380px) { .storage-area-options { grid-template-columns: 1fr; } }

/* Car-park map -------------------------------------------------------- */

/* A control strip for the whole operation view, not for one section: the
   instant it names drives the map and the metrics together. */
.operation-view-toolbar { display: flex; flex-wrap: wrap; align-items: end; gap: 16px; padding: 16px 24px; background: var(--surface-subtle); border-bottom: 1px solid var(--border); }
.operation-view-field { display: grid; gap: 6px; }
.operation-view-field label { font-size: 12px; }
/* The width belongs to the wrapper once there is one, or the input would
   be capped inside a box that is not, leaving the UTC tag adrift. */
.operation-view-field input, .operation-view-field .input-adornment { width: min(100%, 250px); }
.operation-view-field .input-adornment input { width: 100%; }
/* The timeline sits under the map, not in the toolbar above it: it is a
   scale for the thing it is beneath, and reading it means looking at the
   map and the hour together. Gutters match .map-panel so the track lines
   up with the map's edges. */
.car-park-timeline { margin: 0 24px 22px; }
/* The shared range rule stretches with flex: 1, which needs a flex parent
   to mean anything - on its own here the track collapses to the input's
   intrinsic width and bunches up at the left. The track is a scale for
   the map above it, so it spans the same width. */
.car-park-timeline input[type="range"] { display: block; width: 100%; }
/* The thumb travels between its own half-widths, so the tick strip is inset
   by half a thumb (9px of 18px). Without that the 00:00 and 24:00 labels
   sit outside the positions their cursor can actually reach. */
.car-park-timeline-ticks { position: relative; height: 26px; margin: 8px 9px 0; padding: 0; list-style: none; }
.car-park-timeline-ticks li { position: absolute; top: 0; transform: translateX(-50%); }
.car-park-tick { padding: 2px 6px; border: 0; border-radius: 6px; background: none; color: var(--muted); font: inherit; font-size: 12px; font-variant-numeric: tabular-nums; cursor: pointer; }
.car-park-tick:hover:not(:disabled) { background: var(--surface-subtle); color: var(--text); }
/* A loaded hour is one step away; an unloaded one costs a reconstruction.
   Worth telling apart before you click, not after. */
.car-park-tick[data-loaded] { color: var(--text); font-weight: 600; }
/* Marked the way .dashboard-tabs marks its current tab: accent as an
   underline, not as a fill. --accent-text is a dark pink for reading
   against the page, so on a bright --accent fill the two nearly matched.
   Inset shadow rather than a border so nothing reflows on selection. */
.car-park-tick[aria-current] { color: var(--accent-text); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent); }
.car-park-tick:disabled { opacity: .4; cursor: not-allowed; }
.car-park-timeline-label { display: block; margin: 4px 9px 0; text-align: center; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }

.car-park-status { margin: 16px 24px 0; font-variant-numeric: tabular-nums; }
.car-park-status:empty { display: none; }
.car-park-section .map-panel { position: relative; margin: 14px 24px 22px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
/* Two panes, equal width, sharing the gutters a single map has. The layout
   packs a car park to be roughly square (packSquarish), so two of them side
   by side come out near 2:1 - close to the shape of a wide screen, where
   stacking them would give 1:2 and waste most of it. */
.car-park-maps { display: grid; grid-template-columns: 1fr; }
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) { grid-template-columns: 1fr 1fr; gap: 0; }
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .map-panel { margin-left: 12px; margin-right: 12px; }
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .map-panel:first-child { margin-left: 24px; }
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .map-panel:last-child { margin-right: 24px; }
/* Halving the width halves how much of the yard is legible, so a pair gets
   a shorter frame - two full-height maps would not fit on screen together,
   and seeing both at once is the whole point. */
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .car-park-frame { height: min(56vh, 620px); }
/* One set of controls for the pair. The two viewports are mirrored, so a
   second set would drive both maps identically - two ways to do one thing,
   taking room from the map that is the point. They live on the right, where
   they already sit, so nothing moves when a comparison is opened. */
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .map-panel:first-child .car-park-controls,
.car-park-maps:has(> .map-panel:not([hidden]) ~ .map-panel:not([hidden])) .map-panel:first-child .car-park-picker { display: none; }

/* Full screen means the map gets the screen. Without this the panel keeps
   its viewport-relative height and its margins, so the extra room becomes a
   border of the fullscreen backdrop - which is black by default, not the
   page's own surface.

   Keyed on an attribute the app sets, not on :fullscreen. The pseudo-class
   needs a vendor prefix in older Safari, and when it fails to match there
   is no error - the panel simply keeps the height it had, which is a little
   over half the screen for a pair. An attribute matches everywhere, outranks
   the :has() comparison rules above without depending on source order, and
   can be asserted in a test. */
.car-park-stage[data-fullscreen] { display: flex; flex-direction: column; height: 100vh; background: var(--surface); }
/* The maps take what is left after the scrubber, which keeps its own
   height. Full screen is for scrubbing through a day at the largest size
   the screen allows, so leaving the timeline behind would make it a view
   you can only look at. */
.car-park-stage[data-fullscreen] .car-park-maps { flex: 1 1 auto; min-height: 0; grid-template-rows: minmax(0, 1fr); align-items: stretch; }
.car-park-stage[data-fullscreen] .car-park-timeline { flex: 0 0 auto; margin: 0 24px 16px; }
.car-park-stage[data-fullscreen] .map-panel { display: flex; flex-direction: column; height: auto; min-height: 0; margin: 0; border: 0; border-radius: 0; }
/* Stretched by flex rather than sized at 100%. A percentage height needs a
   definite height on the parent to resolve against, and the mount's comes
   from flex - which is definite enough for one map and was not once a
   caption shared the column, leaving the frame at its ordinary height and
   the map cut off half way down. Flex has no such dependency. */
.car-park-stage[data-fullscreen] .map-panel .map-mount { display: flex; flex: 1 1 auto; min-height: 0; }
.car-park-stage[data-fullscreen] .map-panel .car-park-frame { flex: 1 1 auto; height: auto; min-height: 0; }
/* Two maps look alike; say which is which. */
.map-caption { margin: 0; padding: 6px 10px; border-bottom: 1px solid var(--border); background: var(--surface-subtle); color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.map-caption[hidden] { display: none; }
/* Stepping to an hour that is not loaded covers the map rather than
   replacing it. The map underneath keeps its pan and zoom, so what arrives
   lands where the reader was already looking instead of the page lurching. */
.scrub-overlay { position: absolute; inset: 0; z-index: 3; background: var(--surface-subtle); opacity: .72; }
.map-mount { background: var(--surface); }

/* Placeholders for content that is on its way. They hold the shape the
   real thing will take, so nothing jumps when it arrives - which is why
   the map skeleton repeats the frame's height and the metric skeletons
   sit in the same grid as the cards they stand in for. */
.skeleton { background: var(--surface-subtle); animation: skeleton-pulse 1.5s ease-in-out infinite; }
.skeleton-map { height: min(78vh, 880px); }
.skeleton-metric { min-height: 100px; border: 1px solid var(--border); border-radius: 8px; }
.storage-area-metrics[data-loading="false"] .skeleton-metric { animation: none; opacity: .65; }
@keyframes skeleton-pulse { 50% { opacity: .45; } }
/* Idle, not loading: the same footprint as the skeleton it sits alongside,
   but static (no pulse) with a message explaining why nothing is shown yet. */
.unloaded-map { display: flex; align-items: center; justify-content: center; text-align: center; padding: 24px; background: var(--surface-subtle); color: var(--muted); }
.unloaded-map p { max-width: 340px; margin: 0; font-size: 14px; }
/* A pulse is decoration; the status line above says the same thing. */
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }
/* The map is a surface you move over, so it fills a fixed viewport and the
   viewBox does the scrolling - a scrollbar would fight the drag. */
.car-park-frame { position: relative; height: min(78vh, 880px); overflow: hidden; touch-action: none; cursor: grab; outline: none; }
/* Dragging a map should move the map, not select the labels it passes. */
.car-park-frame { user-select: none; -webkit-user-select: none; }
.car-park-frame[data-panning] { cursor: grabbing; }
.car-park-frame:focus-visible { outline: 3px solid var(--accent-text); outline-offset: -3px; }
.car-park { display: block; width: 100%; height: 100%; }
/* The controls sit on top of the thing they control, so they get out of the
   way until they are wanted and never quite reach opaque - at 95% the map
   still shows through them, which keeps them reading as an overlay on the
   yard rather than as a panel bolted over it.

   Kept hit-testable at rest, because opacity is what hides them: the
   pointer still finds them, which is what brings them back. That corner was
   never draggable anyway - the controls stop pointerdown from reaching the
   map so a press on a button cannot also pan it.

   :focus-within matters as much as :hover here. A keyboard has no pointer,
   and controls that only appear on hover are controls a keyboard user
   cannot see they have reached. */
.car-park-picker, .car-park-controls { opacity: 0; transition: opacity .18s ease; }
.car-park-frame:hover .car-park-picker, .car-park-frame:hover .car-park-controls { opacity: .4; }
.car-park-picker:hover, .car-park-picker:focus-within,
.car-park-controls:hover, .car-park-controls:focus-within { opacity: .95; }
@media (prefers-reduced-motion: reduce) {
  .car-park-picker, .car-park-controls { transition: none; }
}
.car-park-picker { position: absolute; z-index: 2; top: 10px; right: 10px; }
.car-park-picker select { max-width: 200px; box-shadow: 0 1px 4px rgb(0 0 0 / 12%); }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.car-park-controls { position: absolute; z-index: 2; right: 10px; bottom: 10px; display: grid; gap: 6px; justify-items: center; }
.car-park-pad { display: grid; grid-template-columns: repeat(3, 28px); grid-auto-rows: 28px; gap: 2px; }
.car-park-zoom { display: grid; grid-template-columns: repeat(2, 28px); gap: 2px; }
.car-park-controls button.fullscreen { width: 58px; font-size: 13px; }
.car-park-controls button { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; font-size: 14px; line-height: 1; border-radius: 6px; background: var(--surface); box-shadow: 0 1px 4px rgb(0 0 0 / 12%); }
.car-park-controls button.reset { font-size: 15px; color: var(--accent-text); }

.car-park-tip { position: absolute; z-index: 2; pointer-events: none; padding: 5px 9px; border-radius: 6px; font-size: 12px; white-space: nowrap; color: light-dark(var(--on-accent), var(--text)); background: light-dark(var(--accent), var(--surface-hover)); box-shadow: 0 2px 9px light-dark(rgb(0 0 0 / 25%), rgb(0 0 0 / 50%)); }
/* Hatching marks ground that is not part of the car park, so the gaps
   between groups read as nothing rather than as somewhere empty. */
.car-park .hatch-ground { fill: var(--background); }
.car-park .hatch-line { stroke: var(--border-subtle); stroke-width: 3; }
.car-park .zone-shape { fill: var(--pavement); stroke: var(--pavement-edge); }
.car-park .zone-label { fill: var(--muted); font-size: 12px; font-weight: 600; }
/* Banded, so a group can be named as full rather than compared by eye. */
.car-park .zone-fill { font-weight: 700; font-variant-numeric: tabular-nums; }
.car-park .zone-fill[data-band="low"] { fill: var(--status-running-text); }
.car-park .zone-fill[data-band="medium"] { fill: var(--status-pending-text); }
.car-park .zone-fill[data-band="high"] { fill: var(--accent-text); }
.car-park .zone-fill[data-band="full"] { fill: var(--status-failed-text); }
.car-park .zone[data-focused] .zone-shape { stroke: var(--accent); stroke-width: 2.5; }
.car-park .zone[data-focused] .zone-label { fill: var(--accent-text); }
.car-park .lane-count { fill: var(--muted); font-size: 8px; text-anchor: middle; font-variant-numeric: tabular-nums; }

/* Zoomed out, a lane's bays and cars are too small to mean anything - name
   and fill percentage per group replace them until zooming in far enough
   to read one properly. `data-lod` is set from the current zoom scale. */
.car-park .zone-skeleton-label { fill: var(--muted); font-size: 52px; font-weight: 700; text-anchor: middle; }
.car-park .zone-skeleton-label .zone-skeleton-fill { font-size: 48px; }
.car-park[data-lod="overview"] .lanes,
.car-park[data-lod="overview"] .slots,
.car-park[data-lod="overview"] .zone-label { display: none; }
.car-park:not([data-lod="overview"]) .zone-skeletons { display: none; }
/* The skeleton's own ground carries the band signal as a light tint - full
   colour was too strong a jump from pavement. Only in overview: the same
   shape is plain pavement once lane detail is back. */
.car-park[data-lod="overview"] .zone[data-band="low"] .zone-shape { fill: var(--status-running-text); fill-opacity: 0.65; }
.car-park[data-lod="overview"] .zone[data-band="medium"] .zone-shape { fill: var(--status-pending-text); fill-opacity: 0.65; }
.car-park[data-lod="overview"] .zone[data-band="high"] .zone-shape { fill: var(--accent-text); fill-opacity: 0.65; }
.car-park[data-lod="overview"] .zone[data-band="full"] .zone-shape { fill: var(--status-failed-text); fill-opacity: 0.65; }
/* The name and fill line both inherit this - stronger contrast against the
   band's own colour than the plain muted grey they use over bare pavement. */
.car-park[data-lod="overview"] .zone[data-band="low"] .zone-skeleton-label { fill: var(--zone-band-on-low); }
.car-park[data-lod="overview"] .zone[data-band="medium"] .zone-skeleton-label { fill: var(--zone-band-on-medium); }
.car-park[data-lod="overview"] .zone[data-band="high"] .zone-skeleton-label { fill: var(--zone-band-on-high); }
.car-park[data-lod="overview"] .zone[data-band="full"] .zone-skeleton-label { fill: var(--zone-band-on-full); }
.car-park .lane-count[data-full] { fill: var(--accent-text); font-weight: 700; }
.car-park .lane-shell { fill: var(--pavement-lane); stroke: none; }
/* The sides of a bay are painted lines, not a box - so that is what they
   are, in the bone the paint actually is. */
.car-park .bay-lines { stroke: var(--bay-line); stroke-width: 1.6; stroke-linecap: round; fill: none; }
.car-park .lane-label { fill: var(--muted); font-size: 9px; text-anchor: middle; }

/* Empty and unknown are both grey and mean very different things, so they
   differ by shape as well as tone and are named separately in the legend:
   an empty bay is a plain box, an unknown car is still a car. */
.slot { fill: var(--bay-empty); }
.slot.filled { color: var(--badge); }
.slot.filled.unknown { color: #8a8a8a; }
.car-glass { fill: light-dark(rgb(0 0 0 / 20%), rgb(0 0 0 / 32%)); }
.slot.out-of-cohort { stroke: var(--text); stroke-width: 2.2; }
/* A lane whose cars are not leaving together. Drawn round the lane rather
   than on any bay, because it is the lane that is wrong: everything in it
   has to come out in order, so a car due out days later blocks the ones in
   front of it. */
.lane[data-mixed-eda] .lane-shell { stroke: var(--mixed-eda); stroke-width: 2.4; }
.legend .chip.mixed-eda { background: none; box-shadow: inset 0 0 0 2.4px var(--mixed-eda); }
.slot.filled[data-hovered] { stroke: var(--accent); stroke-width: 2.5; }

/* One colour per timeslot, spaced round the wheel at even lightness and
   chroma so they read as one set rather than as a clash. OKLCH is what
   keeps them evenly bright; the same hues picked in hex would not be. */
:root {
  /* Timeslots use the whole wheel again, minus the narrow band the flow
     ramp occupies. Confining them to the cool half kept the two scales
     apart but put eight hues 25 degrees apart, and telling adjacent slots
     apart is the whole job - so the flow ramp gave ground instead: it now
     separates by lightness through one pink-red band rather than sweeping
     green to red, which costs it nothing and buys back 40-degree spacing
     here, near the 45 it always had. */
  --mixed-eda: light-dark(oklch(58% .20 22), oklch(66% .20 22));
  /* White through pink to red, for cars in arrivals and departures where
     time matters more than cohort. It separates by lightness and chroma
     inside one hue band rather than travelling round the wheel, which
     leaves the rest of it for timeslots. Pale rather than truly white: a
     white car on a near-white bay is invisible in the light theme. */
  --flow-0: light-dark(oklch(95% 0.035 10), oklch(96% 0.035 10));
  --flow-1: light-dark(oklch(87% 0.085 12), oklch(91% 0.085 12));
  --flow-2: light-dark(oklch(78% 0.13 14), oklch(82% 0.13 14));
  --flow-3: light-dark(oklch(68% 0.17 18), oklch(72% 0.17 18));
  --flow-4: light-dark(oklch(57% 0.2 25), oklch(61% 0.2 25));
  --ts-0: light-dark(oklch(74% .16 55), oklch(79% .16 55));
  --ts-1: light-dark(oklch(80% .16 96), oklch(85% .16 96));
  --ts-2: light-dark(oklch(72% .16 136), oklch(77% .16 136));
  --ts-3: light-dark(oklch(70% .16 177), oklch(75% .16 177));
  --ts-4: light-dark(oklch(68% .16 218), oklch(73% .16 218));
  --ts-5: light-dark(oklch(66% .16 259), oklch(71% .16 259));
  --ts-6: light-dark(oklch(62% .16 299), oklch(67% .16 299));
  --ts-7: light-dark(oklch(64% .16 340), oklch(69% .16 340));
}
.slot.filled.flow0 { color: var(--flow-0); } .legend .chip.flow0 { background: var(--flow-0); }
.slot.filled.flow1 { color: var(--flow-1); } .legend .chip.flow1 { background: var(--flow-1); }
.slot.filled.flow2 { color: var(--flow-2); } .legend .chip.flow2 { background: var(--flow-2); }
.slot.filled.flow3 { color: var(--flow-3); } .legend .chip.flow3 { background: var(--flow-3); }
.slot.filled.flow4 { color: var(--flow-4); } .legend .chip.flow4 { background: var(--flow-4); }
.legend-ramp { display: inline-flex; gap: 2px; }
.legend-ramp .chip { width: 12px; }
.slot.filled.t0 { color: var(--ts-0); } .legend .chip.t0 { background: var(--ts-0); }
.slot.filled.t1 { color: var(--ts-1); } .legend .chip.t1 { background: var(--ts-1); }
.slot.filled.t2 { color: var(--ts-2); } .legend .chip.t2 { background: var(--ts-2); }
.slot.filled.t3 { color: var(--ts-3); } .legend .chip.t3 { background: var(--ts-3); }
.slot.filled.t4 { color: var(--ts-4); } .legend .chip.t4 { background: var(--ts-4); }
.slot.filled.t5 { color: var(--ts-5); } .legend .chip.t5 { background: var(--ts-5); }
.slot.filled.t6 { color: var(--ts-6); } .legend .chip.t6 { background: var(--ts-6); }
.slot.filled.t7 { color: var(--ts-7); } .legend .chip.t7 { background: var(--ts-7); }

.legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 22px; margin: 14px 24px 0; }
.legend-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.legend-heading { font-size: 12px; font-weight: 600; color: var(--muted); }
.legend-step { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
.legend .chip { width: 13px; height: 13px; border-radius: 3px; background: var(--badge); flex: none; }
.legend .chip.unknown { background: #8a8a8a; }
.legend .chip.out-of-cohort { box-shadow: inset 0 0 0 1.5px var(--text); }

/* The shared input rule above puts a border and background on every input,
   which shows through the range track; both have to be cleared here. */
input[type="range"] { appearance: none; -webkit-appearance: none; flex: 1; min-width: 0; padding: 0; border: 0; background: transparent; }
input[type="range"]::-webkit-slider-runnable-track { height: 10px; border-radius: 999px; background: var(--badge); box-shadow: inset 0 0 0 1px var(--border); }
input[type="range"]::-moz-range-track { height: 10px; border-radius: 999px; background: var(--badge); box-shadow: inset 0 0 0 1px var(--border); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; margin-top: -4px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); cursor: grab; }
input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); cursor: grab; }
/* Grabbing, not grabbed: the same language the map frame uses for dragging. */
input[type="range"]:active::-webkit-slider-thumb { cursor: grabbing; }
input[type="range"]:active::-moz-range-thumb { cursor: grabbing; }
input[type="range"]:disabled { opacity: .5; }
input[type="range"]:disabled::-webkit-slider-thumb { cursor: progress; }
input[type="range"]:disabled::-moz-range-thumb { cursor: progress; }
@media (forced-colors: active) { input[type="range"] { appearance: auto; } }
@media (max-width: 540px) {
  .operation-view-toolbar { align-items: stretch; flex-direction: column; padding: 16px 18px; }
  .operation-view-field input, .operation-view-field .input-adornment { width: 100%; }
  .car-park-status, .legend { margin-left: 18px; margin-right: 18px; }
  .car-park-section .map-panel { margin: 14px 18px 18px; }
  .car-park-timeline { margin: 0 18px 18px; }
}
