/* =============================================================================
   BLACKOUT.CSS — the Vane blackout (ASTOR §14.6).

   For the handful of actions that take the page hostage: a Monday board pull is
   a synchronous walk of several hundred rows under the tenancy's own token, and
   for the thirty-to-sixty seconds it runs the page looked untouched. People
   clicked again, clicked away, or pressed back — and a pull that is interrupted
   leaves the whole TEAM on the older snapshot without anyone being told.

   So the screen goes dark and Vane's orb comes up on it. It is not decoration:
   the scrim is the control. Nothing on the page beneath it is clickable while it
   is raised, Escape does not dismiss it, and the copy says in words what will
   happen if you leave.

   Palette: this is the one surface in the product that is ink-on-ink rather than
   ink-on-bone, and the only place text is centred outside §7's Editorial Void —
   both because it is a takeover, not a page: there is no layout to sit inside and
   nothing else on screen to align to. It is the Vane console's dock composition
   (static/planning/vane.css), lit from the other side.

   Motion is ql-stitch (the halo, the mark, the indeterminate meter), astor-spin
   (the working arc) and astor-rise (the entrance) — the sanctioned three, §8.3.
   No new keyframes.

   Wiring lives in static/planning/busy.js; the markup in planning/templates/
   _blackout.html, rendered once per tools page from base_tools.html.
   ============================================================================= */
@layer components {

.blk {
  position: fixed;
  inset: 0;
  /* Above everything: the toast tray and the nav progress bar are 9999, the Vane
     widget 960. A blackout that something can paint over is not a blackout. */
  z-index: 10000;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  /* DARKENED, not sealed. Cain, 2026-09-04, on the first solid-black build: *"you
     took blackout very literally which is cool, but i think i meant a little more
     like darken the screen rather than a full blackout."*

     The objection to a translucent scrim was real — at 96% opacity alone the page's
     own headings and buttons still read straight through, measured at 1440px, and a
     screen you can read the page through does not say STOP. The blur is what answers
     it: at 92% ink with an 8px backdrop blur the page survives as shape and colour and
     dies as text, so it is plainly still there and plainly not available. Opacity
     alone could not do both — measured at 1440px over a real table, 86%/6px still
     left the page heading legible.

     Ink rather than black, because the disc is ink too — the sphere's mass comes from
     the blur and the hairline edge, not from a surface differential, and ink-on-ink is
     the palette (§2). */
  background: color-mix(in srgb, var(--c-ink) 92%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--c-white);
  /* The scrim IS the interruption guard — it must eat every click, not pass one
     through to the button underneath. */
  pointer-events: auto;
  overscroll-behavior: contain;
  animation: astor-rise 0.3s ease both;
}
/* display:grid would otherwise beat the attribute, and the overlay would sit on
   every page permanently. */
.blk[hidden] { display: none; }

.blk__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 460px;
}

/* ── The orb ──────────────────────────────────────────────────────────────────
   The console orb inverted onto black. The disc stays ink (a sphere darker than
   the dark it sits in reads as mass, not as a hole) and earns its edge from a
   white hairline rather than from a surface differential that black cannot give. */
.blk__orb {
  position: relative;
  width: 168px;
  height: 168px;
  display: grid;
  place-items: center;
  margin-bottom: var(--space-8);
  /* Its own stacking context, so the z-index the motion loop flips on each electron
     (behind the disc on the far half of its orbit, in front on the near half) is
     resolved here and cannot reach out into the page. */
  isolation: isolate;
  will-change: transform;      /* the rAF float lives on this node */
}
.blk__halo,
.blk__spin,
.blk__gimbal,
.blk__disc {
  position: absolute;
  border-radius: 50%;            /* sanctioned: the orb is the one circle (§3) */
}
/* Breathing halo — the pulse you can feel, on the brand's 2.8s rhythm. */
.blk__halo {
  inset: 0;
  border: 1px solid var(--c-white);
  opacity: 0.22;
  animation: ql-stitch 2.8s ease-in-out infinite;
}
/* Equatorial shell ring, squashed — the console precesses this from JS; here it
   is still, because a blackout should feel held rather than busy. */
.blk__gimbal {
  inset: -10px;
  border: 1px solid var(--c-white);
  opacity: 0.08;
  transform: rotateX(72deg);
}
/* The working arc — the sanctioned astor-spin spinner. It is the REDUCED-MOTION
   fallback and nothing else: when the rAF loop runs, three electrons already carry the
   motion, and a fourth thing going round at a fourth speed is noise. When the loop is
   refused, this is the only moving indicator left and §14 requires one — so it turns on
   exactly there and is off here. */
