/* Portolan admin — the ONLY stylesheet added on top of the vendored OTS
   console kit, loaded after console.css. Almost every rule below names a gap
   in the kit; there are no utility classes, and the single exception that
   overrides a kit component is the ultrawide `.login` rule at the bottom,
   which is called out where it sits. A later kit revision therefore still
   drops in cleanly (SPEC §4.7) — that one rule would simply become redundant
   if the kit ever bounds `.login` itself.
   Approved as part of the P4 mockup — see docs/IMPLEMENTATION.md. */

/* forms — the kit has no multi-column form grid (components.html
   does this with an inline style) */
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:1rem}
.form-grid .wide{grid-column:1/-1}

/* preformatted JSON in a code well — audit detail */
pre.codebox{margin:0;white-space:pre-wrap;word-break:normal;overflow-x:auto}

/* the login family — the kit's login.html carries these four as
   inline styles; they belong in a stylesheet, not in markup */
.loginform .btn{justify-content:center}
.loginform h2{margin-top:.5rem}
.loginform .sub{font:400 11.5px/1.5 var(--mono);color:var(--faint);margin:-.25rem 0 0}
.loginform .fine{font:400 11px/1.5 var(--mono);color:var(--faint);margin:.15rem 0 0}
.hero .status{margin-top:1.1rem}

/* enrollment — the QR beside the manual-entry key, in the hero */
.enroll{display:flex;align-items:flex-start;gap:1.1rem;margin-top:.6rem}
.enroll .manual{display:flex;flex-direction:column;gap:.6rem;flex:1;min-width:0}

/* a quota meter in a table cell, with its numbers beneath */
.quota{display:flex;flex-direction:column;gap:.35rem;min-width:132px}

/* row actions in a table cell */
.acts{display:flex;gap:.4rem}

/* an eyebrow above a heading — the kit does this for .loginform h2 only */
dialog .eyebrow + h3{margin-top:.4rem}


/* the login family on very wide displays — the one rule here that overrides a
   kit component rather than filling a gap in it.

   The kit's `.login` is two fluid columns (1.05fr .95fr) with no upper bound,
   while the content inside them is fixed: `.hero .inner` caps at 420px against
   its column's left edge, and `.loginform` caps at 340px centred in its. Below
   about 1500px that reads as an intentional split screen. Past it the two
   blocks drift apart — on a 2969px display the enrolment QR sits at the far
   left of the window and the form at the far right, with a dead zone between
   them wide enough to lose the connection between the two steps.

   Pulling both toward the centre seam rather than capping `.login` keeps the
   two-tone background full-bleed, which is the part of the design that reads
   as deliberate. Gated at a width the P4 mockup never covered, so the approved
   appearance is untouched: below 1500px this rule does nothing at all. */
@media (min-width: 1500px) {
  .hero .inner { margin-left: auto; }
  .loginpane { justify-content: flex-start; }
}


/* The live region stacks the pipeline cards above the run log. The panel's own
   gap only spaces its direct children, and this wrapper is one of them -- so
   without a gap of its own its two children abut and their card borders and
   shadows collide. Match the panel's 1.25rem section gap. */
#pipeline-state{display:flex;flex-direction:column;gap:1.25rem}

/* ── the run log ─────────────────────────────────────────────────────────
   A row per build run, expanding to that run's own stage timeline. The kit
   has no accordion component at all, so the whole family below is new.
   Approved as part of the P10 mockup — see docs/IMPLEMENTATION.md. */
.runlog{--runcols:14px 11px 142px 122px 70px 92px 82px minmax(0,1fr) 76px 190px;
  display:flex;flex-direction:column}
.runcols,.run > summary{display:grid;grid-template-columns:var(--runcols);
  gap:.85rem;align-items:center}
