/* backend/public/blackwall.css — the Blackwall portal's own layer (Arise)
 * Implements US-6.7 (FR-004 rev., NFR-003 rev.) — from the approved specimen,
 * claude.ai/artifact/KRwghthzotCmKkznuinVYs (round 1, 2026-10-13).
 *
 * NOTE: loads LAST — after nt/css/variables.css, components.css and fonts.css,
 *       which `bun run sync:design` writes from @jpberlin-dev/neoturne-design.
 *       Every recipe the page draws (shell, page furniture, field, input, button,
 *       notice, SUMMON) is the package's; this file holds only what a consumer
 *       owns — the aura, the ground, the layout, the summon's size and the
 *       arise choreography. A missing recipe is fixed in nt-design, never here.
 * NOTE: local classes are `bw-*`; `.nt-*` belongs to the package. Never set
 *       `background` or `border` on an `.nt-*` class — the seam is painted with
 *       both. A `bw-*` class on an `.nt-*` element may set layout and the
 *       recipe's own custom properties, nothing else.
 */

/* ─── THE TENANCY — Blackwall's aura is red (canon §4.2, §4.5) ──────────────
 * Chosen by Jan in the look round. The page eyebrow, heading and rule read it;
 * summon, shell, input, button and focus stay cyan (they are product).
 * ────────────────────────────────────────────────────────────────────────── */
:root {
  --aura-rgb: var(--aura-red-rgb);
  --aura-cal: var(--aura-red-cal);
}

/* Registered so the arise's bottom-up trace can interpolate (§8.1 Draw).
 * Without @property support it jumps at 50% — the page still ends visible. */
@property --bw-draw {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 115%;
}

/* ─── GROUND — the void (canon §3), vignette only (§6.4, no wash) ────────── */
body {
  margin: 0;
  background: var(--void);
}

.bw-screen {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  min-height: 100svh;
  padding: var(--spacing-xl) var(--spacing-base);
  overflow: hidden;
  background: var(--void);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-body);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

.bw-screen::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(135% 105% at 50% 42%, transparent 52%, rgba(0, 0, 0, 0.6) 100%);
}

/* ─── LAYOUT — summon over one shell, centred ───────────────────────────── */
.bw-column {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-lg);
  width: min(24rem, 100%);
}

/* The summon's size — the recipe leaves it to the consumer (summon.css). */
.bw-summon {
  align-self: center;
  width: 88px;
  height: 88px;
}

.bw-shell {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-lg);
}

.bw-intro .nt-page__head {
  margin-bottom: var(--spacing-sm);
}

.bw-tagline {
  margin: 0;
  color: var(--text-secondary);
}

.bw-form {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-base);
}

.bw-tagline[hidden],
.bw-form[hidden],
.bw-success[hidden],
.bw-notice[hidden] {
  display: none;
}

/* The field message — variant B (chosen by Jan): the words stay in readable
 * neutral ink; the input's red seam says WHICH field, the summon's
 * instability says THAT it failed ("error is behaviour, not hue", §4.4). */
.bw-field-msg {
  margin: 0;
  min-height: 1.4em;
  font-size: var(--font-size-small);
  color: var(--text-secondary);
}

/* The primary action spans the form; the package has no block modifier. */
.bw-submit {
  width: 100%;
}

/* GRANTING — the 6.6 pressed state, held. NOT `disabled`: the recipe's
 * :disabled is DORMANT, and a button that is working is not offline. The
 * in-flight guard lives in the script (aria-disabled + inFlight). */
.bw-submit.is-working {
  transform: translateY(1px);
  cursor: progress;
}

/* ─── SUCCESS — "Welcome beyond the Blackwall." ─────────────────────────── */
.bw-success {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.bw-success__line {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.625rem;
  font-weight: var(--font-weight-semibold);
  line-height: 1.15;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  text-wrap: balance;
}

.bw-success__note {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ENSEMBLE BEAT (§9) — while the summon is awake, the shell drops toward
 * dormant. It is furniture; nothing else may compete with the one figure. */
.bw-screen.is-granted .bw-shell {
  --seam-run: 0.16;
  --seam-peak: rgba(var(--seam-rgb), 0.3);
  box-shadow: none;
}

/* ─── ARISE — canon §8.1, 800ms, on one form ────────────────────────────────
 * Draw (0–40%): the shell traces in bottom-up. Ignite (40–72%): the summon
 * blooms past target (1.07, halo 1.7×). Settle (72–100%). Type +90ms.
 * NEVER blocks interaction: nothing here is `pointer-events: none` or
 * `visibility: hidden`, so the field is focusable and typeable from first
 * paint. The mask exists only while `.bw-arise` is set — left on, it would
 * clip the shell's bloom; the script removes the class when the entry ends.
 * NOTE: the halo keyframes read the recipe's own --summon-bloom / -bloom-a, so
 *       the overshoot is always relative to whatever presence the summon holds.
 * ────────────────────────────────────────────────────────────────────────── */
.bw-arise .bw-shell {
  -webkit-mask-image: linear-gradient(to top, #000 calc(var(--bw-draw) - 15%), transparent var(--bw-draw));
  mask-image: linear-gradient(to top, #000 calc(var(--bw-draw) - 15%), transparent var(--bw-draw));
  animation: bw-draw 320ms cubic-bezier(0.4, 0, 0.2, 1) both;
}

.bw-arise .bw-shell > * {
  animation: bw-type 400ms var(--ease-out) 90ms both;
}

.bw-arise .bw-summon {
  animation: bw-ignite 800ms linear both;
}

.bw-arise .bw-summon .nt-summon__halo {
  animation: bw-ignite-halo 800ms linear both;
}

@keyframes bw-draw {
  from { --bw-draw: 0%; }
  to   { --bw-draw: 115%; }
}

@keyframes bw-type {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes bw-ignite {
  0%   { opacity: 0;   transform: scale(0.96); }
  40%  { opacity: 0.7; transform: scale(1);    animation-timing-function: cubic-bezier(0.22, 1.35, 0.36, 1); }
  72%  { opacity: 1;   transform: scale(1.07); animation-timing-function: ease-out; }
  100% { opacity: 1;   transform: scale(1); }
}

@keyframes bw-ignite-halo {
  0%   { opacity: 0; }
  40%  { opacity: var(--summon-bloom-a); filter: blur(var(--summon-bloom)); animation-timing-function: cubic-bezier(0.22, 1.35, 0.36, 1); }
  72%  { opacity: calc(var(--summon-bloom-a) * 1.7); filter: blur(calc(var(--summon-bloom) * 1.7)); animation-timing-function: ease-out; }
  100% { opacity: var(--summon-bloom-a); filter: blur(var(--summon-bloom)); }
}

/* DISMISS (§8.1) — fast, centre-outward: the form leaves in ~180ms. */
.bw-form.is-leaving {
  animation: bw-dismiss 180ms ease-in both;
}

@keyframes bw-dismiss {
  to { opacity: 0; transform: scale(0.98); }
}

.bw-success.is-arriving {
  animation: bw-type 400ms var(--ease-out) 90ms both;
}

/* REDUCED MOTION — a static entry. The summon's own fault already holds as a
 * still seam break (summon.css); only this layer's motion is switched off. */
@media (prefers-reduced-motion: reduce) {
  .bw-arise .bw-shell,
  .bw-arise .bw-shell > *,
  .bw-arise .bw-summon,
  .bw-arise .bw-summon .nt-summon__halo,
  .bw-form.is-leaving,
  .bw-success.is-arriving {
    animation: none;
  }

  .bw-arise .bw-shell {
    -webkit-mask-image: none;
    mask-image: none;
  }
}
