/* The running demo: the same view, with rows and lanes placed by the script
   frame by frame, a scripted pointer, and the player's controls under the panel. */
.stage { display: flex; flex-direction: column; align-items: center; gap: 14px; }
/* each run fades in (a replay fades the last one out) */
.stage { animation: fade-in 300ms var(--ease-out); }

/* each project keeps its own tree, lanes and commit list, side by side in
   the page; only the one on screen shows. A project's lanes sit in a layer
   that adds no box of its own, so they place against the timeline as before */
.lanes-layer { display: contents; }
.demo :is(.lanes-layer, .tree-art, .branches ul, .commits)[hidden] { display: none; }

/* rows and lanes sit at a y the script eases toward, so they can slide in,
   shift and tuck away */
.demo .branches ul { display: block; }
.demo .branch { position: absolute; top: 0; left: 0; right: 0; will-change: transform, opacity; }
.demo .branch .name { display: inline-block; transition: color 300ms ease; }
/* a finished branch's row: greyed, free for the next one */
.demo .branch .badge { transition: opacity 300ms ease, filter 300ms ease; }
.demo .branch.merged .name { color: var(--faint); }
.demo .branch.merged .badge { opacity: .45; filter: grayscale(1); }
/* a branch dropped without merging is struck through as well */
.demo .branch.closed .name { text-decoration: line-through; text-decoration-color: var(--faint); }
.lane { position: absolute; top: 0; left: 0; width: 100%; will-change: transform, opacity; }
.lane > .bar, .lane > .agent, .lane > .status { top: 36px; }
.lane > .commit-mark { top: 36px; z-index: 1; }
.lane > .bar { transition: opacity 400ms ease, outline-color 150ms ease; }
/* a live stretch is a workspace you can open: under the pointer (or its
   agent or status) its frame thickens to 2px, drawn just inside the 1px
   border so the bar keeps its size, and stays so while its panel is open */
.lane > .bar.live { outline: 1px solid transparent; outline-offset: -2px; }
.lane > .bar.live.open { outline-color: var(--edge); }
/* a past stretch opens the commit it ended in: too faint for a frame to
   show, it deepens as a whole instead, under the pointer and while open */
.lane > .bar.past[data-cid] { transition: opacity 200ms ease; }
.lane > .bar.past.open { opacity: .7; }
@media (hover: hover) and (pointer: fine) {
  .lane > .bar.live:hover, .lane:has(> .agent:hover, > .status:hover) > .bar.live { outline-color: var(--edge); }
  .lane > .bar.past[data-cid]:hover { opacity: .7; }
}
/* ticks are placed by transform from the timeline's left edge */
.demo .tick, .demo .tick-text { transition: opacity 150ms ease; }
.demo .tick { left: 0; }
.demo .tick.edge { align-items: flex-start; }
/* only Now's label takes the pointer: the tick lines lie over the lanes; no tick text selects */
.demo .tick { pointer-events: none; user-select: none; -webkit-user-select: none; }
.demo .tick.now .tick-label { pointer-events: auto; cursor: grab; margin: -2px 0; padding: 2px 5px; border-radius: 5px; transition: background-color 150ms ease; }
.demo .tick.now.grabbed .tick-label { cursor: grabbing; }
.demo .tick.now.grabbed .tick-label, .demo .tick.now.off .tick-label { background: var(--accent-bg); }
@media (hover: hover) and (pointer: fine) { .demo .tick.now .tick-label:hover { background: var(--accent-bg); } }
/* looking back: Now's line stays at the live edge, and what came after the
   moment being looked at sits behind a veil */
.live-line { position: absolute; top: 28.5px; left: 0; width: 1px; height: 930.5px; background: rgba(34, 108, 187, .25); pointer-events: none; }
.after-view { position: absolute; top: 33px; height: 943px; z-index: 2; background: rgba(255, 255, 255, .6); pointer-events: none; }
/* the stats column stays above the veil, which runs under it toward the live edge */
.demo .stats { z-index: 5; }
.commits li { transition: opacity 150ms ease; }
.commits li.later { opacity: .35; }

/* a warning reads in place of the status: red on red. When another agent
   joins or leaves the branch, the status slides to make room or close up */
