/* ==========================================================================
   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: 480px;
  height: auto;
  margin: 18px 0 4px;
  overflow: visible;
}

/* --------------------------------------------------------------- the mark --- */

/* Thirty pieces, each scattered until its group is set. The offset, the rotation
   and the delay come in as custom properties from buildout.js — a fixed scatter
   per piece would be ninety numbers in a stylesheet and the same assembly every
   time, and the interesting part of a puzzle is that it does not land the same
   way twice.

   fill-box, not the initial view-box: `center` has to mean the centre of this
   piece. Without it every piece rotates and scales about the middle of the whole
   mark, and the effect is the logo breathing rather than thirty tiles arriving.

   The overshoot in the timing function is what makes a piece read as snapping
   into a slot instead of sliding to a stop. */
.bo__piece {
  fill: url(#komunidad-mark);
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  transform:
    translate(var(--dx, 0px), var(--dy, -18px))
    rotate(var(--rot, -12deg))
    scale(.55);
  transition:
    opacity .30s ease var(--d, 0ms),
    transform .52s cubic-bezier(.18, 1.42, .36, 1) var(--d, 0ms);
}
.bo__group[data-set="true"] .bo__piece {
  opacity: 1;
  transform: none;
}

/* The mark's own gradient runs #1F3556 to #2BA093 — left to right, dark end
   first — and it was drawn for a white page. On this one it disappears: the
   leftmost column of tiles is near-black navy on near-black navy. So the gradient
   is kept exactly as the file defines it and lifted here, in the one place it is
   shown against a dark background. Editing the stops instead would have made
   this the logo's second set of colours. */
.bo__glyph {
  filter:
    brightness(1.74) saturate(1.1)
    /* A standing glow, not only at the finish: with five gears, a chip and a
       circuit on screen the mark has to hold the centre while it is still
       incomplete, and six tiles of flat colour would not.
       6px, not 10: the wider one spread past the tiles and read as haze rather
       than as light coming off them. */
    drop-shadow(0 0 6px rgba(52, 176, 178, .34));
}

/* The finished mark, acknowledged once. A short lift and a glow rather than a
   permanent effect: it marks the moment the thing is whole, and then the screen
   is about leaving. */
/* No transform-origin here, and that is the whole point: an SVG `transform`
   attribute maps to the CSS transform property, so `transform-origin: center`
   re-anchors the translate-and-scale in the markup as well. It scaled about the
   middle of the view box instead of about the origin the attribute assumed, and
   put the mark 83px right and 56px down of centre — the right size, the wrong
   place. Same trap as the gears below, opposite direction: they need fill-box
   because they DO rotate about themselves; this one rotates about nothing, so it
   must be left alone. bo-seat animates `filter` only, which needs no origin. */
.bo[data-phase="done"] .bo__glyph { animation: bo-seat .9s cubic-bezier(.2, .9, .2, 1) 1; }

/* The finish line, said once. The title takes the accent for the second and a
   half it is on screen — the only place in this overlay where copy changes
   colour, which is what makes it register. */
.bo[data-phase="done"] .bo__title { color: var(--bo-accent); }
.bo[data-phase="done"] .bo__eyebrow { color: var(--bo-accent-2); }
.bo[data-phase="done"] .bo__piece { filter: drop-shadow(0 0 3px rgba(120, 220, 212, .40)); }

@keyframes bo-seat {
  0%   { filter: none; }
  35%  { filter: brightness(1.35) drop-shadow(0 0 12px rgba(110, 214, 208, .5)); }
  100% { filter: none; }
}

/* A failure leaves the pieces where they are and drains the colour out of them:
   nothing was created, and a half-built mark still holding its brand gradient
   reads as success interrupted. */
.bo[data-phase="failed"] .bo__piece {
  fill: var(--bo-err);
  opacity: .45;
  filter: none;
}

/* ----------------------------------------------------------------- gears --- */

/* Behind the mark, and quiet. They are the reason the mark is assembling, not
   the thing being watched — at full contrast two spinning wheels beat a logo
   standing still every time. */
.bo__gears { opacity: .3; }

/* Three meshing, so they turn in alternating directions at speeds inversely
   proportional to their tooth counts or the teeth visibly slip: 8 against 6
   against 6, so 5.2s against 3.9s against 3.1s. */
/* 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__gear--tiny { animation: bo-spin 3.1s linear infinite; }
/* The right-hand pair drives the other way, so the two clusters do not read as
   one belt. Slower, because it is smaller and further from the eye. */
.bo__gear--slow { animation-duration: 7.4s; animation-direction: reverse; }

.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); } }

