/* ═══════════════════════════════════════════════════════════════════════════
   @nobul/ui — boot splash "Aurora" (v2026.20.0)

   One loading screen for every app. Load this file in <head> after tokens.css
   and the app's brand override (it paints with --brand-nav / --brand-accent),
   and put splash.js right after the #splash element (see splash.js header).

   Composition (decided 2026-08-30): near-black ground tinted with the app
   colour, two drifting bubbles of that colour plus a small accent bubble under
   a fine grain; the spinner (the original 42px dual ring, scaled to 104px) and
   the status line sit 15% above centre; the app title (Inter 300) sits
   bottom-left with the Nobul wordmark beneath it. Retry controls hang below the
   status line, out of the flow, so the spinner never moves.
   ═══════════════════════════════════════════════════════════════════════════ */

.nobul-splash {
  /* The colour is theme-independent: --brand-splash is declared once per app (light
     block only) so the screen does not change tone when the deferred theme script
     switches --brand-nav to its dark variant half a second into the boot. */
  --nobul-splash-color: var(--brand-splash, var(--brand-nav, #0F172A));
  --nobul-splash-accent: var(--brand-splash-accent, var(--brand-accent, #dc2626));
  position: fixed; inset: 0; z-index: var(--z-splash, 3000);
  overflow: hidden; color: #fff;
  font-family: var(--font, "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  background: color-mix(in oklch, var(--nobul-splash-color) 32%, #07090d);
  transition: opacity .45s ease;
  -webkit-font-smoothing: antialiased;
}
/* The fade itself is driven by splash.js (Web Animations); this is the fallback. */
.nobul-splash.is-hiding { opacity: 0; pointer-events: none; }

/* ── Light ──────────────────────────────────────────────────────────────── */
.nobul-splash-light { position: absolute; inset: 0; pointer-events: none; }
.nobul-splash-bubble {
  position: absolute; display: block; aspect-ratio: 1; border-radius: 50%;
  filter: blur(70px); mix-blend-mode: screen; will-change: transform;
}
.nobul-splash-bubble.b1 {
  width: max(92vw, 72vh); left: -30vw; bottom: -48vw; opacity: .86;
  background: radial-gradient(circle, color-mix(in oklch, var(--nobul-splash-color) 95%, white), transparent 62%);
  animation: nobul-splash-drift1 26s ease-in-out infinite alternate;
}
.nobul-splash-bubble.b2 {
  width: max(64vw, 50vh); right: -18vw; top: -34vw; opacity: .5;
  background: radial-gradient(circle, color-mix(in oklch, var(--nobul-splash-color) 87%, white), transparent 62%);
  animation: nobul-splash-drift2 32s ease-in-out infinite alternate;
}
.nobul-splash-bubble.b3 {
  width: max(34vw, 28vh); left: 56vw; top: 30vw; opacity: .22;
  background: radial-gradient(circle, var(--nobul-splash-accent), transparent 60%);
  animation: nobul-splash-drift3 21s ease-in-out infinite alternate;
}
@keyframes nobul-splash-drift1 { to { transform: translate(12%, -8%) scale(1.14); } }
@keyframes nobul-splash-drift2 { to { transform: translate(-14%, 10%) scale(1.12); } }
@keyframes nobul-splash-drift3 { to { transform: translate(-18%, -12%) scale(1.25); } }
.nobul-splash-grain {
  position: absolute; inset: 0; display: block; opacity: .085; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── Spinner + status (15% above centre) ────────────────────────────────── */
.nobul-splash-centre {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  transform: translateY(-9vw);
}
.nobul-splash-stack { position: relative; display: flex; flex-direction: column; align-items: center; }
/* The original 42px dual ring (safety/public/index.html), scaled: ring = size/21, inner inset = size/7. */
.nobul-splash-spinner { --nobul-splash-spinner: 104px; width: var(--nobul-splash-spinner); height: var(--nobul-splash-spinner); position: relative; }
.nobul-splash-spinner::before,
.nobul-splash-spinner::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: calc(var(--nobul-splash-spinner) / 21) solid transparent;
}
.nobul-splash-spinner::before {
  border-top-color: rgba(255,255,255,.6); border-right-color: rgba(255,255,255,.15);
  animation: nobul-splash-spin .8s cubic-bezier(.55,.15,.45,.85) infinite;
}
.nobul-splash-spinner::after {
  inset: calc(var(--nobul-splash-spinner) / 7);
  border-bottom-color: rgba(255,255,255,.35); border-left-color: rgba(255,255,255,.08);
  animation: nobul-splash-spin 1.2s cubic-bezier(.55,.15,.45,.85) infinite reverse;
}
@keyframes nobul-splash-spin { to { transform: rotate(360deg); } }

/* Type is held back until the Inter face is available (splash.js adds is-type-ready),
   so the title never renders in a fallback font and then swaps. */
.nobul-splash-status,
.nobul-splash-display { opacity: 0; transition: opacity .2s ease; }
.nobul-splash.is-type-ready .nobul-splash-status,
.nobul-splash.is-type-ready .nobul-splash-display { opacity: 1; }

.nobul-splash-status {
  margin-top: 23px; min-height: 16px; text-align: center; white-space: nowrap;
  font-size: 11px; font-weight: 200; letter-spacing: .02em; color: rgba(255,255,255,.92);
  font-variant-numeric: tabular-nums;
}
.nobul-splash-status .txt { display: inline-block; }
.nobul-splash-status .txt.is-out { animation: nobul-splash-out .17s ease-in forwards; }
.nobul-splash-status .txt.is-in { animation: nobul-splash-in .32s cubic-bezier(.2, .7, .2, 1) both; }
.nobul-splash-status .count { display: inline-block; margin-left: .35em; color: rgba(255,255,255,.7); }
.nobul-splash-status .count:empty { display: none; }
@keyframes nobul-splash-out { to { opacity: 0; transform: translateX(-10px); } }
@keyframes nobul-splash-in { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }

/* Retry controls: absolutely positioned under the status line — never in the flow. */
.nobul-splash-actions {
  position: absolute; top: calc(100% + 14px); left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 10px; white-space: nowrap;
}
.nobul-splash-actions[hidden] { display: none; }
.nobul-splash-detail {
  max-width: 360px; white-space: normal; text-align: center;
  font-size: 12px; font-weight: 300; line-height: 1.45; color: rgba(255,255,255,.62);
}
.nobul-splash-detail:empty { display: none; }
.nobul-splash-buttons { display: flex; gap: 8px; }
.nobul-splash-retry,
.nobul-splash-ghost {
  height: 32px; padding: 0 12px; border-radius: 6px; cursor: pointer;
  display: inline-flex; align-items: center; text-decoration: none;
  font: 600 12px/1 var(--font, "Inter", ui-sans-serif, system-ui, sans-serif);
}
.nobul-splash-retry { border: 1px solid rgba(255,255,255,.3); background: rgba(255,255,255,.1); color: #fff; }
.nobul-splash-ghost { border: 1px solid rgba(255,255,255,.16); background: transparent; color: rgba(255,255,255,.72); }
.nobul-splash-retry:hover { background: rgba(255,255,255,.18); }
.nobul-splash-retry:focus-visible,
.nobul-splash-ghost:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* ── Identity, bottom-left ──────────────────────────────────────────────── */
.nobul-splash-display { position: absolute; left: 4.6vw; bottom: 7vw; line-height: 1; }
.nobul-splash-title {
  display: block; margin: 0;
  font-size: clamp(34px, 6.4vw, 92px); font-weight: 300; letter-spacing: -.008em; color: #fff;
  text-shadow: 0 2px 24px rgba(0,0,0,.25);
}
.nobul-splash-wordmark { display: block; height: clamp(18px, 2.3vw, 34px); width: auto; margin-top: 1.3vw; opacity: .9; }

/* Portrait phones: keep the title readable and the spinner clear of it. */
@media (max-aspect-ratio: 1/1) {
  .nobul-splash-centre { transform: translateY(-10vh); }
  .nobul-splash-display { left: 6vw; bottom: 10vh; }
  .nobul-splash-title { font-size: clamp(30px, 9vw, 56px); }
  .nobul-splash-wordmark { height: clamp(16px, 4vw, 26px); margin-top: 3vw; }
  .nobul-splash-bubble.b1 { left: -60vw; bottom: -40vh; }
  .nobul-splash-bubble.b2 { right: -40vw; top: -20vh; }
}

/* Some app stylesheets zero every animation under prefers-reduced-motion
   (`*, *::before, *::after { animation-duration: .01ms !important }`). The
   spinner is the one thing that must keep moving, so its timings are pinned
   at a higher specificity. */
.nobul-splash .nobul-splash-spinner::before { animation-duration: .8s !important; animation-iteration-count: infinite !important; }
.nobul-splash .nobul-splash-spinner::after { animation-duration: 1.2s !important; animation-iteration-count: infinite !important; }

/* Reduced motion: the light stands still and the spinner slows; the status
   swipe (18px over 320ms, the only sign of progress) and the fade stay. */
@media (prefers-reduced-motion: reduce) {
  .nobul-splash-bubble { animation: none; }
  .nobul-splash .nobul-splash-spinner::before { animation-duration: 3s !important; }
  .nobul-splash .nobul-splash-spinner::after { animation-duration: 4.5s !important; }
}
