/* Configurator styling. The design system is /shared/styles/brand.css, which
 * this page LINKS — it holds no palette of its own.
 *
 * That is what lets the reader re-ground it. Every colour here is a token
 * brand.css defines and shared/theme/theme-dark.css redefines, so the dark
 * palette arrives without this file knowing it exists. The moment a rule states
 * a literal instead, that rule is correct in one theme and wrong in the other —
 * so both sides of every colour pair follow the palette, or neither does.
 *
 * What remains below is only what is specific to this form: its two-column
 * layout, the repeatable rows, and the bill.
 */

* { box-sizing: border-box; }

html {
  background: var(--bg);
  height: 100%;
  overflow: hidden;
}

body {
  margin: 0;
  /* The page is full-height and does not scroll as a whole — the two columns
     scroll independently. Everything else about this element (the reading face,
     weight 300, the palette) is brand.css's, and restating any of it here is
     how the system face survived a self-hosted Inter being loaded four files
     away. */
  height: 100%;
  overflow: hidden;
}

main#app {
  display: flex;
  flex-direction: column;
  max-width: 1200px;
  height: 100vh;
  margin: 0 auto;
  padding: 24px 28px 20px;
}

/* ---------- Header ---------- */

header {
  flex: 0 0 auto;
  margin-bottom: 12px;
  /* The switch sits at the far right of the head, level with the title, which
     is where every other Drift surface puts it. */
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

header .head-text {
  min-width: 0;
}

/* The switch is a control, not text: it keeps its size and never stretches to
   the head's height. */
header #theme-switch {
  flex: 0 0 auto;
}

header h1 {
  font-size: 1.5rem;
  font-weight: 600;
  margin: 0 0 0.15rem;
  line-height: 1.1;
}

header p {
  margin: 0;
  font-size: 13px;
  font-weight: 300;
  color: var(--muted);
}

/* ---------- Section labels ---------- */

#breakdown h2 {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink);
  margin: 0 0 8px;
}

/* ---------- Form columns ---------- */

/* Two-column layout pinned to the remaining viewport height. Each
 * column scrolls independently so the page never scrolls as a whole. */
#form {
  flex: 1 1 0;
  min-height: 0;
}

.form-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 28px;
  height: 100%;
}

.form-col-left,
.form-col-right {
  overflow-y: auto;
  padding-right: 6px;
}

/* Thin, subtle scrollbar so it doesn't eat into the layout. */
.form-col-left::-webkit-scrollbar,
.form-col-right::-webkit-scrollbar {
  width: 4px;
}
.form-col-left::-webkit-scrollbar-thumb,
.form-col-right::-webkit-scrollbar-thumb {
  background: var(--ink-subtle);
}

/* ---------- Form ---------- */

form fieldset {
  background: var(--panel);
  border: 1px solid var(--ink);
  border-radius: 0;
  padding: 14px 18px;
  margin: 12px 0;
}

form fieldset:first-child {
  margin-top: 0;
}

/* THE KEYCAP — a primitive's name, moulded as a key.
 *
 * The variant that won the brand's element panel 3/3, at legend scale. Its rule
 * is that the raise is SEMANTICS: a key that stands proud goes somewhere, a key
 * lying flat only names something. These name the section they open and lead
 * nowhere, so they are flat — no lift, no shadow, and no hover. Spending the
 * raised position on decoration is what would make it stop meaning anything.
 *
 * Ink on the fill, not white: white on Atomic amber is 2.15:1 and on Canvas
 * green 2.54:1, against ink's 6.82 and 5.77. --on-brand is that ink, and it is
 * deliberately NOT a var of --ink — the triad does not move between themes, so
 * what is written on it must not either.
 */
.keycap {
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
  height: 20px;
  border: 2px solid var(--ink);
  background: var(--ink);
  color: var(--on-brand);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* One object. A key broken across two lines is two half keys. */
  white-space: nowrap;
}

.keycap--atomic   { background: var(--brand-1); }
.keycap--backbone { background: var(--brand-2); color: var(--panel); }
.keycap--canvas   { background: var(--brand-3); }

