/*
Theme Name:  WNMU Halloween
Template:    wnmu
Description: Seasonal Halloween takeover of the wnmu theme. Inherits everything from wnmu and only adds a skin: night/pumpkin header, nav, subheader and footer, a greeting ribbon with a pause control, cobwebs, a graveyard strip, hero fog, and Bug.js flies and spiders. Activate "wnmu" again to return to the normal site; nothing else changes.
Author:      WNMU Web Strategy & Development
Version:     1.0.0
Requires at least: 5.3
Requires PHP: 7.4
Text Domain: wnmu
*/

/* ==========================================================================
   HOW THIS FILE IS ORGANIZED
   1. Tokens
   2. Recolor the parent chrome (utility bar, header, nav, subheader, footer)
   3. Hover glow + focus rings ("spooky hover effects")
   4. Greeting ribbon
   5. Sky layer over the header (cobwebs, dangling spider)
   6. Soil strip above the footer (tombstones, pumpkins)
   6b. Fog on the home page hero slider
   6c. Bug.js flies and spiders
   6d. "Happy Halloween!" header logo
   6e. Slime dripping off the header
   6f. Peeking ghost
   6g. Ghost that follows the mouse
   7. Motion gating (pause control, reduced motion), forced colors, print

   ACCESSIBILITY NOTES (WCAG 2.1 AA)
   - Every text/background pair below was checked for 4.5:1, every UI border
     or icon for 3:1. Ratios are noted next to the rule.
   - All decor is aria-hidden and pointer-events: none, so it is never
     announced and never blocks a click.
   - CSS decor sits outside text: in the header's empty bands, in a strip
     above the footer, and (fog) between the hero photo and its text.
   - Exception, on purpose: the Bug.js flies and spiders (6c) walk over the
     whole page, text included. They move away or die on hover, ignore
     touches on touch screens, and are removed when motion is paused.
   - Motion only runs while <html> has .hw-motion (set by the ribbon script).
     See section 7.
   ========================================================================== */


/* 1. Tokens ---------------------------------------------------------------- */

/* Creepster (SIL OFL 1.1, assets/fonts/OFL.txt), self-hosted from the
   @fontsource package: no request to Google, so no visitor IPs leave the
   site. Display use ONLY (ribbon title, footer headings): it's a dripping
   novelty face and would hurt readability as body text. swap = text shows
   in the fallback right away, then switches. */
