/* Ambient layers behind the Today hero (markup from js/adaptive/ambient.js + assets/*.js).
   Decoration only: nothing here takes pointer events or sits above the hero content (which is
   z-index 2), and only transform/opacity animate, so every frame stays on the compositor.
   Base styles are each particle's RESTING pose; animation is an extra on top. That way switching
   animation off (reduced motion, data-motion, .adp-static) leaves a calm, complete still scene.
   Colours come from --adp-* variables; the fallbacks already suit every time-of-day theme.
   themeTokens.js sets --adp-glow and --adp-weather-effect on :root as TRANSLUCENT washes (e.g.
   rgba(120,152,196,0.38) in rain, fully transparent with no weather), so they only tint glows,
   halos and the window glass — a particle painted with them would all but vanish.
   color-mix() / min() sit inside @supports: a declaration holding var() is never rejected at
   parse time, so an older browser would drop the rule's plain fallback instead of the new one.
   Per-particle numbers (--d duration, --dl delay, --o opacity, --y resting offset, --sz size…)
   are written inline by the JS. */

.adp-ambient { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; contain: layout paint; }
.adp-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; animation: adp-fade-in 0.8s ease-out both; }
.adp-layer i, .adp-layer span { position: absolute; display: block; pointer-events: none; }
.adp-layer svg { display: block; overflow: visible; pointer-events: none; }

@keyframes adp-fade-in { from { opacity: 0; } to { opacity: 1; } }
/* Falling particles are full-height columns with the particle at their top edge, so translating
   the column by ±100% of its own height carries the particle across the whole scene. */
@keyframes adp-fall { from { transform: translate3d(0, -100%, 0); } to { transform: translate3d(var(--dx, 0px), 100%, 0); } }
@keyframes adp-sway { from { transform: translate3d(-6px, 0, 0); } to { transform: translate3d(6px, 0, 0); } }
@keyframes adp-twinkle { 0%, 100% { opacity: var(--o, 0.8); } 50% { opacity: calc(var(--o, 0.8) * 0.35); } }
@keyframes adp-drift { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(var(--dx, 24px), 0, 0); } }
@keyframes adp-breathe { from { opacity: calc(var(--o, 0.7) * 0.7); transform: scale(0.96); } to { opacity: var(--o, 0.7); transform: scale(1.04); } }
@keyframes adp-tumble { from { transform: rotate(var(--r, 0deg)); } to { transform: rotate(calc(var(--r, 0deg) + 360deg)); } }
@keyframes adp-swing { from { transform: rotate(-2.5deg); } to { transform: rotate(2.5deg); } }
@keyframes adp-bob { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(0, -7px, 0); } }
@keyframes adp-confetti-fall {
  0% { transform: translate3d(0, -100%, 0); opacity: 1; }
  85% { opacity: 1; }
  100% { transform: translate3d(var(--dx, 0px), 100%, 0); opacity: 0; }
}
@keyframes adp-drip {
  0% { transform: translate3d(0, 0, 0); opacity: 0; }
  15% { opacity: 0.6; }
  100% { transform: translate3d(0, 34px, 0); opacity: 0; }
}

/* ---- Falling columns: rain, snow, leaves, blossoms, confetti ---- */
.adp-rain > i, .adp-heavy-rain > i, .adp-snow > i, .adp-leaves > i, .adp-blossoms > i, .adp-confetti > i {
  top: 0; width: 12px; height: 100%; transform: translate3d(0, var(--y, 40%), 0);
}