/* Backbone purple clears neither white (4.06:1) nor ink (3.61) at this size —
 * an open divergence the brand records rather than hides. White is the closer
 * of the two, so it is what the key wears, matching every other Backbone fill
 * on the platform. --panel rather than #fff because the fill is themeable and
 * a literal here would be the exact fault this file exists to avoid. */

/* A legend holding a key IS the key: it drops its own tab chrome so the two
 * boxes do not nest. The sections named after a primitive wear the key; Slice
 * and Billing are not primitives and keep the plain tab. */
form legend:has(.keycap) {
  padding: 0;
  background: none;
  border: 0;
  margin-bottom: 0;
}

form legend {
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink);
  background: var(--panel);
  border-top: 1px solid var(--ink);
  border-left: 1px solid var(--ink);
  border-right: 1px solid var(--ink);
  border-bottom: 1px solid var(--panel);
  margin-bottom: -1px;
}

form label {
  display: grid;
  grid-template-columns: 1fr 140px 100px 80px;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  font-size: 13px;
  font-weight: 300;
  min-height: 36px;
}

form input[type="number"],
form input[type="text"] {
  background: var(--panel);
  border: 1px solid var(--ink);
  color: var(--ink);
  padding: 6px 10px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  border-radius: 0;
  text-align: left;
  width: 100%;
}

form input[type="number"]:focus,
form input[type="text"]:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--brand-2);
}

/* Hide native browser steppers — we render our own [-] / [+] buttons. */
form input[type="number"]::-webkit-outer-spin-button,
form input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
form input[type="number"] {
  -moz-appearance: textfield;
}

/* ---------- Stepper ---------- */

/* DOM order inside .stepper is [input, minus, plus] so the <input>
 * becomes the label's first labelable descendant — see the comment in
 * enhanceStepper() in configurator.js for why that matters. We restore
 * the visual [−] [input] [+] order here with the `order` property. */
.stepper {
  display: flex;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
  width: 100%;
}

.stepper input[type="number"] {
  order: 2;
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  padding: 6px 4px;
  font-weight: 600;
}

.stepper-minus {
  order: 1;
}

.stepper-plus {
  order: 3;
}

.stepper button {
  flex: 0 0 30px;
  min-width: 0;
}

/* Stepper buttons look like physical pushbuttons: raised-by-default (4px
 * translate + hard shadow), change color on hover to signal intent, and
 * visibly depress into the surface on :active. This is the same "toy"
 * affordance used across drift-website, just inverted — the website
 * buttons are flat at rest and rise on hover, while these steppers are
 * already proud of the surface so the press animation reads as a real
 * click. */
.stepper button {
  background: var(--panel);
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  padding: 0;
  border-radius: 0;
  cursor: pointer;
  user-select: none;
  transform: translateY(-3px);
  box-shadow: 0 3px 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s ease, color 0.15s ease;
}

/* Only the hovered button changes color. No transform on hover — the
 * button is already raised, and moving it up more would fight the
 * "press to depress" mental model. */
.stepper button:hover:not(:disabled) {
  background: var(--brand-1);
  color: var(--on-brand);
}

/* Pressed: the button slams down flush with the surface. */
.stepper button:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: 0 0 0 var(--ink);
}

.stepper button:focus { outline: none; }
.stepper button:focus-visible {
  outline: 2px solid var(--brand-2);
  outline-offset: 2px;
}

/* Disabled = flat on the surface, no shadow, no lift. */
.stepper button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: var(--panel);
  color: var(--ink);
  transform: translateY(0);
  box-shadow: none;
}

form input[type="number"]:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background: var(--panel);
  color: var(--ink);
}

