/* Subsprint 8.6 — Visuell grund: gradient backdrop + glass surfaces +
   motion-grund.

   Why this lives in its own file (outside the Tailwind pipeline):
     1. It's plain CSS — no Tailwind utilities, no @apply. Adding it
        through input.css would force a full rebuild for every tweak.
     2. It's intentionally loaded AFTER static/dist/tailwind.css so
        the .surface-card glass override and the motion rules win
        without specificity gymnastics.
     3. Sprint 8.5 promised a separate motion.css; keeping the file
        layout matches the plan literally so future readers don't
        have to translate.

   Reduced-motion contract: every @keyframes or transition that *moves
   pixels* sits inside @media (prefers-reduced-motion: no-preference).
   Static colour transitions (hover background, focus-ring) are still
   allowed — those don't trigger vestibular issues and they carry
   feedback that AT users rely on. */


/* -------------------------------------------------------------------------
   Background — fixed gradient + optional grain.

   Pinned via ::before on <body> with position: fixed so the colours
   stay still while content scrolls. (background-attachment: fixed on
   <body> is buggy with backdrop-filter on Safari/iOS, hence the
   pseudo-element.) z-index: -1 keeps it behind every flow element
   without needing an isolation hack on the page.

   The grain overlay (~4% noise SVG, inline data-URL) defeats banding
   on the low-contrast 3-stop gradient. Roughly 1.2KB and cached as
   part of the stylesheet.
   ----------------------------------------------------------------------- */
html, body { min-height: 100%; }

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    linear-gradient(
      174deg,
      var(--bg-grad-1, #d6dccd) 0%,
      var(--bg-grad-2, #e6e2d6) 52%,
      var(--bg-grad-3, #d8cdba) 100%
    );
  pointer-events: none;
}

/* Grain overlay — sits on top of the gradient, still under content.
   Opacity 0.04 keeps the texture sub-perceptual; you only notice if
   it goes missing. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  opacity: 0.04;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 160px 160px;
}

/* The app-shell columns must let the gradient show through — both the
   page surface and the inner main both used to paint --page solid.
   Make them transparent and let the body backdrop carry the colour. */
.app-shell__main { background: transparent; }


/* -------------------------------------------------------------------------
   Glass surfaces — Apple iOS 2026-vibe.

   .surface-card from input.css already has the radius and shadow
   tier; we override only background, border tone, and add backdrop-
   filter. Selecting on the class (rather than redefining inside
   @layer components) means this rule paints the same way regardless
   of where Tailwind ends up ordering its layer.
   ----------------------------------------------------------------------- */
.surface-card {
  background: var(--glass-bg);
  backdrop-filter: blur(18px) saturate(1.05);
  -webkit-backdrop-filter: blur(18px) saturate(1.05);
  border-color: var(--glass-border);
}

/* Warn-accent (left rule). Used by the manual-invite bypass card.
   We can't put `style="border-left-color: var(--warn)"` on the element
   directly because CSP style-src is `'self' + nonce` and nonces don't
   apply to inline style attributes. Class form keeps the design token
   without breaching the policy. */
.surface-card--warn {
  border-left-color: var(--warn);
}

/* Active-tier — was already a .is-active class in input.css carrying
   shadow-active. Layer the accent-glow inner-shadow on top so the
   element reads as "currently focused/selected" beyond just shadow
   depth. */
.surface-card.is-active,
.is-active.surface-card {
  box-shadow:
    var(--shadow-active),
    inset 0 0 0 1px var(--accent-glow);
}

/* Browsers without backdrop-filter (older Firefox-on-Linux) get a
   slightly more opaque fallback so cards still read as cards rather
   than ghost rectangles. */
@supports not (backdrop-filter: blur(1px)) {
  .surface-card {
    background: color-mix(in srgb, var(--surface) 92%, transparent);
  }
}


/* -------------------------------------------------------------------------
   Motion — wrapped so reduced-motion users get a still UI.
   ----------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {

  /* Page-load entrance. <main> fades in + slides up on first paint;
     motion.js stamps .motion-ready on <html> after DOMContentLoaded so
     the rule only fires once per navigation (HTMX swaps don't re-run
     it; that's the htmx-swap-flash's job below). */
  .app-shell__main {
    opacity: 0;
    transform: translateY(6px);
  }
  html.motion-ready .app-shell__main {
    opacity: 1;
    transform: translateY(0);
    transition:
      opacity 220ms var(--motion-ease),
      transform 220ms var(--motion-ease);
  }

  /* Hover-lift on interactive cards. .surface-card-lift in input.css
     bumps shadow-only; 8.6 adds the y-translate so the card *feels*
     like it floats. 180ms ease-out matches the plan. */
  .surface-card--interactive:hover,
  .surface-card-lift:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lift);
    transition:
      transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .surface-card--interactive,
  .surface-card-lift {
    transition:
      transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
      box-shadow 180ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* Current-stage-dot breath. .stage-bar__step--current is the only
     stage-bar dot we breathe — passed/future are deliberately still
     so the user's eye lands on "where you are now". */
  @keyframes metod-stage-breathe {
    0%, 100% {
      transform: scale(1);
      box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
    }
    50% {
      transform: scale(1.06);
      box-shadow: 0 0 0 7px var(--accent-glow);
    }
  }
  .stage-bar__step--current .stage-bar__dot {
    animation: metod-stage-breathe 2s var(--motion-ease) infinite;
  }

  /* HTMX-swap shrink-flash. motion.js adds .metod-swap-flash to the
     swap target on htmx:afterSwap and removes it 240ms later. The
     animation paints a brief inset accent-glow that signals
     "something just landed here" without moving any pixels — quiet
     enough to live alongside the more emphatic celebrations 8.7
     will add. */
  @keyframes metod-swap-flash {
    0%   {
      box-shadow: inset 0 0 0 2px var(--accent-glow);
      transform: scale(0.998);
    }
    60%  {
      box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 8%, transparent);
      transform: scale(1);
    }
    100% {
      box-shadow: inset 0 0 0 2px transparent;
      transform: scale(1);
    }
  }
  .metod-swap-flash {
    animation: metod-swap-flash 240ms var(--motion-ease);
  }
}


/* Reduced-motion fallback: page paints immediately, no breath, no
   swap-flash, no hover-translate. Hover still gets the shadow bump
   (it's pure colour, not motion) so affordance stays visible. */
@media (prefers-reduced-motion: reduce) {
  .app-shell__main { opacity: 1; transform: none; }
  .surface-card--interactive:hover,
  .surface-card-lift:hover { box-shadow: var(--shadow-lift); transform: none; }
}