/* ------------------------------------------------------- chip and circuit --- */

/* A processor. Outline package, filled die, hairline pins — the die is the only
   filled thing, so it is what the eye finds, and it is the only thing that
   moves. */
.bo__chipbody {
  fill: none;
  stroke: var(--bo-accent);
  stroke-width: 2.4;
}
.bo__pin { stroke: var(--bo-accent); stroke-width: 1.8; stroke-linecap: round; }
.bo__chipdie {
  fill: var(--bo-accent);
  transform-box: fill-box;
  transform-origin: center;
  animation: bo-compute 2.6s ease-in-out infinite;
}
/* Not opacity alone: a square that only dims reads as a flicker. Shrinking it a
   little at the same time reads as a load rising and falling. */
@keyframes bo-compute {
  0%, 100% { opacity: .45; transform: scale(.86); }
  50%      { opacity: 1;   transform: scale(1); }
}

/* Circuit traces. The dash pattern moves, not the path — one pulse travelling
   the length of each trace, which is a signal rather than a diagram of one. The
   two traces run at different speeds and one is offset, so they do not pulse in
   lockstep. */
/* The wire itself: always there, quiet. */
.bo__wire {
  fill: none;
  stroke: var(--bo-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  opacity: .4;
}
/* The pulse on top of it: one bright dash, moving. */
.bo__trace {
  fill: none;
  stroke: var(--bo-ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 13 97;
  animation: bo-signal 2.9s linear infinite;
}
.bo__trace--b { animation-duration: 3.7s; animation-delay: -1.2s; }
.bo__node { fill: var(--bo-accent-2); }

/* 110 is the dasharray total, so one cycle moves the pulse exactly one pattern
   length and the loop is seamless. */
@keyframes bo-signal { to { stroke-dashoffset: -110; } }

/* Both stop with the gears — the whole machine is one machine. */
.bo[data-phase="done"] .bo__chipdie,
.bo[data-phase="failed"] .bo__chipdie,
.bo[data-phase="done"] .bo__trace,
.bo[data-phase="failed"] .bo__trace { animation: none; }
.bo[data-phase="failed"] .bo__chipbody,
.bo[data-phase="failed"] .bo__pin,
.bo[data-phase="failed"] .bo__wire,
.bo[data-phase="failed"] .bo__trace { stroke: var(--bo-err); }
.bo[data-phase="failed"] .bo__chipdie,
.bo[data-phase="failed"] .bo__node { fill: var(--bo-err); }

/* ----------------------------------------------------------------- meter --- */

.bo__meter {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 480px;
  margin: 2px 0 14px;
}

/* Tabular figures and a fixed minimum width, or the row jitters sideways every
   time the number gains a digit. */
.bo__pct {
  flex: 0 0 auto;
  min-width: 3.4ch;
  text-align: right;
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--bo-ink);
  font-variant-numeric: tabular-nums;
}
.bo__pct i { font-style: normal; font-size: .55em; font-weight: 500; opacity: .6; margin-left: 1px; }

.bo__bar {
  flex: 1 1 auto;
  height: 4px;
  border-radius: 3px;
  background: rgba(158, 214, 219, .16);
  overflow: hidden;
}
/* Width is set by script every frame, so it is not transitioned — easing an
   already-eased value is what makes a bar feel like it is lagging the number
   beside it. */
.bo__bar i {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--bo-accent-2), var(--bo-accent));
}
.bo[data-phase="failed"] .bo__bar i { background: var(--bo-err); }
.bo[data-phase="failed"] .bo__pct { color: var(--bo-err); }

/* ------------------------------------------------------------- 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__step { transition: none; }
  .bo__gear,
  .bo__gear--small,
  .bo__gear--tiny,
  .bo__gear--slow,
  .bo__chipdie,
  .bo__trace,
  .bo__glyph,
  .bo__step[data-state="doing"] .bo__mark { animation: none; }

  /* The pieces still have to arrive — that is the progress, not decoration — so
     they fade in where they belong rather than flying to it. */
  .bo__piece {
    transform: none;
    transition: opacity .2s linear var(--d, 0ms);
  }
  .bo[data-phase="done"] .bo__piece { filter: none; }
}