.runcols{padding:.6rem 1.15rem;border-bottom:1px solid var(--border);
  font:600 10.5px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
.run{border-bottom:1px solid var(--line)}
.run:last-child{border-bottom:0}
.run > summary{padding:.72rem 1.15rem;cursor:pointer;font-family:var(--mono);
  font-size:12.5px;list-style:none}
.run > summary::-webkit-details-marker{display:none}
.run > summary::marker{content:""}
.run > summary:hover{background:var(--surface-2)}
.run[open] > summary{background:var(--surface-2);box-shadow:inset 2px 0 0 var(--accent)}
.run .chev{color:var(--faint);transition:transform .12s;font-size:11px}
.run[open] .chev{transform:rotate(90deg);color:var(--accent)}
.run > summary .dot{width:9px;height:9px;border-radius:50%;background:var(--border-strong)}
.run > summary .dot.done{background:var(--good)}
.run > summary .dot.fail{background:var(--down)}
.run > summary .dot.run{background:var(--info);box-shadow:0 0 0 3px var(--info-bg)}
.run .when{color:var(--text);font-weight:600;font-variant-numeric:tabular-nums}
.run .job{color:var(--dim)} .run .ver{color:var(--faint)}
.run .dur,.run .size{color:var(--dim);font-variant-numeric:tabular-nums;text-align:right}
.run .reached{color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.run[open] .reached{color:var(--text)}
.run .live{text-align:right}
.rundetail{padding:1.15rem;border-top:1px solid var(--line)}

/* ── the provisioning row ─────────────────────────────────────────────────
   The pool coming up before a build_runs row exists: same run-log family, an
   amber "not running yet" state that hands off to the run row. P10
   provisioning-visibility change — mockup approved 2026-09-09. */
.run > summary .dot.prov{background:var(--warn);box-shadow:0 0 0 3px var(--warn-bg);
  animation:provpulse 1.6s ease-in-out infinite}
.run.prov[open] > summary{box-shadow:inset 2px 0 0 var(--warn)}
.run.prov[open] .chev{color:var(--warn)}
.pill.prov{color:var(--warn);background:var(--warn-bg)}
.pill.prov::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--warn);
  animation:provpulse 1.6s ease-in-out infinite}
@keyframes provpulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.run > summary .dot.prov,.pill.prov::before{animation:none}}
.provdetail{padding:1.15rem;display:flex;flex-direction:column;gap:1rem}
.provhead{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;font-family:var(--mono)}
.provhead h4{margin:0;font:600 13px/1.2 var(--mono);color:var(--text)}
.provhead span{font-size:11px}
.provsteps{display:flex;flex-direction:column}
.pstep{display:grid;grid-template-columns:13px minmax(0,190px) minmax(0,1fr);gap:.75rem;
  align-items:start;font-family:var(--mono);font-size:12px;padding:.5rem 0;border-bottom:1px solid var(--line)}
.pstep:last-child{border-bottom:0}
.pstep .pd{width:9px;height:9px;border-radius:50%;margin-top:.2rem;background:var(--border-strong)}
.pstep.done .pd{background:var(--good)}
.pstep.active .pd{background:var(--warn);box-shadow:0 0 0 3px var(--warn-bg);
  animation:provpulse 1.6s ease-in-out infinite}
.pstep .plabel{color:var(--faint)}
.pstep.done .plabel{color:var(--text)} .pstep.active .plabel{color:var(--text);font-weight:600}
.pstep .pnote{color:var(--dim);line-height:1.55;white-space:normal}
@media (prefers-reduced-motion:reduce){.pstep.active .pd{animation:none}}
.provfoot{font-family:var(--mono);font-size:11.5px;color:var(--faint);line-height:1.55;
  padding-left:.9rem;border-left:1px solid var(--border)}

/* ── teardown feedback ────────────────────────────────────────────────────
   The mirror of the provisioning row: a settled run whose build pool is still
   being reaped (lease active/releasing). Amber and pulsing like provisioning,
   rendered as a sibling under the run row so it shows without expanding, and it
   clears itself on the next 10s poll when the lease releases. Stop-run teardown
   feedback — approved 2026-09-09 (todos.md). */
.teardown-note{display:flex;align-items:center;gap:.55rem;
  padding:.45rem 1.15rem .55rem calc(1.15rem + 25px);
  font-family:var(--mono);font-size:12px;color:var(--warn);
  border-left:2px solid var(--warn);background:var(--warn-bg)}
.teardown-note .spin{flex:none;width:9px;height:9px;border-radius:50%;
  background:var(--warn);box-shadow:0 0 0 3px var(--warn-bg);
  animation:provpulse 1.6s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.teardown-note .spin{animation:none}}

/* ── Start-Run busy state ─────────────────────────────────────────────────
   The trigger blocks its response on the Vultr pool-create + k8s round-trip, so
   the button must not look ignored: htmx marks the in-flight form .htmx-request,
   which hides the label and shows a pulsing "Provisioning…" (pulse matches the
   provisioning row's), while hx-disabled-elt greys the button so it cannot be
   clicked twice. The provisioning row itself lands on the same beat, because
   trigger_result refreshes #pipeline-state on load rather than after a delay. */
