/* ky-wallfx — per-wallpaper personalization FX (companion to ky/ky-wallfx.js).
   ==========================================================================

   Structural + accent CSS only. All motion that varies per frame lives in the
   JS as canvas drawing and spring physics; the ONLY CSS animations here are
   two opacity-only ambient keyframes (ember pulse, frost arrival), both
   killed under reduced motion.

   GATES. Everything in this file is inert unless BOTH of these hold:
     · html.ky-wallmode          — the wallpaper experience is active
     · html[data-ky-wall="..."]  — the specific plate the rule serves
   So on the plain Honey & Ink portfolio none of this can ever match, and a
   failed ky-wallfx.js load leaves zero visual residue: no rule below does
   anything without a class the JS stamps.

   TRANSFORM CONTRACT. .ky-fxg is the one transform this module ever applies
   to page chrome, and it is written as a CLASS + custom properties rather
   than inline style precisely so that any other engine's inline transform
   (Framer entrances on index.html, ky-cursor's letter spans) beats it
   automatically. --kyfx-base carries the element's own pre-existing computed
   transform (captured at rest by the JS) so displacing an element never
   erases how it positioned itself. #ky-music is deliberately NOT a transform
   target: its base transform is owned by a fill-mode:both entrance animation
   that would silently override this rule — the JS knows this and only ever
   paints canvas FX over the bar, never moves it. */

/* ── the one shared overlay canvas ──────────────────────────────────────── */
/* Above the music bar (9000) so weather can pass over glass chrome; below
   the cinema exit pill (9200) and every flood (12000) so controls and swap
   covers always win. pointer-events:none is the never-intercept-clicks law. */
#ky-wallfx{
  position:fixed;
  inset:0;
  z-index:9080;
  pointer-events:none;
  opacity:1;
  transition:opacity .45s ease;
}
/* stamped by the JS when the wallpaper stage has scrolled away (index.html
   in persistent wallmode) — the weather fades rather than floating over
   #Works content the plate has nothing to do with */
#ky-wallfx.ky-wallfx-dim{ opacity:0; }

/* ── gravitational / parallax choreography (singularity, starfield-drift) ─ */
/* translate3d keeps the element on its own compositor layer for the whole
   time the class is armed, so the spring never triggers layout or paint.
   Custom-property fallbacks mean an armed element with no values yet renders
   exactly at rest — there is no "half-applied" state. */
html.ky-wallmode .ky-fxg{
  transform:
    translate3d(var(--kyfx-x, 0px), var(--kyfx-y, 0px), 0)
    scale(var(--kyfx-s, 1))
    var(--kyfx-base, translateZ(0));
  will-change:transform;
}

/* ── forest-snow: frost sheen on the glass chrome edges ─────────────────── */
/* A static crystalline top-light on the calendar card and the contact pill.
   ::before is verified free on both targets (ky-cal.css and the Tailwind
   button classes define none; ky-glassify.css declares no pseudo-elements).
   #ky-music::before belongs to the glass hairline, so the bar is excluded.
   The wash is capped low enough that ink over it stays comfortably past
   4.5:1 — the heavy white lives only in the top 2px edge line. */
html.ky-wallmode[data-ky-wall="forest-snow"] .ky-fx-frost{
  position:relative;   /* static targets only; no offsets, layout unchanged */
}
html.ky-wallmode[data-ky-wall="forest-snow"] .ky-fx-frost::before{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  pointer-events:none;
  background:
    linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,0) 24%),
    radial-gradient(130% 90% at 50% -12%, rgba(214,236,255,.14), transparent 56%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.34),
    inset 0 0 0 1px rgba(230,244,255,.10);
  animation:kyfx-frost-in .9s ease both;
}
@keyframes kyfx-frost-in{
  from{ opacity:0; }
  to  { opacity:1; }
}

/* ── ember-ring: warm pulse on the accent chrome ────────────────────────── */
/* An inset ember rim that breathes on the calendar card and the contact
   pill. ::after is free on both (the CONTACT button's after: trick lives on
   an inner span, .kyc2-d state marks live on the day cells). Opacity is the
   only animated property. The accent var is published on <html> by the JS
   from the ky:wall-theme detail, with the plate's own ember as fallback. */
html.ky-wallmode[data-ky-wall="ember-ring"] .ky-fx-ember{
  position:relative;
}
html.ky-wallmode[data-ky-wall="ember-ring"] .ky-fx-ember::after{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  pointer-events:none;
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--ky-fx-accent, #E76F45) 52%, transparent),
    inset 0 -10px 24px -14px var(--ky-fx-accent, #E76F45);
  opacity:.14;
  animation:kyfx-ember-pulse 3.6s ease-in-out infinite;
}
@keyframes kyfx-ember-pulse{
  0%, 100%{ opacity:.14; }
  50%     { opacity:.42; }
}

/* ── dignified static path ──────────────────────────────────────────────── */
/* The JS refuses to run under any of these, so this is the CSS backstop for
   the same rule: reduced motion, no hover, or a coarse pointer gets plain,
   still chrome and no overlay at all — never a frozen half-effect. */
@media (prefers-reduced-motion: reduce), (hover: none), (pointer: coarse){
  #ky-wallfx{ display:none !important; }
  html.ky-wallmode .ky-fxg{ transform:none !important; will-change:auto; }
  html.ky-wallmode .ky-fx-frost::before,
  html.ky-wallmode .ky-fx-ember::after{ display:none !important; }
}