@font-face {
  font-family: "Creepster";
  src: url("assets/fonts/creepster-latin-400-normal.woff2") format("woff2"),
       url("assets/fonts/creepster-latin-400-normal.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --hw-night:      #140a1f;  /* near-black purple: main dark surface        */
  --hw-night-2:    #22103a;  /* dropdown panels                            */
  --hw-night-3:    #341a59;  /* hover on dark panels                       */
  --hw-pumpkin:    #f5761a;  /* 6.9:1 vs --hw-night, either way round       */
  --hw-pumpkin-hi: #ff9a3c;  /* 9.1:1 vs --hw-night                         */
  --hw-candle:     #ffd36b;  /* hover text on dark surfaces                 */
  --hw-parchment:  #fbeedd;  /* header background; black text ~19:1         */
  --hw-soil:       #3a2416;

  /* Height of the band the sky layer covers. Mobile: header only. */
  --hw-utility-h: 0px;
  --hw-header-h:  120px;

  /* The brand purple drives buttons/bands in many components. A deeper
     "midnight" purple darkens them for the season. Only goes DARKER, so
     white-on-purple gets more contrast and purple-on-light text too. */
  --color-primary-purple: #26103f;
}

@media (min-width: 801px) {
  :root {
    --hw-utility-h: 40px;                         /* .site-utility-bar height */
    --hw-header-h:  var(--site-header-height);    /* 130px from the parent    */
  }
}

::selection { background: var(--hw-pumpkin); color: var(--hw-night); }
html { scrollbar-color: var(--hw-pumpkin) var(--hw-night); }


/* 2. Recolor the parent chrome -------------------------------------------- */

/* Header: variables are scoped to .site-header in the parent, so they are
   overridden there. `html .site-header` outranks the parent's `.site-header`. */
html .site-header {
  --color-site-header-bg                             : var(--hw-parchment);
  --color-site-header-box-shadow                     : hsl(25 90% 25% / 0.45);
  --color-site-header-dropdown-content               : var(--hw-night-2);
  --color-site-header-dropdown-content-center        : hsl(272 55% 8% / 0.94);
  --color-site-header-dropdown-content-center-active : var(--hw-night-3);
  --color-site-header-dropdown-content-link-active   : var(--hw-pumpkin-hi);  /* 7.4:1 on night-2 */
  --color-site-header-apply-btn                      : var(--hw-pumpkin);
  --color-site-header-apply-btn-active               : var(--hw-pumpkin-hi);
  --color-site-header-apply-btn-text                 : var(--hw-night);       /* 6.9:1 / 9.1:1 */
}

/* Mobile: the parent only paints the header background on desktop. */
@media (max-width: 800px) {
  html .site-header { background: var(--hw-parchment); }
}

@media (min-width: 801px) {
  .site-utility-bar { background: #0b0612; }  /* 85% white links: ~16:1 */

  /* Pumpkin rule along the header's bottom edge. Inset shadow, not a
     border, so the fixed 130px header keeps its exact height. */
  html .site-header {
    box-shadow: inset 0 -3px 0 var(--hw-pumpkin),
                0 0 24px 0 var(--color-site-header-box-shadow);
  }

  /* The search toggle hard-codes white; white on pumpkin is only 3.1:1. */
  html .site-header-search-btn { color: var(--hw-night); }
}

/* Subheader (sticky department bar). */
html .subheader {
  --color-subheader-bg: var(--hw-night);          /* white text: 19:1 */
  --color-subheader-submenu-bg: var(--hw-night-2);
  box-shadow: inset 0 -2px 0 var(--hw-pumpkin);
}
/* Site-name pill: pumpkin with dark text (6.9:1). The link inside it would
   otherwise inherit the subheader's white (3.1:1 on pumpkin). */
html .subheader-site-name,
html .subheader-site-name a { background: var(--hw-pumpkin); color: var(--hw-night); }
html .subheader-site-name a { background: none; }

/* Footer. */
html .site-footer { background: var(--hw-night); }
html .site-footer__col-heading {
  color: var(--hw-pumpkin-hi);                       /* 9.1:1 */
  /* Creepster at a size where it stays legible (the parent's 0.7rem
     small caps would be unreadable in a novelty face). */
  font-family: "Creepster", var(--font-family);
  font-size: 1.15rem;
  font-weight: 400;
  letter-spacing: 0.06em;
}
html .site-footer__col a:hover,
html .site-footer__col a:focus-visible,
html .site-footer__address a:hover,
html .site-footer__address a:focus-visible { color: var(--hw-candle); }


/* 3. Hover glow + focus rings -------------------------------------------- */

/* Candle glow on light text over dark surfaces. A glow behind light text
   only adds contrast, it never reduces it. */
.site-utility-bar a:hover,
.site-utility-bar a:focus-visible,
.site-nav-dropdown-content a:hover,
.site-nav-dropdown-content a:focus-visible,
.subheader a:hover,
.subheader a:focus-visible,
.site-footer a:hover,
.site-footer a:focus-visible {
  text-shadow: 0 0 10px hsl(30 100% 55% / 0.85);
}

/* Subtle pumpkin halo behind the dark nav labels on the light header. */
.site-nav-dropdown-item:hover > header,
.site-nav-dropdown-item:focus-visible > header {
  text-shadow: 0 0 8px hsl(28 100% 55% / 0.6);
}

/* Glowing pumpkin buttons. */
.site-header-links-apply-now:hover,
.site-header-links-apply-now:focus-visible,
.site-header-search-btn:hover,
.site-header-search-btn:focus-visible {
  box-shadow: 0 0 0 3px hsl(28 100% 62% / 0.35),
              0 0 18px 2px hsl(24 100% 50% / 0.55);
}

/* Focus rings: dark ring on the light header (WCAG 2.4.7 / 1.4.11),
   pumpkin ring on the dark surfaces (9.1:1). */
.site-header a:focus-visible,
.site-header button:focus-visible,
.site-nav-dropdown-item:focus-visible {
  outline: 3px solid var(--hw-night);
  outline-offset: 2px;
}
.site-nav-dropdown-content a:focus-visible,
.site-utility-bar a:focus-visible,
.subheader a:focus-visible,
.site-footer a:focus-visible,
.hw-ribbon button:focus-visible {
  outline: 3px solid var(--hw-pumpkin-hi);
  outline-offset: 2px;
}

/* Pumpkin underline on plain text links in page content. Only the
   decoration color changes; link text keeps its normal color. */
#content a:not([class]):hover {
  text-decoration-color: var(--hw-pumpkin);
  text-decoration-thickness: 2px;
}


/* 4. Greeting ribbon -------------------------------------------------------- */
.hw-ribbon {
  background: var(--hw-night);
  color: #fff;                                   /* 19:1 */
  border-bottom: 2px solid var(--hw-pumpkin);
}
/* `hidden` must win over the display values set below. */
.hw-ribbon[hidden],
.hw-ribbon [hidden] { display: none !important; }

.hw-ribbon__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  max-width: calc(100% - 40px);
  min-height: 44px;
  margin: 0 auto;
  padding: 6px 0;
}
.hw-ribbon__icon { flex: none; width: 30px; height: 25px; }
.hw-ribbon__msg {
  flex: 1 1 16rem;
  margin: 0;
  color: #fff;                                   /* the parent colors <p> black */
  font-size: 0.9rem;
  line-height: 1.35;
}
.hw-ribbon__title {
  font-family: "Creepster", var(--font-family);
  font-size: 1.25rem;
  letter-spacing: 0.04em;
  color: var(--hw-pumpkin-hi);                   /* 9.1:1 on night */
  margin-right: 0.35em;
}
.hw-ribbon__btn {
  min-height: 32px;                              /* above the 24px target minimum */
  padding: 4px 14px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--hw-pumpkin-hi);                   /* 9.1:1 */
  background: transparent;
  border: 2px solid var(--hw-pumpkin);           /* 6.9:1 non-text contrast */
  border-radius: 100px;
  cursor: pointer;
  transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
}
.hw-ribbon__btn:hover {
  color: var(--hw-night);                        /* 6.9:1 on pumpkin */
  background: var(--hw-pumpkin);
  box-shadow: 0 0 14px hsl(24 100% 50% / 0.6);
}
.hw-ribbon__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 36px;
  height: 36px;
  padding: 0;
  color: #fff;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.hw-ribbon__close:hover { background: hsl(0 0% 100% / 0.14); }