.btn-busy{display:none;align-items:center;gap:.45rem}
.htmx-request .btn-busy{display:inline-flex}
.htmx-request .btn-label{display:none}
.btn-busy::before{content:"";width:8px;height:8px;border-radius:50%;
  background:var(--on-accent);animation:provpulse 1.6s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.btn-busy::before{animation:none}}

/* ── stage timeline ───────────────────────────────────────────────────── */
.stages{display:flex;flex-direction:column;gap:.5rem;padding:1.15rem}
.stage{display:grid;grid-template-columns:11px 168px minmax(0,1fr) 76px;gap:.75rem;
  align-items:center;font-family:var(--mono);font-size:12px}
.stage .dot{width:9px;height:9px;border-radius:50%;background:var(--border-strong)}
.stage.done .dot{background:var(--good)}
.stage.run  .dot{background:var(--info);box-shadow:0 0 0 3px var(--info-bg)}
.stage.fail .dot{background:var(--down);box-shadow:0 0 0 3px var(--down-bg)}
.stage .who{color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stage.done .who,.stage.run .who{color:var(--text)}
.stage.fail .who{color:var(--down);font-weight:600}
.stage .track{height:10px;background:var(--surface-2);border-radius:20px;overflow:hidden}
.stage .track i{display:block;height:100%;border-radius:20px;background:var(--good)}
.stage.run .track i{background:var(--info)}
.stage.fail .track i{background:var(--down)}
.stage .amt{text-align:right;color:var(--dim);font-variant-numeric:tabular-nums}
.stage.pending .amt{color:var(--faint)}

/* a stage's one-line detail, beneath the stage it explains. The mockup put
   this text in the console instead; a failed stage carries it in the database,
   so it is shown where the failure is rather than only where the log is. */
.stagenote{margin:-.15rem 0 .25rem calc(11px + .75rem);padding-left:.9rem;
  border-left:1px solid var(--border);font-family:var(--mono);font-size:11.5px;
  color:var(--dim);line-height:1.55;white-space:normal}

/* ── the two-column split inside an expanded run ──────────────────────── */
.runsplit{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:1.25rem;align-items:start}
@media (max-width:1080px){ .runsplit{grid-template-columns:minmax(0,1fr)} }

/* a grid table whose cells hold sentences rather than values */
table.grid.prose td{white-space:normal;vertical-align:top}
table.grid.prose td:first-child{white-space:nowrap}

/* ── resource facts, in the pod-and-node panel ────────────────────────── */
.facts{display:flex;flex-direction:column;gap:.95rem;padding:1.15rem}
.fact{display:flex;flex-direction:column;gap:.4rem;font-family:var(--mono);font-size:12px}
.fact .row{display:flex;justify-content:space-between;gap:.75rem}
.fact .row span{color:var(--dim)}
.fact .row b{color:var(--text);font-variant-numeric:tabular-nums}
.fact .sub{font-size:11px;color:var(--faint);line-height:1.5}

/* ── the log console ──────────────────────────────────────────────────── */
.console{margin:0;max-height:240px;overflow:auto;white-space:pre-wrap;line-height:1.65;
  padding:.9rem 1rem;border:0;border-radius:0;background:var(--bg-grain);
  font-family:var(--mono);font-size:12px;color:var(--dim);word-break:break-word}
/* one classed block per log line: the timestamp recedes, the [LEVEL] badge and
   its row take a semantic colour. WARN/ERROR tint the whole row so a failure
   reads at a glance. Levels come from views/console.rs. */
.console .ln{display:block;padding:0 .35rem;margin:0 -.35rem;border-left:2px solid transparent}
.console .ln .ts{color:var(--faint)}
.console .ln .lvl{font-weight:700}
.console .ln .msg{color:var(--dim)}
.console .ln.info .lvl{color:var(--info)}
.console .ln.warn{background:var(--warn-bg);border-left-color:var(--warn)}
.console .ln.warn .lvl,.console .ln.warn .msg{color:var(--warn)}
.console .ln.error{background:var(--down-bg);border-left-color:var(--down)}
.console .ln.error .lvl,.console .ln.error .msg{color:var(--down)}
.console .ln.debug .lvl,.console .ln.debug .msg{color:var(--faint)}


/* ── the run-a-pipeline trigger card ──────────────────────────────────────
   The kit has no trigger form; these are the approved P10 mockup's rules. */
.trigger{display:flex;flex-wrap:wrap;align-items:flex-end;gap:.9rem;padding:1.15rem}
.trigger .field{min-width:210px}
.trigger .field.wide{min-width:260px}
.policy{display:flex;flex-direction:column;gap:.55rem;padding:0 1.15rem 1.15rem}
.policy .opt{display:flex;gap:.6rem;align-items:flex-start;font-family:var(--mono);
  font-size:12px;color:var(--dim);line-height:1.55}
.policy .opt b{color:var(--text)}
.policy input{margin-top:.2rem;accent-color:var(--accent)}
.policy .callout.warn{margin-top:.4rem}
#trigger-result:not(:empty){padding:0 1.15rem 1.15rem}

/* ---- a stopped run, and the control that stops it ------------------------ */
/* A run an operator stopped is not a failure. The run log is history, and
   colouring a deliberate decision the same red as a broken build makes the red
   pill mean nothing -- so `cancelled` reads muted. `.pill.mute` is the kit's
   already; the dot and the stage rows need their own to match it. */
.run > summary .dot.mute{background:var(--faint)}
.stage.mute .dot{background:var(--faint)}
.stage.mute .who{color:var(--dim)}
.stage.mute .track i{background:var(--faint)}

/* Both headers are `space-between` flex, so a third child would centre the
   note that currently sits at the right edge. Pushing the note's own margin
   instead keeps it beside the title and sends the button to the edge, and is
   scoped to the headers that actually carry a Stop so no other card header
   moves. Without `:has()` the note simply centres -- cosmetic, not broken. */
.card-h:has(> .btn-danger) .faint,
.provhead:has(> .btn-danger) span{margin-right:auto}

/* ── the elevation product picker (sub-project B) ─────────────────────────
   A new section on the trigger card, between the trigger row and the policy
   block. Shown only for terrain and the 3DEP scratch warning only when 3DEP is
   picked — both via :has(), so no client JS is added. Approved mockup
   6f5e13c5 — see docs/IMPLEMENTATION.md. */
.elevation{display:none;border-top:1px solid var(--line);margin-top:.1rem;
  padding:1.1rem 1.15rem 0;background:var(--accent-ghost)}
form:has(#pl option[value="terrain"]:checked) .elevation{display:block}
.elev-h{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;margin-bottom:.95rem}
.elev-h .label{font-size:12px;color:var(--accent)}
.elev-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.3rem}
.elev-col{display:flex;flex-direction:column;gap:.4rem}
.col-h{margin-bottom:.2rem}
.src{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:.65rem;align-items:center;
  font-family:var(--mono);font-size:12.5px;color:var(--dim);cursor:pointer;
  padding:.4rem .55rem;border-radius:var(--radius-sm)}
.src:hover{background:var(--surface)}
.src input{accent-color:var(--accent);margin:0}
.src-main b{color:var(--text)} .src-main .dim{color:var(--dim)}
.src-status{justify-self:end;white-space:nowrap}
.uso{color:var(--warn)}
.dep-hint{font-family:var(--mono);font-size:11px;color:var(--faint);line-height:1.5;
  padding-left:.55rem;margin-top:.15rem}
.elevation .scratch{display:none;margin-top:.55rem;font-size:12px}
form:has(#s_3dep:checked) .elevation .scratch{display:flex}
@media (max-width:760px){ .elev-grid{grid-template-columns:1fr} }

/* ── build-from-downloaded + go-live (2026-09-18) ─────────────────────────
   The "build only" checkbox sits above the elevation grid it dims; the JS
   toggle (trigger.html) flips `.is-disabled` on the sources column and shows
   the skip note in its place. Same tokens, same component grammar as the
   rules above -- approved mockup 7f8e80b4, see docs/IMPLEMENTATION.md. */
.buildonly-row{padding:0 0 .95rem 0}
.elev-col.is-disabled{opacity:.4}
.elev-col.is-disabled .src{cursor:not-allowed}
.elev-col.is-disabled .src:hover{background:none}
.skip-note{font-family:var(--mono);font-size:11px;color:var(--faint);line-height:1.5;
  padding:.15rem .55rem .5rem;margin:0}

/* ── the terrain-build action strip ───────────────────────────────────────
   Resume/Restart controls for the last terrain run, between the pipeline
   cards and the "Run a pipeline" card. The kit has no such strip; these are
   the approved mockup's rules. */
.terrainbar{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;padding:.9rem 1.15rem}
.terrainbar .acts{display:flex;gap:.5rem}

/* ── the resume-preview table's skip rows ─────────────────────────────────
   `.skip` is set by the 6b resume-preview template but had no styling: a
   producer the plan will not re-run should read muted, like an already-done
   step, rather than the same weight as one that will run. */
table.grid tr.skip td{color:var(--dim)}