.status { transition: background-color 150ms ease, color 150ms ease, translate 220ms var(--ease-out); }
.lane > .agent.crew { transition: translate 220ms var(--ease-out); }
.lane > .agent.crew.leave { transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out), translate 220ms var(--ease-out); }
.status.warn { background: #fbe7ec; color: var(--del); }

/* new things arrive from slightly smaller and transparent, never from nothing */
@keyframes arrive { from { opacity: 0; transform: scale(0.9); } }
/* An agent mounting onto a branch grows out of the end of its stretch: from
   half size, at its left edge, to full, as it fades in; its status follows a
   beat later from 90%. Rare (a few times a run), so it may be seen. With
   reduced motion both only fade in */
@keyframes agent-in { from { opacity: 0; transform: scale(0.5); } }
@keyframes fade-in { from { opacity: 0; } }
.agent.arrive { transform-origin: 0 50%; animation: agent-in 260ms var(--ease-out); }
.status.arrive { transform-origin: 0 50%; animation: arrive 220ms var(--ease-out) 60ms backwards; }
@media (prefers-reduced-motion: reduce) { .agent.arrive, .status.arrive { animation: fade-in 200ms ease; } }
/* leaving, it goes back the way it came, a little faster: the square shrinks
   to half size into the end of its stretch as it fades, its status fades
   down to 90% first (reduced motion: they only fade) */
.agent.leave { transform-origin: 0 50%; opacity: 0; transform: scale(0.5); transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out); }
.status.leave { transform-origin: 0 50%; opacity: 0; transform: scale(0.9); transition: opacity 140ms var(--ease-out), transform 140ms var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .agent.leave, .status.leave { transform: none; } }
@keyframes mark-in { from { opacity: 0; transform: scaleY(0.4); } }
.commit-mark.arrive { animation: mark-in 200ms var(--ease-out); }

/* a mark names itself on hover (Commit), and a line to main says what it published */
.commit-mark > .name-tip { bottom: calc(100% + 6px); }
.merge-line::before { content: ""; position: absolute; inset: 0 -4px; }
/* a commit's details say where it went: merged or published (with the
   connector's glyph, in its branch's colour), or not yet */