.unit {
  color: var(--muted);
  font-size: 11px;
  font-weight: 300;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Per-row subtotal, lives in the 4th label column and is updated from
 * the live-pricing response via data-key. We match the item-to-row by
 * key, not by human-readable label, so renaming copy in the backend
 * doesn't silently break the UI wiring. Brand-3 green is the same
 * colour the big "prepaid total" uses — it's the "this is money" colour
 * of the design system. */
.subtotal {
  color: var(--brand-3);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* ---------- Guardrails ---------- */

/* Read-only list of per-preset limits (RPM, runtime, storage caps, etc.)
 * shown when a preset is highlighted. Uses a <dl> rendered as a two-column
 * grid so labels and values line up neatly without any input controls. */
#guardrails-fieldset dl {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 3px 20px;
  margin: 0;
  padding: 0;
}

#guardrails-fieldset dt {
  font-size: 12px;
  font-weight: 300;
  color: var(--ink);
  padding: 3px 0;
}

#guardrails-fieldset dd {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin: 0;
  padding: 3px 0;
  text-align: right;
}

/* ---------- Breakdown / totals ---------- */

#breakdown {
  background: var(--panel);
  border: 1px solid var(--ink);
  border-radius: 0;
  padding: 14px 18px;
  margin: 12px 0;
}

#breakdown ul {
  list-style: none;
  padding: 0;
  margin: 0 0 12px;
  font-size: 13px;
}

#breakdown li {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  border-bottom: 1px solid var(--ink-subtle);
  font-weight: 300;
}

#breakdown li:last-child {
  border-bottom: none;
}

#breakdown li .label {
  color: var(--ink);
}

#breakdown li .qty {
  color: var(--muted);
  margin-left: 8px;
}

#totals {
  border-top: 2px solid var(--ink);
  padding-top: 10px;
  font-size: 14px;
}

#totals p {
  display: flex;
  justify-content: space-between;
  margin: 4px 0;
  font-weight: 300;
}

#totals p strong {
  font-weight: 600;
}

#totals p:last-child span {
  color: var(--brand-3);
  font-weight: 700;
  font-size: 16px;
}

/* ---------- Submit ---------- */

#actions {
  margin-top: 12px;
}

#actions button {
  display: block;
  width: 100%;
  background: var(--brand-1);
  color: var(--on-brand);
  border: 1px solid var(--ink);
  border-radius: 0;
  font-family: inherit;
  font-weight: 600;
  font-size: 14px;
  padding: 12px 24px;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

#actions button:hover:not(:disabled) {
  transform: translateY(-3px);
  box-shadow: 0 3px 0 var(--ink);
}

#actions button:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: 0 1px 0 var(--ink);
}

#actions button:focus { outline: none; }
#actions button:focus-visible {
  outline: 2px solid var(--brand-2);
  outline-offset: 2px;
}

#actions button:disabled {
  opacity: 0.55;
  cursor: wait;
}

#status {
  margin-top: 8px;
  font-size: 13px;
  font-weight: 300;
  color: var(--muted);
}

#status.error {
  color: var(--error);
  font-weight: 600;
}


/* ---------- Hacker mode ---------- */

/* Hacker mode is toggled by writing data-active-preset="hacker" on
 * <form>. Everything below is a CSS-driven reaction to that state:
 *
 *   - Hide every "× €0.50/mo" unit label. In hacker mode the math
 *     would read "N × €0.50/mo = included", which is noisy and
 *     contradictory — the green "included" subtotal on the right
 *     already tells the whole story.
 *   - Swap the Billing fieldset for a fixed "Hacker slices are free
 *     forever" note. Hacker slices skip billing entirely (see the
 *     case "hacker" branch in api/routes/slice_lifecycle.go), so the
 *     months input would be a lie.
 *
 * #hacker-billing-note is hidden by default so it never flashes into
 * view before the JS has set the data attribute. It is only shown
 * when hacker mode is active. */
#hacker-billing-note {
  display: none;
}

/* Scoped to the resource fieldsets — the slice name fieldset uses a
 * .unit span too ("only alphanumeric characters") and that hint has
 * nothing to do with pricing, so it must stay visible in every mode. */
#form[data-active-preset="hacker"] fieldset:not(#name-fieldset) .unit {
  display: none;
}

#form[data-active-preset="hacker"] #billing-fieldset {
  display: none;
}

#form[data-active-preset="hacker"] #hacker-billing-note {
  display: block;
}

