/* ==========================================================================
   Buildout — the overlay between "Build my platform" and the platform.

   Provisioning takes a second or two and then signs the browser in. Without
   something on screen that is a dead button followed by an unexplained page
   change; with this it is a machine assembling the thing that was just
   described, and the five lines of the checklist are the five things the server
   is actually doing.

   The overlay is appended outside .asm, so it cannot inherit that block's tokens
   and declares its own. Values match assembler.css deliberately — this is the
   same product, one screen later.

   Nothing here is load-bearing for correctness: if this stylesheet or its script
   fails, buildout.js is never constructed and the assembler falls back to
   navigating straight to the platform.
   ========================================================================== */

.bo {
  --bo-ink: #eaf4f4;
  --bo-ink-2: #9fbdc2;
  --bo-ink-3: #6d8f97;
  --bo-line: rgba(158, 214, 219, .22);
  --bo-accent: #3ec6cd;
  --bo-accent-2: #1d969e;
  --bo-ok: #52d1a4;
  --bo-err: #ff9a8f;

  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 24px;
  /* Deep teal-navy, the masthead colour taken all the way down, so the overlay
     reads as the same brand rather than as a generic loading screen. */
  background:
    radial-gradient(120% 90% at 50% 8%, #10404a 0%, rgba(16, 64, 74, 0) 62%),
    linear-gradient(180deg, #071a25 0%, #0a2632 58%, #061620 100%);
  color: var(--bo-ink);
  font-family: Poppins, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 13.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;

  opacity: 0;
  transition: opacity .32s ease;
}

/* `hidden` alone loses to `display: grid`, so it is restated. */
.bo[hidden] { display: none; }

.bo[data-open="true"] { opacity: 1; }

/* The particle field. Behind everything, and never a click target — the stage
   above it holds the only control on the screen. */
.bo__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.bo__stage {
  position: relative;
  width: 100%;
  max-width: 560px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.bo__logo { height: 26px; width: auto; opacity: .95; }

.bo__eyebrow {
  margin-top: 18px;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bo-ink-3);
}

.bo__title {
  margin: 6px 0 0;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -.01em;
  color: var(--bo-ink);
}

.bo__lede {
  margin: 8px 0 0;
  max-width: 40ch;
  font-size: 13px;
  color: var(--bo-ink-2);
}

/* --------------------------------------------------------------- the scene --- */

.bo__scene {
  width: 100%;
  max-width: 420px;
  height: auto;
  margin: 18px 0 4px;
  overflow: visible;
}

.bo__ground { stroke: var(--bo-line); stroke-width: 1.4; }

/* Girders start collapsed at their own top edge and drop into place, which is
   the direction a crane actually delivers them. */
.bo__floor {
  opacity: 0;
  transform: translateY(-14px);
  transition: opacity .34s ease, transform .5s cubic-bezier(.2, 1.5, .4, 1);
}
.bo__floor[data-set="true"] { opacity: 1; transform: none; }

.bo__beam { stroke: var(--bo-accent-2); stroke-width: 3; stroke-linecap: round; }
.bo__post { stroke: #2b6e78; stroke-width: 3; stroke-linecap: round; }

/* Windows only light up once the whole frame is standing. */
.bo__pane { fill: var(--bo-accent); opacity: 0; transition: opacity .5s ease; }
.bo[data-phase="done"] .bo__pane { opacity: .5; }

.bo__crane { stroke: #35818c; stroke-width: 2.2; stroke-linecap: round; fill: none; }
.bo__lattice { stroke: rgba(158, 214, 219, .3); stroke-width: 1.2; }

/* The hook rides up and down while there is work to deliver, and parks when
   there is not. transform-origin sits on the jib so the cable pivots there, and
   transform-box is stated because Safari resolved SVG transform-origin against
   the element's own bounding box for years — the default is only recently
   view-box, and getting it wrong swings the hook from its own middle. */
.bo__hook {
  transform-box: view-box;
  transform-origin: 186px 30px;
  animation: bo-hoist 3.2s ease-in-out infinite;
}
.bo[data-phase="done"] .bo__hook,
.bo[data-phase="failed"] .bo__hook { animation: none; }

@keyframes bo-hoist {
  0%, 100% { transform: translateY(0) rotate(-1.5deg); }
  50%      { transform: translateY(26px) rotate(1.5deg); }
}

/* The gears. Two meshing, so they must turn in opposite directions and at speeds
   inversely proportional to their tooth counts or the teeth visibly slip: 8
   teeth against 6, so 5.2s against 3.9s. */
/* fill-box, not the initial view-box: `center` has to mean the centre of this
   gear, not the centre of the whole scene. And the gear group carries no
   `transform` presentation attribute — its translate lives on a parent <g> —
   because a CSS transform replaces that attribute outright rather than composing
   with it, which would drop every gear onto the origin. */
.bo__gear {
  transform-box: fill-box;
  transform-origin: center;
  animation: bo-spin 5.2s linear infinite;
}
.bo__gear--small { animation: bo-spin-back 3.9s linear infinite; }

.bo__gearbody { fill: none; stroke: var(--bo-accent); stroke-width: 6.5; }
.bo__tooth { fill: var(--bo-accent); }
.bo__hub { fill: #071a25; stroke: var(--bo-accent); stroke-width: 2; }

/* A machine that stops does not stop instantly. */
.bo[data-phase="done"] .bo__gear,
.bo[data-phase="failed"] .bo__gear {
  animation-duration: 2.4s;
  animation-timing-function: cubic-bezier(.1, .8, .2, 1);
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}
.bo[data-phase="failed"] .bo__gearbody,
.bo[data-phase="failed"] .bo__tooth { stroke: var(--bo-err); fill: var(--bo-err); }
.bo[data-phase="failed"] .bo__gearbody { fill: none; }

@keyframes bo-spin      { to { transform: rotate(360deg); } }
@keyframes bo-spin-back { to { transform: rotate(-360deg); } }

/* ------------------------------------------------------------- checklist --- */

.bo__steps {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  width: 100%;
  max-width: 380px;
  text-align: left;
}

.bo__step {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  font-size: 13px;
  color: var(--bo-ink-3);
  border-top: 1px solid var(--bo-line);
  transition: color .3s ease;
}
.bo__step:first-child { border-top: 0; }
.bo__step[data-state="doing"] { color: var(--bo-ink); }
.bo__step[data-state="done"]  { color: var(--bo-ink-2); }

/* The marker is one element in three states: empty ring, pulsing ring, tick. */
.bo__mark {
  flex: 0 0 auto;
  position: relative;
  width: 16px;
  height: 16px;
  border: 1.5px solid var(--bo-line);
  border-radius: 50%;
  transition: border-color .3s ease, background-color .3s ease;
}
.bo__step[data-state="doing"] .bo__mark {
  border-color: var(--bo-accent);
  animation: bo-pulse 1.4s ease-in-out infinite;
}
.bo__step[data-state="done"] .bo__mark {
  border-color: var(--bo-ok);
  background: var(--bo-ok);
}
/* Drawn with two borders rather than a glyph so it needs no icon font and
   inherits nothing from the page's typography. */
.bo__step[data-state="done"] .bo__mark::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1.5px;
  width: 5px;
  height: 8.5px;
  border: solid #06241c;
  border-width: 0 1.8px 1.8px 0;
  transform: rotate(40deg);
}

@keyframes bo-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(62, 198, 205, .35); }
  50%      { box-shadow: 0 0 0 5px rgba(62, 198, 205, 0); }
}

/* ----------------------------------------------------------------- outcome --- */

.bo__note {
  margin: 16px 0 0;
  max-width: 44ch;
  font-size: 12.5px;
  color: var(--bo-ink-3);
}

.bo__fail {
  margin: 16px 0 0;
  padding: 12px 14px;
  max-width: 44ch;
  border: 1px solid rgba(255, 154, 143, .35);
  border-radius: 10px;
  background: rgba(255, 154, 143, .08);
  font-size: 13px;
  color: var(--bo-err);
  text-align: left;
}

.bo__back {
  margin-top: 12px;
  padding: 10px 18px;
  border: 1px solid var(--bo-line);
  border-radius: 999px;
  background: transparent;
  color: var(--bo-ink);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}
.bo__back:hover { border-color: var(--bo-accent); color: var(--bo-accent); }
.bo__back:focus-visible { outline: 2px solid var(--bo-accent); outline-offset: 2px; }

.bo[data-phase="failed"] .bo__steps { opacity: .4; }

/* ------------------------------------------------------------------ small --- */

@media (max-width: 520px) {
  .bo { padding: 20px 18px; align-items: start; padding-top: 8vh; }
  .bo__title { font-size: 19px; }
  .bo__scene { margin-top: 12px; }
}

/* Short and wide — a laptop in landscape with the browser chrome open. The scene
   is the first thing to give up its height; the checklist is information. */
@media (max-height: 620px) {
  .bo__scene { max-width: 300px; }
  .bo__eyebrow { margin-top: 10px; }
}

/* ---------------------------------------------------------- reduced motion --- */

/* The steps still advance and the redirect still happens — that is information,
   not decoration. Only the moving parts stop. buildout.js makes the same check
   and renders the particle field as a single static frame. */
@media (prefers-reduced-motion: reduce) {
  .bo,
  .bo__floor,
  .bo__step,
  .bo__mark,
  .bo__pane { transition: none; }
  .bo__gear,
  .bo__gear--small,
  .bo__hook,
  .bo__step[data-state="doing"] .bo__mark { animation: none; }
  .bo__floor { transform: none; }
}
