/* Clear Mint — Seasonal Experience Engine (SeasonalFallingEffects)
   A lightweight, CSS-animation-driven falling-particle layer.
   Auto-selects an experience from the current date/time using a priority system:
     1. User milestone   (via `milestone` prop)
     2. Major holiday    (Canadian + Ontario statutory)
     3. Special observance
     4. Seasonal theme    (winter / spring / summer / fall)
     5. Default Clear Mint leaves
   Override for testing with the `effect` prop, e.g. <SeasonalFallingEffects effect="christmas" />.
   Renders nothing under prefers-reduced-motion. No heavy libraries; GPU transforms only.

   NOTE: the spec's modular files (HolidayRules / SeasonRules / MilestoneRules /
   SpecialDayRules / TimeOfDayRules / EffectLibrary) are consolidated here as labelled
   sections so the whole engine runs in-browser via Babel with zero build step. */
(function () {
  "use strict";

  /* ============================================================
     STYLESHEET (keyframes + base) — injected once
     ============================================================ */
  function injectStyle() {
    if (document.getElementById("cm-sfx-style")) return;
    var s = document.createElement("style");
    s.id = "cm-sfx-style";
    s.textContent = [
      ".cm-sfx{position:absolute;inset:0;pointer-events:none;overflow:hidden;}",
      ".cm-sfx-p{position:absolute;top:0;display:block;will-change:transform,opacity;",
      "transform:translate3d(0,-15vh,0);",
      "animation-name:cmSfxFall,cmSfxFade;animation-timing-function:linear,ease-in-out;",
      "animation-iteration-count:infinite,infinite;animation-fill-mode:both,both;}",
      ".cm-sfx-p svg{display:block;width:100%;height:100%;overflow:visible;}",
      ".cm-sfx-glow{filter:drop-shadow(0 0 3px currentColor);}",
      "@keyframes cmSfxFall{",
      "0%{transform:translate3d(0,-15vh,0) rotate(var(--r0,0deg));}",
      "50%{transform:translate3d(calc(var(--dx,0px) * 0.6),50vh,0) rotate(calc((var(--r0,0deg) + var(--r1,360deg)) / 2));}",
      "100%{transform:translate3d(var(--dx,0px),116vh,0) rotate(var(--r1,360deg));}}",
      "@keyframes cmSfxFade{0%{opacity:0;}9%{opacity:var(--op,.4);}86%{opacity:var(--op,.4);}100%{opacity:0;}}",
      "@media (prefers-reduced-motion: reduce){.cm-sfx{display:none !important;}}"
    ].join("");
    document.head.appendChild(s);
  }

  /* ============================================================
     HELPERS
     ============================================================ */
  function rnd(a, b) { return a + Math.random() * (b - a); }
  function pick(a) { return a[Math.floor(Math.random() * a.length)]; }
  function ymd(d) { return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); }

  function nthWeekday(y, m, weekday, n) { // m 0-based, weekday 0=Sun
    var d = new Date(y, m, 1), count = 0;
    while (d.getMonth() === m) {
      if (d.getDay() === weekday) { count++; if (count === n) return new Date(d); }
      d.setDate(d.getDate() + 1);
    }
    return null;
  }
  function mondayOnOrBefore(y, m, day) { // nearest Monday <= the given date
    var d = new Date(y, m, day);
    while (d.getDay() !== 1) d.setDate(d.getDate() - 1);
    return d;
  }
  function easterSunday(y) { // Meeus/Jones/Butcher
    var a = y % 19, b = Math.floor(y / 100), c = y % 100,
        d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25),
        g = Math.floor((b - f + 1) / 3), h = (19 * a + b - d - g + 15) % 30,
        i = Math.floor(c / 4), k = c % 4, l = (32 + 2 * e + 2 * i - h - k) % 7,
        mth = Math.floor((a + 11 * h + 22 * l) / 451);
    return new Date(y, Math.floor((h + l - 7 * mth + 114) / 31) - 1, ((h + l - 7 * mth + 114) % 31) + 1);
  }

  /* ============================================================
     SEASON RULES (priority 4)
     ============================================================ */
  function getSeason(m) {
    if (m === 11 || m === 0 || m === 1) return "winter";
    if (m >= 2 && m <= 4) return "spring";
    if (m >= 5 && m <= 7) return "summer";
    return "fall";
  }

  /* ============================================================
     HOLIDAY + OBSERVANCE RULES (priority 2–3)
     ============================================================ */
  function getSeasonalEffect(now) {
    now = now || new Date();
    var y = now.getFullYear(), m = now.getMonth(), d = now.getDate(), t = ymd(now);
    var es = easterSunday(y), gf = new Date(es); gf.setDate(es.getDate() - 2);
    var em = new Date(es); em.setDate(es.getDate() + 1);

    /* ---- Major holidays ---- */
    if ((m === 11 && d === 31) || (m === 0 && d === 1)) return "newyear";        // New Year's Eve / Day
    if (m === 11 && d === 26) return "boxingday";                                // Boxing Day
    if (m === 11 && d >= 1 && d <= 25) return "christmas";                       // Christmas season
    if (m === 1 && d === 14) return "valentine";                                 // Valentine's Day
    var fam = nthWeekday(y, 1, 1, 3); if (fam && m === 1 && d === fam.getDate()) return "familyday"; // Family Day
    if (t === ymd(gf)) return "goodfriday";                                      // Good Friday
    if (t === ymd(es) || t === ymd(em)) return "easter";                         // Easter Sun/Mon
    var vic = mondayOnOrBefore(y, 4, 24); if (m === 4 && d === vic.getDate()) return "victoriaday"; // Victoria Day
    if (m === 6 && d === 1) return "canada";                                     // Canada Day
    var civic = nthWeekday(y, 7, 1, 1); if (civic && m === 7 && d === civic.getDate()) return "civic"; // Civic Holiday
    var lab = nthWeekday(y, 8, 1, 1); if (lab && m === 8 && d === lab.getDate()) return "labour";      // Labour Day
    if (m === 8 && d === 30) return "truthreconciliation";                       // Truth & Reconciliation
    var th = nthWeekday(y, 9, 1, 2); if (th && m === 9 && d === th.getDate()) return "thanksgiving";   // Thanksgiving
    if (m === 9 && d === 31) return "halloween";                                 // Halloween
    if (m === 10 && d === 11) return "remembrance";                              // Remembrance Day

    /* ---- Special observances ---- */
    if (m === 3 && d === 22) return "earthday";                                  // Earth Day
    var mom = nthWeekday(y, 4, 0, 2); if (mom && m === 4 && d === mom.getDate()) return "mothersday";  // Mother's Day
    var dad = nthWeekday(y, 5, 0, 3); if (dad && m === 5 && d === dad.getDate()) return "fathersday";  // Father's Day

    /* ---- Seasonal theme fallback ---- */
    return getSeason(m);
  }

  /* ============================================================
     TIME OF DAY (priority modifier — calmer at night)
     ============================================================ */
  function getTimeOfDay(now) {
    var h = (now || new Date()).getHours();
    if (h >= 5 && h < 11) return "morning";
    if (h >= 11 && h < 17) return "afternoon";
    if (h >= 17 && h < 21) return "evening";
    return "night";
  }

  /* ============================================================
     EFFECT LIBRARY
     types: glyph names · colors: palette · size/op/dur ranges · count · drift(px)
     ============================================================ */
  var GOLD = ["#D4AF37", "#C8A23A", "#B8942A"];
  var EFFECTS = {
    // default + seasons
    leaves:       { types: ["leaf"], colors: ["#D4AF37", "#C8A23A", "#B8942A", "#2E7D5B", "#3F6B4E"], size: [12, 27], op: [.16, .40], dur: [11, 22], count: 22, drift: 55 },
    winter:       { types: ["snow", "snow", "sparkle"], colors: ["#FFFFFF", "#EAF1F4", "#F4FAFF"], size: [6, 15], op: [.32, .68], dur: [10, 19], count: 24, drift: 32 },
    spring:       { types: ["petal", "butterfly", "leaf"], colors: ["#F4C6D6", "#CFE8C8", "#F6E3A1", "#2E7D5B", "#9ED1AE"], size: [11, 22], op: [.24, .50], dur: [10, 19], count: 20, drift: 56 },
    summer:       { types: ["sparkle", "snow", "leaf"], colors: ["#E7C763", "#F2D88A", "#FBFAF4", "#2E7D5B"], size: [9, 20], op: [.20, .44], dur: [10, 20], count: 20, drift: 50, glow: true },
    fall:         { types: ["leaf", "maple"], colors: ["#C8742E", "#B8942A", "#A23E2A", "#8A5A22", "#D4A95D"], size: [12, 27], op: [.22, .48], dur: [11, 21], count: 22, drift: 58 },
    // major holidays
    newyear:      { types: ["confetti", "sparkle"], colors: ["#D4AF37", "#E7C763", "#F2D88A", "#FBFAF4", "#2E7D5B"], size: [7, 13], op: [.45, .80], dur: [7, 15], count: 28, drift: 70 },
    christmas:    { types: ["snow"], colors: ["#FFFFFF", "#EAF1F4", "#F4FAFF"], size: [6, 15], op: [.40, .80], dur: [9, 18], count: 26, drift: 34 },
    boxingday:    { types: ["sparkle", "snow"], colors: ["#D4AF37", "#E7C763", "#FFFFFF", "#F4FAFF"], size: [7, 15], op: [.38, .70], dur: [9, 17], count: 24, drift: 38, glow: true },
    valentine:    { types: ["heart", "petal"], colors: ["#D98BA0", "#C75D77", "#D4AF37", "#E7C0CC"], size: [12, 22], op: [.28, .55], dur: [10, 19], count: 20, drift: 48 },
    familyday:    { types: ["heartleaf"], colors: ["#D4A95D", "#C97B5A", "#B8703F", "#CE8A9A", "#2E7D5B"], size: [13, 24], op: [.24, .50], dur: [11, 20], count: 20, drift: 46 },
    goodfriday:   { types: ["petal", "sparkle"], colors: ["#F4F1EA", "#FBFAF4", "#E7D2A6", "#D4AF37"], size: [10, 20], op: [.26, .50], dur: [11, 20], count: 18, drift: 44 },
    easter:       { types: ["petal", "butterfly"], colors: ["#F4C6D6", "#CFE8C8", "#F6E3A1", "#C9D9F0", "#E7D2A6"], size: [10, 21], op: [.34, .62], dur: [10, 19], count: 22, drift: 58 },
    victoriaday:  { types: ["sparkle", "leaf"], colors: ["#D4AF37", "#E7C763", "#2E7D5B", "#3F6B4E"], size: [10, 22], op: [.28, .54], dur: [10, 19], count: 20, drift: 48, glow: true },
    canada:       { types: ["maple", "maple", "sparkle"], colors: ["#D34B4B", "#F4F1EA"], sparkleColors: ["#F4F1EA", "#F2D88A"], size: [12, 23], op: [.30, .60], dur: [9, 17], count: 22, drift: 46 },
    civic:        { types: ["leaf", "sparkle"], colors: ["#2E7D5B", "#3F6B4E", "#D4AF37", "#5FA37A"], size: [11, 24], op: [.22, .46], dur: [11, 20], count: 20, drift: 52 },
    labour:       { types: ["leaf", "confetti"], colors: ["#D4AF37", "#C8A23A", "#F2D88A", "#2E7D5B"], size: [11, 23], op: [.26, .50], dur: [10, 19], count: 20, drift: 54 },
    truthreconciliation: { types: ["leaf", "snow"], colors: ["#E07B39", "#C8581F", "#D4772E", "#B45A22"], size: [11, 24], op: [.26, .50], dur: [11, 20], count: 20, drift: 48 },
    thanksgiving: { types: ["leaf", "maple"], colors: ["#C8742E", "#B8942A", "#A23E2A", "#8A5A22", "#D4A95D"], size: [12, 27], op: [.22, .48], dur: [11, 21], count: 22, drift: 58 },
    halloween:    { types: ["bat", "bat", "leaf"], colors: ["#2A2233", "#1E2A24", "#352A40"], leafColors: ["#D4772E", "#C8581F", "#A8531C"], size: [12, 22], op: [.30, .55], dur: [8, 16], count: 20, drift: 52 },
    remembrance:  { types: ["poppy"], colors: ["#C32A2A", "#A81E1E", "#8E1818"], size: [12, 22], op: [.30, .56], dur: [11, 20], count: 18, drift: 40 },
    // special observances
    earthday:     { types: ["leaf", "sparkle"], colors: ["#2E7D5B", "#3F6B4E", "#5FA37A", "#D4AF37"], size: [12, 25], op: [.24, .48], dur: [11, 20], count: 22, drift: 54 },
    mothersday:   { types: ["petal", "heart"], colors: ["#E7B7C6", "#D98BA0", "#D4AF37", "#F4C6D6"], size: [11, 22], op: [.28, .54], dur: [10, 19], count: 20, drift: 50 },
    fathersday:   { types: ["leaf"], colors: ["#D4AF37", "#B8942A", "#2E7D5B", "#3F6B4E"], size: [12, 26], op: [.20, .44], dur: [11, 21], count: 20, drift: 52 },
    // milestone (priority 1) — premium celebration
    milestone:    { types: ["confetti", "sparkle", "leaf"], colors: ["#D4AF37", "#E7C763", "#F2D88A", "#FBFAF4", "#2E7D5B"], size: [8, 18], op: [.40, .78], dur: [7, 15], count: 30, drift: 72, glow: true }
  };

  /* ============================================================
     GLYPHS — small, simple SVGs
     ============================================================ */
  function Glyph(props) {
    var c = props.color, t = props.type, R = React.createElement;
    switch (t) {
      case "leaf":
        return R("svg", { viewBox: "0 0 64 76" },
          R("path", { d: "M34 4C50 18 56 44 38 72c-2-18-4-34 -4-68z", fill: c }),
          R("path", { d: "M30 12C12 28 10 52 30 72c0-18-2-40 0-60z", fill: c, opacity: 0.8 }));
      case "snow":
        return R("svg", { viewBox: "0 0 24 24" }, R("circle", { cx: 12, cy: 12, r: 6, fill: c }));
      case "confetti":
        return R("svg", { viewBox: "0 0 14 20" }, R("rect", { width: 14, height: 20, rx: 3, fill: c }));
      case "heart":
        return R("svg", { viewBox: "0 0 24 24" },
          R("path", { d: "M12 21S2.3 15.6.4 9.9C-.6 6.7 1.4 4 4.6 4 6.7 4 8 5.3 8.9 6.6 9.8 5.3 11.1 4 13.2 4c3.2 0 5.2 2.7 4.2 5.9C15.6 15.6 12 21 12 21z", fill: c }));
      case "heartleaf":
        return R("svg", { viewBox: "0 0 24 27" },
          R("path", { d: "M12 24S2.3 18.6.4 12.9C-.6 9.7 1.4 7 4.6 7 6.7 7 8 8.3 8.9 9.6 9.8 8.3 11.1 7 13.2 7c3.2 0 5.2 2.7 4.2 5.9C15.6 18.6 12 24 12 24z", fill: c }),
          R("path", { d: "M12 25V11.5", stroke: c, strokeWidth: 1.3, strokeLinecap: "round", opacity: 0.65 }));
      case "petal":
        return R("svg", { viewBox: "0 0 24 24" }, R("ellipse", { cx: 12, cy: 12, rx: 6, ry: 11, fill: c }));
      case "butterfly":
        return R("svg", { viewBox: "0 0 28 24" },
          R("g", { fill: c },
            R("path", { d: "M14 12C12 6 7 3 3 5c-2 1-2 5 0 7-2 1-2 5 1 6 4 1 8-2 10-6z" }),
            R("path", { d: "M14 12c2-6 7-9 11-7 2 1 2 5 0 7 2 1 2 5-1 6-4 1-8-2-10-6z" })),
          R("path", { d: "M14 6v12", stroke: "#3A2E18", strokeWidth: 1.2, strokeLinecap: "round", opacity: 0.7 }));
      case "maple":
        return R("svg", { viewBox: "0 0 24 24" },
          R("path", { d: "M12 1l1.7 3.7 3.2-1.3-1 3.3 3.5.2-2.3 2.4 4 1.6-3.8 1 1.3 3.3-3.4-1.1-.1 3.8-2.5-2.5-2.5 2.5-.1-3.8-3.4 1.1 1.3-3.3L1.7 11l4-1.6-2.3-2.4 3.5-.2-1-3.3 3.2 1.3L12 1z", fill: c }));
      case "sparkle":
        return R("svg", { viewBox: "0 0 24 24" },
          R("path", { d: "M12 2l1.6 7.1L21 12l-7.4 2.9L12 22l-1.6-7.1L3 12l7.4-2.9L12 2z", fill: c }));
      case "bat":
        return R("svg", { viewBox: "0 0 24 14" },
          R("path", { d: "M12 3c.8 1.1 1.5 1.7 2.6 1.7.7 0 1.1-.4 1.4-1 .2 1 .8 1.6 1.7 1.6.6 0 1-.2 1.5-.6-.2 1-.1 1.8.6 2.6-1.3-.2-2.3.2-3 1.1-.5-1-1.3-1.4-2.3-1.4-1.2 0-2 .7-2.5 1.6-.5-.9-1.3-1.6-2.5-1.6-1 0-1.8.5-2.3 1.4-.7-.9-1.7-1.3-3-1.1.7-.8.8-1.6.6-2.6.5.4.9.6 1.5.6.9 0 1.5-.6 1.7-1.6.3.6.7 1 1.4 1C10.5 4.7 11.2 4.1 12 3z", fill: c }));
      case "poppy":
        return R("svg", { viewBox: "0 0 24 24" },
          R("g", { fill: c },
            R("ellipse", { cx: 12, cy: 8, rx: 4.5, ry: 5.5 }),
            R("ellipse", { cx: 8, cy: 14, rx: 5.5, ry: 4.5 }),
            R("ellipse", { cx: 16, cy: 14, rx: 5.5, ry: 4.5 }),
            R("ellipse", { cx: 12, cy: 16, rx: 4.5, ry: 5.5 })),
          R("circle", { cx: 12, cy: 12, r: 2.1, fill: "#1E1212" }));
      default:
        return null;
    }
  }

  /* ============================================================
     COMPONENT
     props: effect?, milestone?, density?, now?(Date for testing)
     ============================================================ */
  function SeasonalFallingEffects(props) {
    props = props || {};
    var prefersReduced = typeof window !== "undefined" && window.matchMedia &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    React.useEffect(function () { injectStyle(); }, []);

    var now = props.now || new Date();
    // priority: milestone > explicit effect > date-driven
    var eff = props.milestone ? "milestone" : (props.effect || getSeasonalEffect(now));
    var cfg = EFFECTS[eff] || EFFECTS.leaves;

    var tod = getTimeOfDay(now);
    var nightCalm = tod === "night";

    var isMobile = typeof window !== "undefined" && window.innerWidth < 880;
    var density = (props.density || 1) * (nightCalm ? 0.7 : 1) * (isMobile ? 0.5 : 1);
    var count = Math.max(6, Math.round(cfg.count * density));
    var durScale = nightCalm ? 1.3 : 1;     // slower at night
    var opScale = nightCalm ? 0.85 : 1;

    var particles = React.useMemo(function () {
      var arr = [];
      for (var i = 0; i < count; i++) {
        var type = pick(cfg.types);
        var color;
        if (eff === "canada" && type === "sparkle") color = pick(cfg.sparkleColors);
        else if (eff === "halloween" && type === "leaf") color = pick(cfg.leafColors);
        else color = pick(cfg.colors);
        var size = rnd(cfg.size[0], cfg.size[1]);
        arr.push({
          id: i, type: type, color: color, size: size,
          left: rnd(-4, 100),
          op: rnd(cfg.op[0], cfg.op[1]) * opScale,
          dur: rnd(cfg.dur[0], cfg.dur[1]) * durScale,
          delay: -rnd(0, cfg.dur[1] * durScale),
          dx: rnd(-cfg.drift, cfg.drift),
          r0: rnd(0, 360),
          r1: rnd(0, 360) + (Math.random() < 0.5 ? -1 : 1) * rnd(120, 420),
          glow: !!cfg.glow && (type === "sparkle" || type === "snow")
        });
      }
      return arr;
    }, [eff, count, durScale, opScale]);

    if (prefersReduced) return null;

    return React.createElement(
      "div", { className: "cm-sfx", "aria-hidden": "true", "data-effect": eff, "data-tod": tod },
      particles.map(function (p) {
        return React.createElement("span", {
          key: p.id,
          className: "cm-sfx-p" + (p.glow ? " cm-sfx-glow" : ""),
          style: {
            left: p.left + "%",
            width: p.size + "px",
            height: p.size + "px",
            color: p.color,
            animationDuration: p.dur + "s, " + p.dur + "s",
            animationDelay: p.delay + "s, " + p.delay + "s",
            "--dx": p.dx + "px",
            "--r0": p.r0 + "deg",
            "--r1": p.r1 + "deg",
            "--op": p.op
          }
        }, React.createElement(Glyph, { type: p.type, color: p.color }));
      })
    );
  }

  window.SeasonalFallingEffects = SeasonalFallingEffects;
  window.getSeasonalEffect = getSeasonalEffect;
  window.getCmSeason = getSeason;
})();