/* Named, sized resources: functions, collections, databases, buckets. Each is
 * a list the user adds to, because each declared item carries its own figure
 * and the bill is their sum. */
.repeat {
  margin-bottom: 14px;
}

.repeat-title {
  font-weight: 700;
  font-size: 14px;
  margin: 0 0 6px;
}

.repeat-title .unit {
  font-weight: 400;
  margin-left: 6px;
}

.repeat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}

.repeat-row .repeat-name {
  flex: 1 1 auto;
  min-width: 0;
}

.repeat-row .repeat-size {
  width: 84px;
  flex: 0 0 auto;
}

.repeat-row .repeat-route {
  flex: 1 1 auto;
  min-width: 0;
}

.repeat-row .repeat-memory {
  width: 72px;
  flex: 0 0 auto;
}

/* The method is a closed list, so it is a select rather than a field — the row
 * cannot express a method the platform refuses. Fixed width, so the route beside
 * it does not resize as the method changes. */
.repeat-row .repeat-method {
  flex: 0 0 auto;
  width: 96px;
  font-family: inherit;
  font-size: 13px;
  padding: 5px 6px;
  border: 1px solid var(--ink);
  border-radius: 0;
  background: var(--panel);
  color: var(--ink);
}

/* The row's own unit label sits between the field and its remove button, so it
 * must not stretch the way the ones in a grid-laid-out label do. */
.repeat-row .unit {
  flex: 0 0 auto;
}

/* Marks the field a refusal named. Inset, so marking one does not shift the
 * fields around it — the tenant is reading a list of numbers and a row that
 * moves as it is marked is a row they lose. */
.refused {
  outline: 2px solid var(--error);
  outline-offset: -2px;
}

/* The applied shape, offered as a file on the terminal screens. A button-shaped
 * link with the same toy affordance as everything else here: raised at rest,
 * flat when pressed. */
.shape-download {
  display: inline-block;
  margin-top: 18px;
  padding: 9px 14px;
  border: 1px solid var(--ink);
  background: var(--panel);
  color: var(--ink);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  border-radius: 0;
  transform: translateY(-3px);
  box-shadow: 0 3px 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s ease, color 0.15s ease;
}

.shape-download:hover {
  background: var(--brand-1);
  color: var(--on-brand);
}

.shape-download:active {
  transform: translateY(0);
  box-shadow: 0 0 0 var(--ink);
}

/* Why the file matters, said once and not in a warning voice. */
.shape-note {
  margin: 12px auto 0;
  max-width: 34rem;
  font-size: 12px;
  font-weight: 300;
  line-height: 1.45;
}

/* The list of what a resize takes away, and the field that authorises it. Sits
 * above the button because it is what the button now does. */
#confirm-panel {
  border: 1px solid var(--ink);
  padding: 10px 12px;
  margin-bottom: 12px;
  font-size: 12px;
  font-weight: 300;
}

#confirm-panel #confirm-lead {
  margin: 0 0 6px;
  font-weight: 600;
}

#confirm-panel ul {
  margin: 0 0 10px;
  padding-left: 18px;
  line-height: 1.45;
}

#confirm-panel li {
  margin-bottom: 4px;
}

/* Stacked, not the grid the resource labels use — this label is a sentence with
 * a field under it, not a row in a table of numbers. */
#confirm-panel #confirm-label {
  display: block;
  font-size: 12px;
  font-weight: 300;
}

#confirm-panel #confirm-input {
  margin-top: 4px;
  width: 100%;
}

/* Said before the button, not after it: a slice leaving the free tier is
 * permanent, and a consequence a person reads only once they have paid is one
 * this platform would be hiding. Bordered rather than tinted — the surface has
 * no filled callouts. */
.leaving-free {
  margin: 10px 0 0;
  padding: 8px 10px;
  border: 1px solid var(--ink);
  font-size: 12px;
  font-weight: 300;
  line-height: 1.45;
}

/* A marked GROUP is a box rather than a field, so it gets the rule around its
 * rows instead of inside each one. */
.repeat.refused {
  outline-offset: 2px;
}