/* Rain: thin slanted drops, never more than 0.35 opaque, so text on top stays crisp. */
.adp-rain { --dx: -52px; }
.adp-heavy-rain { --dx: -72px; }
.adp-rain > i::before, .adp-heavy-rain > i::before {
  content: ""; position: absolute; top: 0; left: 0; width: 1.6px; height: var(--h, 14px); border-radius: 1px;
  background: linear-gradient(to bottom, rgba(111, 159, 204, 0), var(--adp-rain, #6F9FCC) 60%);
  opacity: var(--o, 0.3); transform: rotate(12deg); /* the JS already keeps --o ≤ 0.35 */
}
@supports (opacity: min(0.3, 0.35)) {
  .adp-rain > i::before, .adp-heavy-rain > i::before { opacity: min(var(--o, 0.3), 0.35); }
}
.adp-heavy-rain > i::before { width: 2px; transform: rotate(16deg); }
.adp-rain > i, .adp-heavy-rain > i { animation: adp-fall var(--d, 1s) linear var(--dl, 0s) infinite; }

/* Snow: soft round flakes with a faint edge so they still read on the light day themes. */
.adp-snow > i { animation: adp-fall var(--d, 10s) linear var(--dl, 0s) infinite; }
.adp-snow > i::before {
  content: ""; position: absolute; top: 0; left: 0; width: var(--sz, 4px); height: var(--sz, 4px); border-radius: 50%;
  background: var(--adp-snow, #FFFFFF); box-shadow: 0 0 0 1px rgba(140, 160, 190, 0.18); opacity: var(--o, 0.8);
  animation: adp-sway var(--sw, 3.2s) ease-in-out var(--dl, 0s) infinite alternate;
}

/* Leaves: small tumbling autumn leaves. */
.adp-leaves > i { animation: adp-fall var(--d, 12s) linear var(--dl, 0s) infinite; }
.adp-leaves > i::before {
  content: ""; position: absolute; top: 0; left: 0; width: var(--sz, 9px); height: calc(var(--sz, 9px) * 0.62);
  border-radius: 0 100% 0 100%; background: var(--adp-leaf, #E9A23B); opacity: 0.85; transform: rotate(var(--r, 0deg));
  animation: adp-tumble var(--sp, 7s) linear var(--dl, 0s) infinite;
}
.adp-leaves > i:nth-child(3n+2)::before { background: var(--adp-leaf-2, #D9793F); }
.adp-leaves > i:nth-child(3n)::before { background: var(--adp-leaf-3, #8DBF6A); }

/* Blossoms (Tết): hoa mai and hoa đào drifting down, turning slowly. */
.adp-blossoms > i { animation: adp-fall var(--d, 14s) linear var(--dl, 0s) infinite; }
.adp-blossoms .adp-blossom {
  position: absolute; top: 0; left: 0; width: var(--sz, 12px); height: var(--sz, 12px); opacity: 0.9; transform: rotate(var(--r, 0deg));
}
.adp-blossoms > i .adp-blossom { animation: adp-tumble var(--sp, 9s) linear var(--dl, 0s) infinite; }
.adp-blossoms > i:nth-child(odd) .adp-petals { fill: var(--adp-blossom-mai, #FFD24D); }
.adp-blossoms > i:nth-child(even) .adp-petals { fill: var(--adp-blossom-dao, #F7A8C4); }

/* Confetti: one or two passes (--n), then the pieces fade out for good. */
.adp-confetti > i { animation: adp-confetti-fall var(--d, 4s) cubic-bezier(0.3, 0.1, 0.6, 1) var(--dl, 0s) var(--n, 1) both; }
.adp-confetti > i::before {
  content: ""; position: absolute; top: 0; left: 0; width: var(--w, 5px); height: var(--h, 8px); border-radius: 1.5px;
  background: var(--adp-accent, #FF8C74); opacity: 0.85; transform: rotate(var(--r, 0deg));
  animation: adp-tumble var(--d, 4s) linear var(--dl, 0s) var(--n, 1) both;
}
.adp-confetti > i.adp-confetti-dot::before { height: var(--w, 5px); border-radius: 50%; }
.adp-confetti > i:nth-child(6n+2)::before { background: var(--adp-confetti-2, #FFC94D); }
.adp-confetti > i:nth-child(6n+3)::before { background: var(--adp-confetti-3, #7CCB8A); }
.adp-confetti > i:nth-child(6n+4)::before { background: var(--adp-confetti-4, #5FB4E8); }
.adp-confetti > i:nth-child(6n+5)::before { background: var(--adp-confetti-5, #B0A2ED); }
.adp-confetti > i:nth-child(6n)::before { background: var(--adp-confetti-6, #F48FB1); }

/* ---- Sky ---- */
.adp-stars > i {
  width: var(--sz, 2px); height: var(--sz, 2px); border-radius: 50%;
  background: var(--adp-star, var(--deco-a, #FFE08F)); opacity: var(--o, 0.8);
  animation: adp-twinkle var(--d, 4s) ease-in-out var(--dl, 0s) infinite;
}
.adp-stars > i.adp-star-x { border-radius: 0; clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); }

/* Clouds: one rounded bar plus two puffs, drifting a few px over a minute. */
.adp-clouds > i {
  width: var(--w, 60px); height: calc(var(--w, 60px) * 0.34); border-radius: 999px;
  background: var(--adp-cloud, #FFFFFF); opacity: var(--o, 0.7);
  animation: adp-drift var(--d, 60s) ease-in-out var(--dl, 0s) infinite alternate;
}
/* The night hero has light text (#C9CEF3 muted on navy): a pale cloud behind the subtitle took it
   to ~2.6:1. Night clouds are a deeper blue-grey at ≤ 0.48 opacity, which keeps even the muted
   text ≥ 4.5:1 (test/adaptiveAmbient.test.mjs checks it against the night palette in app.css). */
html[data-period="night"] .adp-clouds > i { background: var(--adp-cloud-night, #5E6AA3); opacity: calc(var(--o, 0.7) * 0.6); }
.adp-clouds > i::before, .adp-clouds > i::after { content: ""; position: absolute; border-radius: 50%; background: inherit; }
.adp-clouds > i::before { width: 46%; height: 150%; left: 16%; bottom: 30%; }
.adp-clouds > i::after { width: 34%; height: 110%; right: 18%; bottom: 36%; }

.adp-sun-glow .adp-sun-core {
  top: calc(var(--sz, 150px) * -0.4); right: calc(var(--sz, 150px) * -0.3); width: var(--sz, 150px); height: var(--sz, 150px); border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 210, 122, 0.7) 0%, rgba(255, 210, 122, 0) 68%);
  opacity: var(--o, 0.7); animation: adp-breathe 10s ease-in-out infinite alternate;
}

/* Moon: phase drawn in SVG by the JS; bigger, brighter and haloed when it is the star of the
   scene (Mid-Autumn, a full moon night). It sits in the sky band's top-right corner, past the
   date chip: the greeting starts ~42 px down, and a 52 px moon lower down sat right behind it
   (pale disc under white letters). The disc is 80% of the box, so it ends by ~41 px. */
.adp-moon .adp-moon-body { top: var(--adp-moon-top, 8px); right: var(--adp-moon-right, 16px); width: 30px; height: 30px; }
.adp-moon.adp-moon-prominent .adp-moon-body { top: var(--adp-moon-top, 3px); right: var(--adp-moon-right, 12px); width: 42px; height: 42px; }
.adp-moon .adp-moon-svg { position: relative; width: 100%; height: 100%; }
.adp-moon .adp-moon-lit { fill: var(--adp-moon, #FFE9B0); opacity: 0.88; }
.adp-moon.adp-moon-prominent .adp-moon-lit { opacity: 1; }
.adp-moon .adp-moon-dark { fill: var(--adp-moon-shadow, #C8CEFF); fill-opacity: 0.16; }
.adp-moon .adp-moon-halo {
  inset: -65%; border-radius: 50%; --o: 0.85;
  background: radial-gradient(circle, rgba(255, 231, 168, 0.5) 0%, rgba(255, 231, 168, 0) 68%);
  opacity: var(--o); animation: adp-breathe 9s ease-in-out infinite alternate;
}

.adp-fog > i {
  left: -25%; width: 150%; height: 38%; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(244, 246, 250, 0.85) 0%, rgba(244, 246, 250, 0) 70%);
  opacity: var(--o, 0.35); animation: adp-drift var(--d, 36s) ease-in-out var(--dl, 0s) infinite alternate;
}
/* Fog bands run across the whole hero, right behind the greeting: at night (light text) a pale
   fog took the muted subtitle to ~2.3:1, so night fog is a deep blue haze instead (≥ 4.5:1). */
html[data-period="night"] .adp-fog > i {
  background: radial-gradient(ellipse at center, rgba(98, 110, 166, 0.85) 0%, rgba(98, 110, 166, 0) 70%);
}

/* ---- Indoor rainy window: frame and sheen hug the edges; nothing crosses the text. The glass
   edge picks up the weather's own wash (--adp-weather-effect: blue-grey rain, warm sun…). ---- */
.adp-window .adp-window-frame {
  inset: 6px; border-radius: 22px; border: 5px solid rgba(255, 255, 255, 0.42);
  border-color: var(--adp-window-frame, rgba(255, 255, 255, 0.42));
  box-shadow: inset 0 0 0 1px rgba(120, 140, 170, 0.14), inset 0 0 10px 2px var(--adp-weather-effect, rgba(111, 159, 204, 0.22));
}
.adp-window .adp-window-sheen {
  inset: 11px; border-radius: 17px;
  background: linear-gradient(125deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0) 32%, rgba(255, 255, 255, 0) 62%, rgba(255, 255, 255, 0.08) 72%, rgba(255, 255, 255, 0) 80%);
}
.adp-window .adp-drip {
  width: var(--sz, 4px); height: calc(var(--sz, 4px) * 1.3); border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  background: var(--adp-rain, #6F9FCC); opacity: 0.45;
  animation: adp-drip var(--d, 9s) ease-in var(--dl, 0s) infinite;
}

/* ---- Festive ---- */
/* Everything that stays put (lanterns, ghosts, hearts) lives in the sky band — the top 40 px of
   the hero — in px from the left edge (above the mascot) or the right edge (past the date chip).
   On heroes narrower than a 360 px phone the right-hand gap is gone, so those items step out. */
@media (max-width: 359.98px) { .adp-layer .adp-zone-r { display: none; } }

/* Lanterns (Mid-Autumn warm ones and Tết red ones) hang from the top edge and swing from their
   string; the glow behind the body pulses warmly. */
.adp-lanterns .adp-lantern, .adp-lanterns-red .adp-lantern { top: 0; width: var(--w, 26px); transform-origin: 50% 0; animation: adp-swing var(--d, 7s) ease-in-out var(--dl, 0s) infinite alternate; }
.adp-lanterns .adp-lantern-string, .adp-lanterns-red .adp-lantern-string { position: relative; width: 1px; height: var(--len, 12px); margin: 0 auto; background: var(--adp-lantern-string, rgba(120, 90, 60, 0.45)); }
.adp-lanterns .adp-lantern svg, .adp-lanterns-red .adp-lantern svg { position: relative; width: 100%; height: auto; }
.adp-lanterns .adp-lantern-body { fill: var(--adp-event-decoration, #FFB35C); }
.adp-lanterns .adp-lantern-star { fill: var(--adp-lantern-star, #FF9A6B); }
.adp-lanterns-red .adp-lantern-red-body { fill: var(--adp-lantern-red, #E4473C); }
.adp-lanterns .adp-lantern-glow, .adp-lanterns-red .adp-lantern-glow {
  left: 50%; top: calc(var(--len, 12px) + var(--w, 26px) * 0.6); width: calc(var(--w, 26px) * 2.4); height: calc(var(--w, 26px) * 2.4);
  margin: calc(var(--w, 26px) * -1.2) 0 0 calc(var(--w, 26px) * -1.2); border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 200, 120, 0.55) 0%, rgba(255, 200, 120, 0) 65%);
  opacity: var(--o, 0.7); animation: adp-breathe var(--gd, 4s) ease-in-out var(--dl, 0s) infinite alternate;
}

/* Fairy lights: the wire is an SVG stretched across the top; bulbs twinkle out of step. The bulb
   itself is solid — the event's translucent --adp-glow only tints the halo around it. */
.adp-fairy-lights .adp-fairy-wire { position: absolute; top: 0; left: 0; width: 100%; height: 32px; }
.adp-fairy-lights > i {
  width: 6px; height: 9px; margin-left: -3px; border-radius: 50% 50% 45% 45%;
  background: var(--adp-light-1, #FFD27A); box-shadow: 0 0 5px 1px var(--adp-glow, rgba(255, 210, 122, 0.55)); opacity: var(--o, 0.95);
  animation: adp-twinkle var(--d, 3s) ease-in-out var(--dl, 0s) infinite;
}
.adp-fairy-lights > i:nth-of-type(4n+2) { background: var(--adp-light-2, #FF9C8A); box-shadow: 0 0 5px 1px rgba(255, 156, 138, 0.5); }
.adp-fairy-lights > i:nth-of-type(4n+3) { background: var(--adp-light-3, #9FD9A8); box-shadow: 0 0 5px 1px rgba(159, 217, 168, 0.5); }
.adp-fairy-lights > i:nth-of-type(4n) { background: var(--adp-light-4, #9CC7F0); box-shadow: 0 0 5px 1px rgba(156, 199, 240, 0.5); }

.adp-tiny-tree .adp-tree { right: var(--adp-tree-right, 12px); bottom: var(--adp-tree-bottom, 20px); width: var(--sz, 44px); opacity: 0.92; }
.adp-tiny-tree .adp-tree svg { width: 100%; height: auto; }
.adp-tiny-tree .adp-tree-star { animation: adp-twinkle 3.6s ease-in-out infinite; }

.adp-pumpkins .adp-pumpkin { bottom: var(--adp-ground, 18px); width: var(--sz, 26px); }
.adp-pumpkins .adp-pumpkin svg, .adp-tiny-ghosts .adp-ghost svg, .adp-hearts .adp-heart svg { width: 100%; height: auto; }
.adp-tiny-ghosts .adp-ghost { width: var(--sz, 22px); opacity: 0.8; animation: adp-bob var(--d, 5.5s) ease-in-out var(--dl, 0s) infinite alternate; }

.adp-hearts .adp-heart { width: var(--sz, 12px); opacity: var(--o, 0.7); animation: adp-bob var(--d, 6s) ease-in-out var(--dl, 0s) infinite alternate; }
.adp-hearts .adp-heart path { fill: var(--adp-heart, var(--adp-accent, #FF8FB1)); }

/* Theme colours that carry their own alpha (the adaptive tokens) mix in here, where supported.
   After every plain rule on purpose: same specificity, so the later one wins. Night fog keeps
   its deeper haze (its selector is more specific). */
@supports (background: color-mix(in srgb, red 50%, transparent)) {
  .adp-sun-glow .adp-sun-core { background: radial-gradient(circle, color-mix(in srgb, var(--adp-glow, #FFD27A) 70%, transparent) 0%, transparent 68%); }
  .adp-moon .adp-moon-halo { background: radial-gradient(circle, color-mix(in srgb, var(--adp-glow, #FFE7A8) 50%, transparent) 0%, transparent 68%); }
  .adp-fog > i { background: radial-gradient(ellipse at center, color-mix(in srgb, var(--adp-fog, #F4F6FA) 85%, transparent) 0%, transparent 70%); }
  .adp-lanterns .adp-lantern-glow, .adp-lanterns-red .adp-lantern-glow {
    background: radial-gradient(circle, color-mix(in srgb, var(--adp-glow, #FFC878) 55%, transparent) 0%, transparent 65%);
  }
}

/* ---- Motion off: every layer keeps its resting pose. .adp-static is set by the JS for
   motion "reduced"/"off", performance mode and per-layer `static`; the html[data-motion] and
   media-query rules catch the user's settings even before the JS re-renders. ---- */
.adp-static, .adp-static *, .adp-static *::before, .adp-static *::after { animation: none !important; }
html[data-motion="reduced"] .adp-layer, html[data-motion="reduced"] .adp-layer *,
html[data-motion="reduced"] .adp-layer *::before, html[data-motion="reduced"] .adp-layer *::after { animation: none !important; }
html[data-motion="off"] .adp-layer, html[data-motion="off"] .adp-layer *,
html[data-motion="off"] .adp-layer *::before, html[data-motion="off"] .adp-layer *::after { animation: none !important; }
@media (prefers-reduced-motion: reduce) {
  .adp-layer, .adp-layer *, .adp-layer *::before, .adp-layer *::after { animation: none !important; }
}

/* High-contrast modes and paper get the plain hero. */
@media (forced-colors: active) { .adp-ambient { display: none; } }
@media print { .adp-ambient { display: none; } }
