:root {
    --bg: #14181d;
    --panel: #1c2229;
    --ink: #e6e9ee;
    --muted: #8d99a8;
    --line: #2c343d;
    --free: #232b34;
    --absent: #0d1013;
    --gate: #f0a020;
    --empty-standard: #3f6f8f;
    --empty-split: #4f8f8f;
    --split-half: #a8c060;
    --split-full: #d8b040;
    --loaded-standard: #c96a4a;
    --move-target: #7fd4ff;
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 20px 24px 40px;
    background: var(--bg);
    color: var(--ink);
    font: 14px/1.5 ui-sans-serif, system-ui, sans-serif;
}

h1 {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
}

h2 {
    margin: 20px 0 8px;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

.subtitle {
    margin: 4px 0 0;
    color: var(--muted);
}

.subtitle #scenario-name {
    color: var(--ink);
    font-weight: 600;
}

nav#scenario-list {
    margin-top: 8px;
    display: flex;
    gap: 12px;
}

nav#scenario-list a {
    color: var(--muted);
}

nav#scenario-list a.current {
    color: var(--gate);
}

.failure {
    margin: 16px 0 0;
    padding: 10px 14px;
    border-left: 3px solid var(--loaded-standard);
    background: var(--panel);
}

.playback {
    max-width: 1120px;
    margin-top: 18px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--panel);
}

.phase-bar {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--line);
}

.phase-badge {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    font-size: 12px;
    color: var(--gate);
}

.phase-progress {
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--muted);
}

.estimated-tag {
    padding: 1px 6px;
    border: 1px dashed var(--muted);
    border-radius: 8px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--muted);
    cursor: help;
}

.phase-jumps {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-left: auto;
}

.phase-jumps button {
    font-size: 12px;
    text-transform: capitalize;
}

.phase-jumps button.current {
    border-color: var(--gate);
    color: var(--gate);
}

.transport { display: flex; align-items: center; gap: 8px; }

button,
select {
    padding: 5px 10px;
    border: 1px solid #46515e;
    border-radius: 4px;
    background: #252d36;
    color: var(--ink);
}

button:hover:not(:disabled) { border-color: var(--gate); }
button:disabled { opacity: 0.4; }

.transport label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    color: var(--muted);
}

.scrubber {
    display: grid;
    grid-template-columns: 230px 1fr;
    align-items: center;
    gap: 14px;
    margin-top: 10px;
    color: var(--muted);
}

.scrubber input { width: 100%; }

.event-details { font: 12px/1.5 ui-monospace, monospace; }
.event-details.muted,
.event-details .leg { color: var(--muted); }
.event-title { color: var(--gate); font-weight: 700; }
.event-total { margin: 5px 0 2px; }
.event-total.estimated { color: var(--muted); font-style: italic; }

main {
    display: flex;
    align-items: flex-start;
    gap: 28px;
    margin-top: 18px;
}

.locker-pane {
    flex: 0 0 auto;
}

.locker-wrap {
    position: relative;
    padding: 8px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--panel);
}

.caption,
.note {
    max-width: 44ch;
    margin: 10px 0 0;
    color: var(--muted);
    font-size: 12px;
}

.note code {
    font: 11px/1 ui-monospace, monospace;
}

/* Scrolls within itself rather than with the page. The key carries a glossary definition per drawn
   term, so it is taller than the viewport — and a sticky element taller than the viewport has a
   bottom the reader can never scroll to. */
aside {
    flex: 0 1 320px;
    position: sticky;
    top: 20px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    padding-right: 8px;
}

.legend {
    margin: 0;
    padding: 0;
    list-style: none;
}

.legend li {
    padding: 2px 0;
}

/* An entry that carries a glossary definition is a block, not a row: the swatch and its name sit
   above the quoted words rather than beside them, or a four-line definition would drag the key
   into a column of its own. */
.legend li.defined {
    padding: 6px 0;
    border-top: 1px solid var(--line);
}

.legend-key {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* A term the page draws but does not colour — a tray move is an animation, a loading cycle a
   route. Indented to the width a swatch would have taken, so the list still reads as one column. */
.legend-key .term {
    margin-left: 40px;
    font-weight: 600;
}

.definition {
    max-width: 44ch;
    margin: 3px 0 0 40px;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.45;
}

.swatch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 15px;
    border-radius: 2px;
    font: 11px/1 ui-monospace, monospace;
    color: #10141a;
}