/* Marks a declared slot nothing serves. Reads as a fact rather than an error:
 * the tenant may be about to deploy into it, and it is also exactly what a pool
 * they are paying for and never reaching looks like. */
.repeat-flag {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  padding: 2px 5px;
  border: 1px solid var(--ink);
  background: var(--surface, transparent);
}

/* Says why a group's rows arrived blank. Weighted like body text rather than a
 * warning: the tenant is being asked for something the platform never recorded,
 * which is the platform's gap, not their mistake. */
.repeat-note {
  font-size: 12px;
  font-weight: 300;
  line-height: 1.4;
  margin: 2px 0 8px;
}

/* Same toy affordance as every other button here: raised at rest, slammed
 * flat on click. */
.repeat-add,
.repeat-remove {
  background: var(--panel);
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: inherit;
  font-weight: 700;
  line-height: 1;
  border-radius: 0;
  cursor: pointer;
  user-select: none;
  transform: translateY(-3px);
  box-shadow: 0 3px 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.15s ease, color 0.15s ease;
}

.repeat-add {
  font-size: 13px;
  padding: 6px 10px;
}

.repeat-remove {
  font-size: 15px;
  padding: 4px 9px;
  flex: 0 0 auto;
}

.repeat-add:hover,
.repeat-remove:hover {
  background: var(--brand-1);
  color: var(--on-brand);
}

.repeat-add:active,
.repeat-remove:active {
  transform: translateY(0);
  box-shadow: 0 0 0 var(--ink);
}

.repeat-add:focus,
.repeat-remove:focus { outline: none; }
.repeat-add:focus-visible,
.repeat-remove:focus-visible {
  outline: 2px solid var(--brand-2);
  outline-offset: 2px;
}

.hacker-free-text {
  margin: 0;
  padding: 2px 0;
  font-size: 13px;
  font-weight: 300;
  color: var(--brand-3);
}

/* ---------- Success screen ---------- */

/* Replaces the entire #app contents on successful slice creation. The
 * form, sidebar, header — everything goes away and is replaced by a
 * single centered checkmark + headline. The min-height pin and the
 * negative margins clear out main#app's own padding so the success
 * panel actually fills the viewport instead of sitting in a tall
 * gutter. The checkmark badge re-uses the same raised-on-ink-shadow
 * affordance as every other interactive element on the page so the
 * goodbye still feels like the same product. */
.success-screen {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 28px;
  height: 100%;
  text-align: left;
}

.success-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 88px;
  height: 88px;
  background: var(--brand-3);
  color: var(--on-brand);
  border: 1px solid var(--ink);
  border-radius: 0;
  font-size: 56px;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 4px 0 var(--ink);
  transform: translateY(-4px);
}

.success-screen h1 {
  margin: 0;
  font-size: 2.25rem;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.1;
}

/* Provisioning view: shown after submit while we poll the api for slice
 * readiness. Layout mirrors the success screen so the transition between
 * the two is just "spinner becomes checkmark, text changes" — no
 * jarring re-flow when the slice finally comes online. The spinner is
 * pure CSS (no SVG, no JS) because we deliberately keep this page
 * dependency-free. */
.provisioning-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  height: 100%;
  overflow-y: auto;
  text-align: center;
}

.provisioning-screen h1 {
  margin: 0;
  font-size: 2rem;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.1;
}

.provisioning-sub {
  margin: 0;
  max-width: 28rem;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.4;
}

/* Static square whose edges light up one at a time in the brand colors.
 * No rotation — progress is communicated through color, not motion.
 * Sequence: top (orange) → right (purple) → bottom (green) → left (ink). */
.spinner {
  display: inline-block;
  width: 56px;
  height: 56px;
  border: 4px solid var(--ink-subtle);
  background: transparent;
  animation: edge-fill 2.4s linear infinite;
}