/* Visually hidden, still read by screen readers. Own class so it doesn't
   depend on the parent's .screen-reader-text. */
.hw-sr {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 600px) {
  .hw-ribbon__inner { max-width: calc(100% - 32px); }  /* 16px gutters */
  .hw-ribbon__msg { font-size: 0.82rem; }
  .hw-ribbon__title { display: block; }                /* own line, no orphaned words */
}


/* 5. Sky layer over the header ------------------------------------------- */

/* Symbol sprite: kept in the DOM (display:none breaks <use> in some
   browsers) but takes no space. */
.hw-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* A zero-height anchor right before the utility bar/header. Its inner box
   covers exactly the utility bar + header band and clips everything, so
   nothing can cause horizontal scrolling or reach the dropdown menus,
   which open BELOW the header. */
.hw-sky { position: relative; height: 0; }
@media (min-width: 801px) {
  /* The desktop header is position:relative; z-index:100 with an opaque
     background, so decor has to sit one level above it to be seen. */
  .hw-sky { z-index: 101; }
}
.hw-sky__inner {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: calc(var(--hw-utility-h) + var(--hw-header-h));
  overflow: hidden;
  pointer-events: none;
}
.hw-sky svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* Cobwebs in the header's top corners. */
.hw-sky .hw-web {
  position: absolute;
  top: var(--hw-utility-h);
  width: 60px; height: 60px;
}
.hw-sky .hw-web--left  { left: 0; }
.hw-sky .hw-web--right { right: 0; transform: scaleX(-1); }