.blk__spin {
  inset: -8px;
  border: 2px solid transparent;
  border-top-color: var(--c-white);
  opacity: 0;
  animation: astor-spin 0.9s linear infinite;
}
.blk__disc {
  width: 104px;
  height: 104px;
  z-index: 2;                  /* shells pass behind (1), near electrons in front (3) */
  background: var(--c-ink);
  border: 1px solid color-mix(in srgb, var(--c-white) 32%, transparent);
  display: grid;
  place-items: center;
}
/* The eye — a quiet white iris ring around the mark. */
.blk__iris {
  position: absolute;
  inset: 20px;                   /* a 64px ring centred in the 104px disc */
  border-radius: 50%;            /* sanctioned: part of the one orb composition */
  border: 1px solid var(--c-white);
  opacity: 0.20;
}
.blk__mark {
  font-size: 46px;
  line-height: 0;
  color: var(--c-white);
  animation: ql-stitch 2.8s ease-in-out infinite;
}
.blk__mark svg { display: block; }

/* ── Electron shells and their electrons ──────────────────────────────────────
   Three tilted orbits round the sphere. Both the ring tilts and the electrons'
   positions are written by the rAF loop in busy.js — nothing here animates, because
   §8.3 sanctions three keyframe animations system-wide and the CSS audit fails a
   fourth. What CSS owns is the resting geometry: a circle the loop squashes into an
   ellipse, and a square the loop moves round it. */
.blk__shell {
  position: absolute;
  inset: -14px;                /* wider than the halo — these orbit the whole orb */
  border-radius: 50%;          /* sanctioned: part of the one orb composition (§3) */
  border: 1px solid var(--c-white);
  opacity: 0.18;
  will-change: transform;
  pointer-events: none;
}
/* No resting tilts here on purpose. They would be a second copy of the ORBITS table in
   busy.js, and the day somebody changes a tilt there the two would silently disagree —
   so the loop's first frame is the only place the geometry lives, and it runs even under
   reduced motion (one frame, then it stands still). Before that frame these are three
   concentric circles, which is a sixteenth of a second and still reads as deliberate.

   The electrons start INVISIBLE for the same reason, and the loop is what lights them:
   at rest they are stacked dead centre, so if the script never runs at all they would
   otherwise render as three stray white squares beside the Astor mark. */

/* THE ELECTRON IS A SQUARE. The Astor mark is eight squares in a ring (§13); these
   read as that ring's outermost members, broken loose and still going round. A circle
   here would be a bead on a wire. */
.blk__electron {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;       /* centred on the orb before the loop translates it */
  background: var(--c-white);
  opacity: 0;                  /* the loop lights them — see above */
  z-index: 3;
  will-change: transform, opacity;
  pointer-events: none;
}

/* ── Copy ─────────────────────────────────────────────────────────────────── */
.blk__eyebrow {
  margin: 0 0 var(--space-3);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-white) 52%, transparent);
}
.blk__title {
  margin: 0 0 var(--space-3);
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.35;
  color: var(--c-white);
}
.blk__body {
  margin: 0;
  font-size: 13px;
  font-weight: 300;
  line-height: 1.7;
  color: color-mix(in srgb, var(--c-white) 62%, transparent);
}

/* ── Indeterminate meter (§8.3: ql-stitch is the sanctioned indeterminate fill) ── */
.blk__meter {
  width: 220px;
  height: 2px;
  margin-top: var(--space-7);
  background: color-mix(in srgb, var(--c-white) 12%, transparent);
}
.blk__meter-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--c-white);
  opacity: 0.55;
  animation: ql-stitch 2.8s ease-in-out infinite;
}

/* ── Elapsed line — the honest number. Not a fake percentage. ─────────────── */
.blk__timer {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--c-white) 46%, transparent);
}
/* The Live Stitch dot: 5px square, ql-stitch, never a circle (§8.3).
   WHITE here, and this is the one place in the product it is not oxblood. Oxblood
   on black measures a 1.6:1 contrast ratio — not a quiet dot but an invisible one,
   and an indicator nobody can see is not an indicator. §13 keeps
   oxblood as the only SIGNAL colour — this is a liveness pulse on an ink ground,
   the same job §14.5 already gives a solid white dot. The rule's purpose survives;
   its default value does not ([[feedback_a_written_decision_is_not_a_veto_over_legibility]]). */
