/* ============================================================================
   PsyShift — Reset, document, typography primitives, ambient background
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100dvh;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, video, canvas { display: block; max-width: 100%; }
svg { fill: currentColor; }

/* Baseline icon size. Component rules such as `.btn svg` or `.dlist__key svg`
   are more specific and override it; this only catches icons dropped into
   plain text, where an unsized SVG would otherwise fill its container. */
.ico {
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  flex-shrink: 0;
  vertical-align: -0.17em;
}

input, button, select, textarea {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
}
button { cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; padding: 0; }

:where(h1, h2, h3, h4) {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
}

h1 { font-size: var(--t-h1); }
h2 { font-size: var(--t-h2); }
h3 { font-size: var(--t-h3); }

/* Numbers in budgets, prices and counters must never jitter while animating. */
.num, .money, [data-count] { font-variant-numeric: tabular-nums; }

/* ── Focus: visible, on-brand, never suppressed ───────────────────────── */
:focus-visible {
  outline: 2px solid var(--c-accent-bright);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: fixed;
  top: var(--s-3);
  left: 50%;
  translate: -50% -160%;
  z-index: 200;
  padding: var(--s-3) var(--s-5);
  background: var(--c-accent);
  color: #fff;
  border-radius: var(--r-full);
  font-weight: 600;
  transition: translate var(--dur-2) var(--ease-out);
}
.skip-link:focus { translate: -50% 0; }

/* ── Scrollbars ───────────────────────────────────────────────────────── */
* { scrollbar-width: thin; scrollbar-color: var(--c-surface-3) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--c-surface-3);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-full);
}
*::-webkit-scrollbar-thumb:hover { background: #333b5c; background-clip: content-box; }

::selection { background: var(--c-accent); color: #fff; }

/* ── Ambient background ───────────────────────────────────────────────
   Three very large, very low-opacity radial fields drifting slowly, plus a
   fine grain layer. This is what stops the dark UI reading as flat black.
   Fixed + pointer-events:none so it never interferes with the app.        */
.aurora {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background:
    radial-gradient(120% 80% at 50% -10%, #10152a 0%, transparent 60%),
    var(--c-bg);
}

.aurora__field {
  position: absolute;
  width: 62vmax;
  height: 62vmax;
  border-radius: 50%;
  filter: blur(90px);
  opacity: .5;
  will-change: transform;
}
.aurora__field--violet {
  top: -22vmax; left: -14vmax;
  background: radial-gradient(circle, rgba(124,92,255,.34), transparent 66%);
  animation: drift-a 34s var(--ease-in-out) infinite alternate;
}
.aurora__field--teal {
  top: 26vmax; right: -20vmax;
  background: radial-gradient(circle, rgba(46,211,183,.20), transparent 66%);
  animation: drift-b 44s var(--ease-in-out) infinite alternate;
}
.aurora__field--magenta {
  bottom: -26vmax; left: 22vmax;
  background: radial-gradient(circle, rgba(255,77,143,.16), transparent 66%);
  animation: drift-c 52s var(--ease-in-out) infinite alternate;
}

@keyframes drift-a { to { transform: translate3d(9vmax, 7vmax, 0) scale(1.14); } }
@keyframes drift-b { to { transform: translate3d(-11vmax, -6vmax, 0) scale(1.08); } }
@keyframes drift-c { to { transform: translate3d(7vmax, -9vmax, 0) scale(1.12); } }

/* Film grain — kills gradient banding on large dark surfaces. */
.aurora::after {
  content: "";
  position: absolute;
  inset: -50%;
  opacity: .028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── Shared utilities ─────────────────────────────────────────────────── */
.eyebrow {
  font-size: var(--t-2xs);
  font-weight: 600;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--c-text-faint);
}

.lede {
  color: var(--c-text-muted);
  font-size: clamp(0.95rem, 0.9rem + 0.25vw, 1.06rem);
  line-height: var(--lh-body);
  max-width: 60ch;
}

.muted  { color: var(--c-text-muted); }
.faint  { color: var(--c-text-faint); }
.soft   { color: var(--c-text-soft); }
.accent { color: var(--c-accent-bright); }
.teal   { color: var(--c-second); }

.stack     { display: flex; flex-direction: column; gap: var(--s-4); }
.row       { display: flex; align-items: center; gap: var(--s-3); }
.row--wrap { flex-wrap: wrap; }
.spread    { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.grow      { flex: 1 1 auto; min-width: 0; }

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

[hidden] { display: none !important; }

/* ── Entrance animation ───────────────────────────────────────────────
   Applied by the router to direct children of a freshly rendered view.
   Short, small travel, staggered — it should register as "settling", not
   as an animation the user has to wait for.                              */
.reveal {
  animation: reveal var(--dur-4) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes reveal {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* The same thing, asked for in Profile & settings rather than by the OS. */
[data-motion="reduced"] *,
[data-motion="reduced"] *::before,
[data-motion="reduced"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
  scroll-behavior: auto !important;
}

/* ═════════════════════════════════════════════════════════════════════════
   PRODUCTION ADDITIONS (server-rendered pages)
   The reference staggered each page's entrance from JavaScript, writing
   `--i` inline on every top-level block. Rendered on the server, the page
   gets the same cascade from its position instead: no inline style.
   ═══════════════════════════════════════════════════════════════════════ */
.view--reveal > * {
  animation: reveal var(--dur-4) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
.view--reveal > :nth-child(2)  { --i: 1; }
.view--reveal > :nth-child(3)  { --i: 2; }
.view--reveal > :nth-child(4)  { --i: 3; }
.view--reveal > :nth-child(5)  { --i: 4; }
.view--reveal > :nth-child(6)  { --i: 5; }
.view--reveal > :nth-child(7)  { --i: 6; }
.view--reveal > :nth-child(8)  { --i: 7; }
.view--reveal > :nth-child(n+9) { --i: 8; }