.commit-note { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: 7px; background: #f8f8f8; color: var(--text-2); line-height: 16px; }
.commit-note .note-text { color: var(--text); white-space: nowrap; }
.commit-note .note-more { color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-glyph { flex: none; }
.note-glyph svg { display: block; fill: none; stroke: var(--edge); stroke-width: 1.16667; stroke-linecap: round; stroke-linejoin: round; }
.merge-line > .name-tip { bottom: auto; top: -26px; }
@media (hover: hover) and (pointer: fine) {
  .merge-line:hover { z-index: 3; }
  .commit-mark:hover > .name-tip, .merge-line:hover > .name-tip { opacity: 1; transform: translateX(-50%); visibility: visible;
    transition: opacity 125ms var(--ease-out) 300ms, transform 125ms var(--ease-out) 300ms, visibility 0s 300ms; }
}

/* new commits open their row at the top of the list */
/* (rows keep their height: clipping lets a flex item shrink, so they must not) */
.commits li { overflow: hidden; flex: none; }

/* the storage grid drains as the history grows */
.cell { transition: background-color 400ms ease; }
.cell .part { transition: width 400ms ease, height 400ms ease; }

/* a project's sandboxes: each card unfolds with its first agent and folds
   away after its last, so the commits move up under the count (a folded card
   takes its 4px gap with it). Switching projects sets them at once */
.sb-fold { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 220ms var(--ease-out), margin-top 220ms var(--ease-out); }
.sb-fold.shut { grid-template-rows: 0fr; margin-top: -4px; }
.sb-fold > .sandbox { min-height: 0; overflow: hidden; transition: transform var(--press) var(--ease-out), background-color 150ms ease, opacity 200ms ease, padding 220ms var(--ease-out); }
.sb-fold.shut > .sandbox { padding-block: 0; opacity: 0; pointer-events: none; }
.demo .sandboxes.still * { transition: none; }
@media (prefers-reduced-motion: reduce) { .sb-fold, .sb-fold > .sandbox { transition: none; } }

/* the timeline can be dragged to look back */
.demo .timeline { cursor: grab; touch-action: pan-y; }
.demo .timeline.dragging { cursor: grabbing; user-select: none; }
.demo .timeline :is(.diff-panel, .sandbox-pop, .stats, button) { cursor: auto; }
.demo .timeline button, .demo .lane > :is(.bar.live, .bar.past[data-cid], .agent, .status) { cursor: pointer; }
/* the time ruler: the band of tick labels above the lanes, as wide as they
   are; a click there brings the Now handle to that time */
.ruler { position: absolute; top: 0; left: 0; width: 832px; height: 33px; cursor: pointer; }

/* the scrollbar under the lanes, drawn as the design's: a 36px well with a
   1px rule, and an 8px grey thumb as long as the share of history in view.
   Drag the thumb, or press the well to glide there */
/* (it sits above the veil that dims what came after a moment looked back at) */
.demo .scrubber { z-index: 4; background: #fff; border: 1px solid var(--line); border-radius: 9px; cursor: auto; }
.scrub-track { position: absolute; left: 9px; right: 9px; top: 17px; height: 1px; border-radius: 1px; background: var(--line); }
.scrub-thumb { position: absolute; left: 9px; top: 13px; height: 8px; border-radius: 2px; background: #888; transition: background-color 150ms ease; }
/* the thumb is 8px tall; a taller invisible hit area makes it easy to take */
.scrub-thumb::before { content: ""; position: absolute; inset: -9px 0; }
.scrub-thumb.held { background: #6b6b6b; }
@media (hover: hover) and (pointer: fine) { .scrub-thumb:hover { background: #767676; } }

/* a workspace's panel is a dock along the bottom, as wide as the lanes and
   just above the scrollbar, so the branches and the live edge stay in view.
   It rises from below and sinks back faster than it came */
.diff-panel.dock { top: auto; left: 8px; right: 336px; bottom: 52px; width: auto; height: var(--dock-h, 440px); transform-origin: 50% 100%; }
.diff-panel.dock:not([data-open]) { transform: translateY(8px) scale(0.99); }
@media (prefers-reduced-motion: reduce) { .diff-panel.dock:not([data-open]) { transform: none; } }
.diff-panel.dock .diff-body { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 12px; overflow: hidden; padding-bottom: 0; }
/* grown, it takes the whole timeline, scrollbar and all; it grows up from
   the dock and settles back down. With reduced motion it simply changes size */
.diff-panel.dock[data-open] { transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out), height 260ms var(--ease-out), bottom 260ms var(--ease-out), visibility 0s; }
.diff-panel.dock.max { bottom: 8px; height: calc(100% - 16px); }
@media (prefers-reduced-motion: reduce) { .diff-panel.dock[data-open] { transition: opacity 220ms var(--ease-out), visibility 0s; } }
.ws-grow svg { display: block; fill: none; stroke: #949494; stroke-width: 1.16667; stroke-linecap: round; stroke-linejoin: round; }
.ws-grow .i-shrink, .diff-panel.max .ws-grow .i-grow { display: none; }
.diff-panel.max .ws-grow .i-shrink { display: inline; }
.diff-panel:not(.dock) :is(.ws-grow, .ws-resize) { display: none; }
/* the top edge of the dock takes a drag to resize it: an 8px band with the
   ns-resize cursor, and a short pill that shows under the pointer or while
   dragging; the height follows the pointer with no transition */
.ws-resize { position: absolute; top: 0; left: 0; right: 0; height: 8px; z-index: 1; cursor: ns-resize; touch-action: none; }
.ws-resize::after { content: ""; position: absolute; top: 3px; left: 50%; width: 32px; height: 3px; margin-left: -16px; border-radius: 2px; background: var(--line); opacity: 0; transition: opacity 150ms ease; }
.diff-panel.sizing .ws-resize::after { opacity: 1; }
@media (hover: hover) and (pointer: fine) { .ws-resize:hover::after { opacity: 1; } }
.diff-panel.dock.sizing { transition: none; user-select: none; }
.ws-col { min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; padding-bottom: 12px; scrollbar-width: thin; scrollbar-color: #d6d6d6 transparent; }
.ws-col > * { flex: none; }

/* a workspace in the panel: its agent, still at work, where a commit's number
   sits; its status as on the lane; then its terminal and uncommitted changes */
.ws-crew { display: flex; flex: none; margin: 2px; }
.ws-crew .agent { position: relative; flex: none; }
/* everyone at work, stacked as on the lane: each square 6px over the last */
.ws-crew .agent + .agent { margin-left: -6px; }
.ws-status { height: 20px; display: flex; align-items: center; padding: 0 8px; border-radius: 4px; background: #f1f1f1; font-size: 10px; letter-spacing: -0.3px; line-height: 1; white-space: nowrap; flex: none;
  transition: background-color 150ms ease, color 150ms ease; }
.ws-status.warn { background: #fbe7ec; color: var(--del); }
.ws-label { color: var(--muted); margin-bottom: -4px; }
.ws-term { border: 1px solid var(--well); border-radius: 7px; background: #f8f8f8; padding: 6px 8px; font: 11px/18px var(--mono); letter-spacing: normal; color: var(--text-2); white-space: pre; overflow: hidden; }
.ws-term .cmd { color: var(--text); }
.ws-term .fail { color: var(--del); }
/* the caret after the latest line says the command is still running */
.ws-term .live::after { content: ""; display: inline-block; width: 6px; height: 11px; margin-left: 4px; vertical-align: -1px; background: var(--faint); animation: caret 1s steps(1) infinite; }
@keyframes caret { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ws-term .live::after { animation: none; } }

/* a recording leaves the player out (?bare) */
.bare .demo-bar { display: none; }
/* the player: under the panel, as wide as it */
.demo-bar { width: calc(1440px * var(--fit, 1)); display: flex; align-items: center; gap: 10px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* the player's buttons are the view's outline icon buttons, on white; their
   icons are drawn like the view's: on a 14px keyline, about 8px across,
   centred by eye, in a 1.17px stroke */
.icon-btn.outline.demo-btn { background: #fff; }
.demo-btn svg { display: block; fill: none; stroke: #949494; stroke-width: 1.16667; stroke-linecap: round; stroke-linejoin: round; }
.demo-btn .i-play { display: none; }
.demo-btn.paused .i-play { display: inline; }
.demo-btn.paused .i-pause { display: none; }
@media (hover: hover) and (pointer: fine) { .icon-btn.outline.demo-btn:hover { background: var(--hover); } }
.demo-track { position: relative; flex: 1; height: 4px; border-radius: 2px; background: #dcdcdc; overflow: hidden; }
.demo-fill { position: absolute; inset: 0 auto 0 0; width: 0; background: var(--accent); }
.demo-time { flex: none; }