/* Spider bobbing on a thread from the right-hand web. Small, and kept in
   the gutter right of the Apply button. */
.hw-spider {
  position: absolute;
  top: var(--hw-utility-h);
  right: 2px;
  width: 18px; height: 18px;
  animation: hw-bob 5s ease-in-out infinite alternate;
}
.hw-spider::before {                       /* the thread */
  content: "";
  position: absolute;
  bottom: 80%; left: 50%;
  width: 1px; height: 200px;
  background: hsl(272 40% 18% / 0.55);
}
@keyframes hw-bob {
  from { transform: translateY(-6px); }
  to   { transform: translateY(26px); }
}

/* The left web would sit on the Halloween logo's tower art: hidden. */
.hw-sky .hw-web--left { display: none; }

/* Mobile: drop the spider too (the menu button sits in that corner). */
@media (max-width: 800px) {
  .hw-spider { display: none; }
}


/* 6. Soil strip above the footer ----------------------------------------- */
.hw-soil {
  position: relative;
  height: 80px;
  overflow: hidden;
  pointer-events: none;
  /* Transparent sky, then a soil band that fades into the footer's night. */
  background: linear-gradient(to bottom,
    transparent 0 48px,
    var(--hw-soil) 48px,
    #2b1a12 62px,
    var(--hw-night) 100%);
}
/* Little mounds along the horizon. */
.hw-soil::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 40px;
  height: 9px;
  background: radial-gradient(ellipse 36px 9px at 50% 100%, var(--hw-soil) 97%, transparent 100%) 0 0 / 90px 9px repeat-x;
}
.hw-soil__item { position: absolute; bottom: 30px; overflow: visible; }
.hw-soil__tomb    { width: 30px; height: 36px; }
.hw-soil__pumpkin { width: 34px; height: 28px; bottom: 28px; }
.hw-soil__tomb--1    { left: 5%; }
.hw-soil__pumpkin--1 { left: calc(5% + 36px); }
.hw-soil__tomb--2    { left: 48%; width: 34px; height: 42px; }
.hw-soil__pumpkin--2 { left: 76%; }
.hw-soil__tomb--3    { left: 88%; }



/* 6b. Fog on the home page hero slider ------------------------------------ */

/* Two drifting fog banks per slide, drawn with pseudo-elements so the
   slider markup (templates/front-page-int/sections/hero-slider.php) isn't
   touched.

   Stacking inside each slide (the slide is positioned and has a z-index,
   so it's its own stacking context):
     0  background image / video, color overlay
     1  FOG  ← here
     2  .fpi-hero__fg-wrap: foreground image, title, button
   So the fog drifts over the photo but never over text or buttons, and
   pagination/arrows (z 10 on the swiper) stay on top.

   Contrast: the fog is a lavender-grey, densest at the bottom and thinning
   out toward the top. Thick fog lightens the photo behind white titles, so
   section 6b also gives the hero title and subtitle a dark halo (below).
   Kicker pills and buttons have solid backgrounds and are unaffected.

   Each layer is 200% wide and its background tile is exactly half that
   (one slide width). Sliding it by -50% lands on an identical frame, so
   the loop is seamless. Only transform animates (cheap, GPU-composited). */
.fpi-hero__slide::before,
.fpi-hero__slide::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 200%;
  z-index: 1;
  pointer-events: none;
  background-repeat: repeat-x;
  background-size: 50% 100%;
}

/* Front bank: low and thick, drifts left. A flat ground haze plus puffs;
   puffs at 0% and 100% are halves of the same puff across the tile seam. */