@keyframes edge-fill {
  0%, 19%   { border-top-color: var(--brand-1);
              border-right-color: var(--ink-subtle);
              border-bottom-color: var(--ink-subtle);
              border-left-color: var(--ink-subtle); }
  25%, 44%  { border-top-color: var(--ink-subtle);
              border-right-color: var(--brand-2);
              border-bottom-color: var(--ink-subtle);
              border-left-color: var(--ink-subtle); }
  50%, 69%  { border-top-color: var(--ink-subtle);
              border-right-color: var(--ink-subtle);
              border-bottom-color: var(--brand-3);
              border-left-color: var(--ink-subtle); }
  75%, 94%  { border-top-color: var(--ink-subtle);
              border-right-color: var(--ink-subtle);
              border-bottom-color: var(--ink-subtle);
              border-left-color: var(--ink); }
  100%      { border-top-color: var(--brand-1);
              border-right-color: var(--ink-subtle);
              border-bottom-color: var(--ink-subtle);
              border-left-color: var(--ink-subtle); }
}

/* Terminal states — animation stops, all edges snap to one color. */
.spinner-success {
  animation: none;
  border-color: var(--brand-3);
}

.spinner-error {
  animation: none;
  border-color: var(--error);
}

/* ---------- Provisioning progress bars ---------- */

/* Per-component provisioning list. Rendered inside .provisioning-screen
 * as a vertical stack of rows — one per slice component (backbone,
 * atomic, canvas). Each row has a label area (name + description) on
 * top and a 4-segment progress bar below. The bar fills left-to-right
 * as the operator progresses through: requested → installed → running
 * → ready. */
.provisioning-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  max-width: 480px;
  text-align: left;
}

.provisioning-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--panel);
  border: 1px solid var(--ink);
  padding: 16px 18px 32px;
  /* Raised-on-ink-shadow, same affordance as every other card on the
   * page. Extra bottom padding accommodates the phase labels that hang
   * below the bar via ::after pseudo-elements. */
  transform: translateY(-4px);
  box-shadow: 0 4px 0 var(--ink);
  transition: background 0.2s ease;
}

.provisioning-row[data-state="ready"] {
  background: var(--panel);
}

.provisioning-row-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.provisioning-row-body strong {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.provisioning-row-sub {
  font-size: 12px;
  font-weight: 300;
  color: var(--muted);
}

/* The bar is a horizontal strip of 4 equal-width segments separated by
 * small gaps. Each segment starts transparent (just an ink border) and
 * fills with the component's brand colour when the phase completes.
 * The colour is set via --bar-color on the .provisioning-bar element so
 * a single set of rules supports all three brand colours. */
.provisioning-bar {
  display: flex;
  gap: 4px;
  width: 100%;
  height: 28px;
}

.provisioning-segment {
  flex: 1 1 0;
  border: 1px solid var(--ink);
  background: transparent;
  position: relative;
  overflow: hidden;
  transition: background 0.3s ease;
}

.provisioning-segment.active {
  background: var(--bar-color);
}

/* Phase labels rendered below each segment via ::after. Tiny contextual
 * text — Requested / Installed / Running / Ready — so the user knows
 * what each segment means without a legend. */
.provisioning-segment::after {
  position: absolute;
  bottom: -18px;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.provisioning-segment[data-phase="requested"]::after { content: "Requested"; }
.provisioning-segment[data-phase="installed"]::after { content: "Installed"; }
.provisioning-segment[data-phase="running"]::after   { content: "Running"; }
.provisioning-segment[data-phase="ready"]::after     { content: "Ready"; }

.provisioning-segment.active::after {
  color: var(--ink);
  font-weight: 600;
}

/* Terminal "Done" badge that appears underneath the checklist once every
 * row has flipped to ready. Sits inside the same .provisioning-screen
 * flex column as the rows, so the default 24px gap applies between the
 * last row and the badge. Uses the same brand-3 green + raised-on-ink
 * affordance as the miniature row checkmarks, scaled up so it reads as
 * the headline element of the finished view. */
.provisioning-done {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  background: var(--brand-3);
  color: var(--on-brand);
  border: 1px solid var(--ink);
  transform: translateY(-4px);
  box-shadow: 0 4px 0 var(--ink);
}

.provisioning-done-check {
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
}

.provisioning-done-text {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