.swatch.free {
    background: var(--free);
    border: 1px solid var(--line);
}

.swatch.absent {
    background: var(--absent);
    border: 1px solid var(--line);
    background-image: repeating-linear-gradient(45deg, transparent 0 3px, var(--line) 3px 4px);
}

.swatch.gate {
    background: var(--gate);
}

.swatch.heat {
    background: linear-gradient(90deg, hsl(190 72% 52%), hsl(12 72% 52%));
}

.swatch.frontier {
    height: 8px;
    border-top: 2px dashed #fff4a8;
    border-radius: 0;
}

.swatch.decision {
    background: transparent;
    border: 1px solid var(--move-target);
    color: var(--move-target);
}

/* The robot and its route, keyed in the colours the locker draws them in. */
.swatch.robot {
    background: #f8f9fb;
    border: 2px solid #171b20;
    border-radius: 50%;
    width: 15px;
}

.swatch.route {
    height: 10px;
    border-radius: 0;
    background: transparent;
    border-top: 2px dashed #f5c56f;
}

.swatch.settle {
    background: transparent;
    border: 2px solid #ee6e73;
    border-radius: 50%;
    width: 15px;
}

/* One swatch per charted objective, drawn as the chart draws it: solid is the cost the arrangement
   has, dashed the lower bound it is measured against. */
.swatch.series {
    height: 10px;
    border-radius: 0;
    background: transparent;
    border-top: 2px solid currentColor;
    border-bottom: 2px dashed currentColor;
}

.swatch.series.staging { color: var(--staging); }
.swatch.series.unloading { color: var(--unloading); }

.swatch.empty-standard { background: var(--empty-standard); }
.swatch.empty-split { background: var(--empty-split); }
.swatch.split-half { background: var(--split-half); }
.swatch.split-full { background: var(--split-full); }
.swatch.loaded-standard { background: var(--loaded-standard); }

.fact {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 3px 0;
    border-bottom: 1px solid var(--line);
}

.fact span {
    color: var(--muted);
}

.fact strong {
    font-weight: 500;
    text-align: right;
}

.tooltip {
    position: absolute;
    z-index: 2;
    max-width: 34ch;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: #0c0f13ee;
    font-size: 12px;
    pointer-events: none;
}

.tooltip-title {
    margin-bottom: 4px;
    font-weight: 600;
}

/* ---- the locker itself ---- */

#locker .cell.free {
    fill: var(--free);
    stroke: #1a2027;
    stroke-width: 0.5;
}

#locker .cell.absent {
    fill: url(#absent-hatch);
    stroke: #202832;
    stroke-width: 0.5;
}

/* Hoverable: the colour prices the slot, and the tooltip is where the price is legible as a
   figure. Everything drawn above it that is not a tray lets the hover through. */
#locker .heat-cell {
    opacity: 0.34;
}

/* The route, the decision marks and the robot are drawn over the grid but are not things to point
   at — they carry no detail of their own, and intercepting a hover would only shadow the slot
   underneath with nothing to show for it. */
#locker .route-layer,
#locker .move-layer,
#locker .robot-layer {
    pointer-events: none;
}

#locker .frontier-line {
    stroke: #fff4a8;
    stroke-width: 2;
    stroke-dasharray: 4 2;
    pointer-events: none;
    paint-order: stroke;
}

#locker .frontier-join {
    stroke-width: 1.25;
}

#locker .frontier-label {
    fill: #fff4a8;
    font-size: 8px;
    text-anchor: end;
    paint-order: stroke;
    stroke: var(--panel);
    stroke-width: 3px;
    stroke-linejoin: round;
    pointer-events: none;
}

#locker .absent-ground {
    fill: var(--absent);
}

#locker .absent-stripe {
    stroke: var(--line);
    stroke-width: 1;
}

#locker .rack-pair {
    fill: #ffffff08;
    stroke: var(--line);
}

#locker text {
    fill: var(--muted);
    font: 9px/1 ui-monospace, monospace;
    dominant-baseline: middle;
}

#locker .pair-label {
    text-anchor: middle;
    font-size: 9px;
}

#locker .rack-label {
    text-anchor: middle;
    font-size: 10px;
    fill: var(--ink);
}

#locker .slot-label {
    text-anchor: end;
}

#locker .gate {
    fill: var(--gate);
}

#locker .gate-label {
    text-anchor: middle;
    fill: #10141a;
    font-weight: 700;
}