.blk__dot {
  width: 5px;
  height: 5px;
  flex-shrink: 0;
  background: var(--c-white);
  opacity: 0.7;
  animation: ql-stitch 2.8s ease-in-out infinite;
}

/* ── The release (§8.2 ghost button, inverted onto the scrim) ─────────────── */
.blk__release {
  margin-top: var(--space-7);
  padding: 6px 32px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--c-white);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--c-white) 40%, transparent);
  border-radius: 0;
  cursor: pointer;
  animation: astor-rise 0.3s ease both;
}
.blk__release:hover { opacity: 0.7; }
.blk__release:focus-visible { outline: 2px solid var(--c-white); outline-offset: 3px; }
.blk__release[hidden] { display: none; }

.blk__release-note {
  margin: var(--space-3) 0 0;
  font-size: 11px;
  line-height: 1.6;
  color: color-mix(in srgb, var(--c-white) 40%, transparent);
}
.blk__release-note[hidden] { display: none; }

/* Scroll lock — set on <body> while the blackout is raised, so a trackpad flick
   cannot reveal the page behind it. */
.blk-lock { overflow: hidden; }

@media (max-width: 640px) {
  .blk__orb { width: 124px; height: 124px; margin-bottom: var(--space-6); }
  .blk__shell { inset: -10px; }
  .blk__electron { width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; }
  .blk__disc { width: 80px; height: 80px; }
  .blk__iris { inset: 15px; }
  .blk__mark { font-size: 34px; }
  .blk__title { font-size: 17px; }
  .blk__meter { width: 180px; }
}

/* ── The same orb, FLOATING on a light page ─────────────────────────────────────
   The covenant alert on /lender/funding/ is a second instance of this orb (Cain,
   2026-09-16: "as good as if not better than loading screen vane ... loading screen vane
   has the orbiting electrons"). Same markup (`_vane_orb.html`), same loop (`busy.js`
   `orbit()`), and its geometry stays HERE beside the phone variant above rather than as a
   redrawn copy somewhere else.
   · `--float` is the size: an 88px box, which busy.js reads to scale the orbits and the
     wander (88 / 168), with the disc at Vane's launcher size.
   · `--on-light`: this composition is white hairlines on an ink scrim, and white on a
     white page is nothing. So the rings, the working arc and the electrons take ink; the
     disc was ink already, and the iris and the mark inside it stay white. */
.blk__orb--float { width: 88px; height: 88px; margin: 0; }
.blk__orb--float .blk__shell    { inset: -7px; }
.blk__orb--float .blk__gimbal   { inset: -5px; }
.blk__orb--float .blk__spin     { inset: -4px; }
.blk__orb--float .blk__disc     { width: 56px; height: 56px; }
.blk__orb--float .blk__iris     { inset: 11px; }
.blk__orb--float .blk__mark     { font-size: 24px; }
.blk__orb--float .blk__electron { width: 4px; height: 4px; margin: -2px 0 0 -2px; }
.blk__orb--on-light .blk__halo,
.blk__orb--on-light .blk__gimbal,
.blk__orb--on-light .blk__shell { border-color: var(--c-ink); }
.blk__orb--on-light .blk__halo  { opacity: 0.30; }
.blk__orb--on-light .blk__shell { opacity: 0.26; }
.blk__orb--on-light .blk__spin  { border-top-color: var(--c-ink); }
.blk__orb--on-light .blk__electron { background: var(--c-ink); }

/* Honour the OS setting: the blackout still appears and still blocks, it just
   stops moving. A user who asked for no motion still needs the interruption
   guard — the arc is a spinner, not decoration, so it is the LAST thing to go. */
@media (prefers-reduced-motion: reduce) {
  .blk,
  .blk__halo,
  .blk__mark,
  .blk__meter-fill,
  .blk__dot { animation: none; }
  /* The electrons stop: busy.js runs a single frame under this setting, so they are
     placed on their orbits and then stand still. That would leave the screen entirely
     motionless, so the arc comes back, slowed — a "working" screen with nothing moving
     on it is indistinguishable from a hung one. */
  .blk__spin { opacity: 0.5; animation-duration: 2.4s; }
  /* THE FLOATING ALERT IS NOT A WORKING SCREEN. Nothing is in progress when a covenant
     breaks, and `.blk__spin` means "working" — an alert that spins reads as loading
     (reviewer's ruling, 2026-09-16). So the floating orb takes the loop's single placement
     frame, electrons still on their orbits, and no arc. The blackout's rule above is
     untouched. */
  .blk__orb--float .blk__spin { opacity: 0; animation: none; }
}

} /* end @layer components */