.fpi-hero__slide::before {
  background-image:
    radial-gradient(ellipse 24% 36% at 0%   90%, hsl(270 22% 88% / 0.75), transparent 70%),
    radial-gradient(ellipse 24% 36% at 100% 90%, hsl(270 22% 88% / 0.75), transparent 70%),
    radial-gradient(ellipse 28% 30% at 22%  97%, hsl(270 22% 88% / 0.70), transparent 70%),
    radial-gradient(ellipse 22% 34% at 42%  84%, hsl(270 22% 88% / 0.60), transparent 70%),
    radial-gradient(ellipse 26% 30% at 62%  95%, hsl(270 22% 88% / 0.70), transparent 70%),
    radial-gradient(ellipse 20% 32% at 80%  82%, hsl(270 22% 88% / 0.55), transparent 70%),
    linear-gradient(to top, hsl(270 20% 86% / 0.45), hsl(270 20% 86% / 0.15) 35%, transparent 55%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 78%);
          mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 78%);
  animation: hw-fog-drift 60s linear infinite;
}

/* Back bank: rolls higher up the photo, softer, slower, drifts right
   (parallax against the front bank). Puffs that cross the tile edge are
   repeated 100% over (110% / -12%) so the seam doesn't show. */
.fpi-hero__slide::after {
  background-image:
    radial-gradient(ellipse 34% 30% at 10%  62%, hsl(270 18% 82% / 0.45), transparent 70%),
    radial-gradient(ellipse 34% 30% at 110% 62%, hsl(270 18% 82% / 0.45), transparent 70%),
    radial-gradient(ellipse 30% 26% at 38%  48%, hsl(270 18% 82% / 0.35), transparent 70%),
    radial-gradient(ellipse 32% 32% at 64%  70%, hsl(270 18% 82% / 0.45), transparent 70%),
    radial-gradient(ellipse 30% 24% at 88%  44%, hsl(270 18% 82% / 0.30), transparent 70%),
    radial-gradient(ellipse 30% 24% at -12% 44%, hsl(270 18% 82% / 0.30), transparent 70%),
    radial-gradient(ellipse 36% 28% at 50% 85%, hsl(270 18% 82% / 0.40), transparent 70%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 45%, transparent 92%);
          mask-image: linear-gradient(to top, #000 0%, #000 45%, transparent 92%);
  animation: hw-fog-drift 95s linear infinite reverse;
}

/* Dark halo around the hero's white text so it stays readable in thick fog
   (WCAG 1.4.3). Overrides the parent's single soft shadow. */
html .fpi-hero__title,
html .fpi-hero__sub {
  text-shadow: 0 0 2px rgb(0 0 0 / 0.85),
               0 0 10px rgb(20 10 31 / 0.8),
               0 2px 14px rgb(0 0 0 / 0.6) !important;
}

@keyframes hw-fog-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}


/* 6c. Bug.js flies and spiders ------------------------------------------- */

/* The library (assets/bug/bug.js) creates one empty <div class="bug"> per
   bug, inline-styled position:fixed; z-index:9999999, and moves it with
   transforms. assets/halloween-bugs.js adds/removes them with the ribbon's
   Pause/Play, so only the leftovers need CSS here. */

/* Squashed spiders fall to the bottom of the window and would sit there on
   top of whatever is underneath. Let clicks pass through them right away,
   then fade them out after a few seconds of twitching. */
.bug.bug-dead {
  pointer-events: none;
  animation: hw-bug-fade 1s ease-in 4s forwards;
}
@keyframes hw-bug-fade {
  to { opacity: 0; visibility: hidden; }
}

/* Touch screens have no hover, so a bug could never move out of the way
   and would swallow the tap on a link beneath it: taps go straight through
   flies and worms. Live spiders are the exception, so a tap knocks them
   down (mobile browsers send the tap to the spider as a mouseover). That
   tap lands on the spider, not on a link under it, and the falling spider
   lets the next tap through at once (.bug-dead above). */