#locker g.tray rect {
    stroke: #0f1318;
    stroke-width: 0.75;
}

#locker g.tray .marker {
    text-anchor: middle;
    fill: #10141a;
    font-size: 9px;
}

#locker g.tray:hover rect {
    stroke: var(--ink);
    stroke-width: 1.25;
}

#locker g.empty-standard rect { fill: var(--empty-standard); }
#locker g.empty-split rect { fill: var(--empty-split); }
#locker g.split-half rect { fill: var(--split-half); }
#locker g.split-full rect { fill: var(--split-full); }
#locker g.loaded-standard rect { fill: var(--loaded-standard); }

#locker g.overflowing rect {
    stroke: var(--loaded-standard);
    stroke-dasharray: 2 2;
    stroke-width: 1.5;
}

#locker .route {
    stroke: #f5c56f;
    stroke-width: 2;
    stroke-dasharray: 4 3;
    opacity: 0.8;
}

#locker .route.x { stroke-width: 3; }

#locker .settle {
    fill: none;
    stroke: #ee6e73;
    stroke-width: 2;
}

#locker .leg-seconds {
    fill: #f5d79f;
    font-size: 8px;
    paint-order: stroke;
    stroke: var(--panel);
    stroke-width: 3px;
    stroke-linejoin: round;
}

/* Where a decision's trays were picked up and set down. Both marks of a pair show at once. */

#locker .move-origin {
    fill: none;
    stroke: var(--move-target);
    stroke-width: 1.5;
    stroke-dasharray: 3 2;
}

#locker .move-target {
    fill: none;
    stroke: var(--move-target);
    stroke-width: 2;
}

#locker .move-arrow {
    stroke: var(--move-target);
    stroke-width: 1;
    opacity: 0.55;
}

#locker .move-label {
    fill: var(--move-target);
    font-size: 8px;
    text-anchor: middle;
    paint-order: stroke;
    stroke: var(--panel);
    stroke-width: 3px;
    stroke-linejoin: round;
}

#locker .robot {
    fill: #f8f9fb;
    stroke: #171b20;
    stroke-width: 2;
}

@media (max-width: 1100px) {
    main { flex-direction: column; }

    /* Stacked below the locker, the key has the whole page to be tall in. */
    aside {
        position: static;
        max-height: none;
        overflow-y: visible;
    }
}

/* --- The two objectives, one seconds axis ------------------------------------------------- */
/* One colour per objective; the bound is the same colour dashed, because a cost and the floor it
   is measured against are one reading rather than two independent series. */
:root {
    --staging: #7fd4ff;
    --unloading: #f08a8a;
}

#objective-chart {
    display: block;
    margin-top: 6px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--panel);
}

.objective-cost,
.objective-bound {
    fill: none;
    stroke-linejoin: round;
}

.objective-cost { stroke-width: 2; }

.objective-bound {
    stroke-width: 1.5;
    stroke-dasharray: 5 4;
    opacity: 0.75;
}

.objective-cost.staging,
.objective-bound.staging,
.objective-now.staging { stroke: var(--staging); }

.objective-cost.unloading,
.objective-bound.unloading,
.objective-now.unloading { stroke: var(--unloading); }

/* Filled with the page ground rather than the line colour, so the marker reads as a position on
   the line rather than as a data point of its own. */
.objective-now {
    fill: var(--bg);
    stroke-width: 2;
}

.objective-label {
    font: 11px/1 ui-sans-serif, system-ui, sans-serif;
    fill: var(--muted);
}

.objective-label.staging { fill: var(--staging); }
.objective-label.unloading { fill: var(--unloading); }

.playhead {
    stroke: var(--gate);
    stroke-width: 1.5;
}

.axis-line { stroke: var(--line); }

.axis-gridline {
    stroke: var(--line);
    stroke-dasharray: 2 4;
}

.axis-label {
    text-anchor: end;
    font: 10px/1 ui-monospace, monospace;
    fill: var(--muted);
}

.phase-band[data-parity="1"] { fill: #ffffff08; }
.phase-band[data-parity="0"] { fill: transparent; }

.phase-band-label {
    text-anchor: middle;
    font: 10px/1 ui-sans-serif, system-ui, sans-serif;
    fill: var(--muted);
}


#objective-readings,
#capacity-readings {
    max-width: 52ch;
    margin-top: 8px;
}