@media (hover: none) {
  .bug { pointer-events: none; }
  .bug[style*="spider-sprite"]:not(.bug-dead) { pointer-events: auto; }
}

@media print, (forced-colors: active) {
  .bug { display: none !important; }
}

/* 6d. "Happy Halloween!" header logo --------------------------------------- */

/* functions.php prints an animated GIF <picture> and a static PNG <picture>;
   show exactly one, following the same .hw-motion switch as everything else.
   No JS / paused / reduced motion → static. */
.hw-logo { display: block; }
html:not(.hw-motion) .hw-logo--anim,
html.hw-motion .hw-logo--static { display: none; }

/* Native size of the art (278x75 desktop, 174x46 mobile), drawn from the 2x
   files on retina screens. Overrides the parent's 260px / 200px widths,
   which were set for the regular logo's proportions. */
html .hw-logo__img.site-header-logo { width: 278px; height: auto; }
@media (max-width: 800px) {
  html .hw-logo__img.site-header-logo { width: 174px; }
}

/* Worm sprite (ours, assets/bug/worm-sprite.png) is drawn at 2x. Bug.js
   positions frames in 1x pixels and doesn't set background-size, so scale
   the sheet back to its 1x size here: crisp on retina, correct frames.
   The attribute selector matches the inline background the library sets. */
.bug[style*="worm-sprite"] { background-size: 108px 96px !important; }


/* 6e. Slime dripping off the header -------------------------------------- */

/* Hangs from the header's bottom edge (covering the pumpkin rule) over the
   top of the subheader. Starts 5px up inside the header, so even the
   longest drip ends ~23px below the edge, above the subheader's text.
   On mobile, where .hw-sky makes no stacking context, the z-index lifts it
   over the content that follows the header. */
.hw-slime {
  position: absolute;
  top: calc(var(--hw-utility-h) + var(--hw-header-h) - 5px);
  left: 0; right: 0;
  height: 80px;                      /* room for the falling drops */
  overflow: hidden;
  pointer-events: none;
  z-index: 60;
}
.hw-slime__edge {
  display: block;
  height: 30px;
  /* 2560px wide and random, so no repeat is visible on any normal screen;
     repeat-x only matters on ultra-wide monitors. */
  background: url("assets/slime-edge.svg") left top / 2560px 30px repeat-x;
}

/* Oozing drips: each slowly stretches and shrinks at its own speed, from
   a gap left for it in the SVG (x = 180, 530, 905, 1370, 1880, 2290).
   Height animates (not scale) so the round bulb keeps its shape. They never
   reach past ~22px below the header. */
.hw-ooze {
  position: absolute;
  top: 6px;
  width: var(--w, 7px);
  height: 6px;
  margin-left: calc(var(--w, 7px) / -2);
  border-radius: 40% 40% 50% 50% / 20% 20% 45% 45%;
  background: linear-gradient(90deg, #5ca81f, #8bdc3e 40%, #5aa31c);
  animation: hw-ooze var(--d, 8s) ease-in-out var(--delay, 0s) infinite alternate;
}
@keyframes hw-ooze {
  from { height: 5px; }
  to   { height: var(--max, 15px); }
}
.hw-ooze--1 { left:  180px; --w: 8px; --max: 16px; --d:  7s;   --delay: -2s; }
.hw-ooze--2 { left:  530px; --w: 6px; --max: 12px; --d: 11s;   --delay: -6s; }
.hw-ooze--3 { left:  905px; --w: 9px; --max: 17px; --d:  9s;   --delay: -1s; }
.hw-ooze--4 { left: 1370px; --w: 7px; --max: 14px; --d:  6.5s; --delay: -4s; }
.hw-ooze--5 { left: 1880px; --w: 8px; --max: 16px; --d: 10s;   --delay: -8s; }
.hw-ooze--6 { left: 2290px; --w: 6px; --max: 11px; --d:  8.5s; --delay: -3s; }

/* A drop forms under an oozing drip, falls and fades. Uneven cycles so the
   three never sync up; the fall itself is the last ~15% of each. */
.hw-slime__drop {
  position: absolute;
  top: 19px;
  width: 6px; height: 8px;
  margin-left: -3px;
  border-radius: 50% 50% 50% 50% / 40% 40% 60% 60%;
  background: linear-gradient(#9be84a, #4f9a18);
  opacity: 0;
  animation: hw-drip var(--d, 9s) ease-in var(--delay, 0s) infinite;
}
.hw-slime__drop--1 { left:  180px; --d:  9s;  --delay: -1s; }
.hw-slime__drop--2 { left:  905px; --d: 13s;  --delay: -5s; }
.hw-slime__drop--3 { left: 1880px; --d: 11s;  --delay: -9s; }
@keyframes hw-drip {
  0%, 70% { transform: translateY(0) scale(0.3); opacity: 0; }
  80%     { transform: translateY(0) scale(1);   opacity: 1; }
  96%     { transform: translateY(56px) scale(1, 1.2); opacity: 0.9; }
  100%    { transform: translateY(60px) scale(1);  opacity: 0; }
}


/* 6f. Peeking ghost ------------------------------------------------------- */

/* Rises from the bottom-left corner of the window every ~24s, wobbles for a
   couple of seconds and sinks back. Left corner, because chat and "back to
   top" widgets usually live on the right. Lavender fill, outline and soft
   shadow so it reads on both white pages and the dark footer. Hidden
   entirely (not frozen mid-peek) when motion is off, see section 7. */
.hw-ghost {
  position: fixed;
  left: 24px; bottom: 0;
  width: 64px; height: 77px;
  z-index: 900;
  pointer-events: none;
  transform: translateY(105%);
  filter: drop-shadow(0 0 6px hsl(272 40% 20% / 0.35));
  animation: hw-peek 24s ease-in-out -14s infinite;
}
.hw-ghost svg { display: block; width: 100%; height: 100%; }
@keyframes hw-peek {
  0%, 70% { transform: translateY(105%) rotate(0); }
  76%     { transform: translateY(22%)  rotate(0); }
  80%     { transform: translateY(22%)  rotate(-7deg); }
  84%     { transform: translateY(22%)  rotate(7deg); }
  88%     { transform: translateY(22%)  rotate(0); }
  94%, 100% { transform: translateY(105%) rotate(0); }
}
@media (max-width: 600px) {
  .hw-ghost { left: 12px; width: 48px; height: 58px; }
}

/* 6g. Ghost that follows the mouse ----------------------------------------- */

/* Full-viewport canvas drawn by assets/halloween-ghost-cursor.js (desktop
   mice only). Never takes clicks; sits under the Bug.js bugs. */
.hw-ghost-cursor {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 9000;
}

/* 7. Motion gating, forced colors, print ---------------------------------- */

/* WCAG 2.2.2 / 2.3.3: decor animates ONLY while <html> has .hw-motion.
   The ribbon script adds it unless the visitor paused, dismissed the ribbon,
   or their OS asks for reduced motion. No JS → no motion. This selector
   outranks every animation rule above regardless of order. */
html:not(.hw-motion) .hw-anim,
html:not(.hw-motion) .fpi-hero__slide::before,
html:not(.hw-motion) .fpi-hero__slide::after { animation-play-state: paused; }

/* Things that would be left parked in view if merely paused: hide them. */
html:not(.hw-motion) .hw-ghost,
html:not(.hw-motion) .hw-slime__drop { display: none; }

/* Windows High Contrast / forced colors: drop the decor entirely. */
@media (forced-colors: active) {
  .hw-sky, .hw-soil, .hw-ribbon__icon, .hw-ghost, .hw-ghost-cursor { display: none; }
  .fpi-hero__slide::before, .fpi-hero__slide::after { content: none; }
}

@media print {
  .hw-ribbon, .hw-sky, .hw-soil, .hw-ghost, .hw-ghost-cursor { display: none !important; }
  .fpi-hero__slide::before, .fpi-hero__slide::after { content: none; }
}
