/* global React, ReactDOM, TweaksPanel, useTweaks, TweakSection, TweakRadio, TweakColor */
const { useRef, useEffect, useMemo, useState } = React;

// --- Data --------------------------------------------------------------------

const ROLES = [
{ title: "Creative Director", url: "https://forms.fillout.com/t/6EmWVwzNf7us" },
{ title: "Editor", url: "https://forms.fillout.com/t/tAK6mUPqEmus" },
{ title: "Comedy Writer", url: "https://forms.fillout.com/t/51WDLRKiSrus" },
{ title: "Producer", url: "https://forms.fillout.com/t/ceyPxYsdAQus" },
{ title: "Videographer", url: "https://forms.fillout.com/t/2pBkNy8s8ius" }];


const PHOTO_POOL = [
"assets/photo2.jpg", "assets/photo3.jpg",
"assets/photo4.jpg", "assets/photo6.jpg",
"assets/photo7.jpg", "assets/photo8.jpg", "assets/photo9.jpg",
"assets/photo10.jpg", "assets/photo11.jpg", "assets/photo12.jpg",
"assets/photo13.jpg", "assets/photo14.jpg",
"assets/photo16.jpg", "assets/photo17.jpg", "assets/photo18.jpg",
"assets/photo19.jpg", "assets/photo20.jpg", "assets/photo21.jpg",
"assets/photo22.jpg", "assets/photo23.jpg", "assets/photo24.jpg",
"assets/photo25.jpg", "assets/photo26.jpg", "assets/photo27.jpg",
"assets/photo28.jpg", "assets/photo29.jpg", "assets/photo30.jpg",
"assets/photo31.jpg", "assets/photo32.jpg", "assets/photo33.jpg",
"assets/photo34.jpg", "assets/photo35.jpg", "assets/photo36.jpg",
"assets/photo37.jpg", "assets/photo38.jpg", "assets/photo39.jpg",
"assets/photo40.jpg", "assets/photo41.jpg", "assets/photo42.jpg",
"assets/photo43.jpg", "assets/photo44.jpg", "assets/photo45.jpg",
"assets/photo46.jpg", "assets/photo47.jpg", "assets/photo48.jpg",
"assets/photo49.jpg", "assets/photo50.jpg", "assets/photo51.jpg",
"assets/photo52.jpg", "assets/photo54.jpg",
"assets/photo55.jpg"];


// Photos per row in the hero band (then doubled for seamless loop).
const BAND_LEN = 22;

// --- Photo pool validation -------------------------------------------------
// Probes every URL in PHOTO_POOL with an Image() and keeps only the ones
// that actually load. Anything missing/broken is silently dropped so it
// never renders as a gray placeholder. Returns null until verification is
// done so callers can render an empty backdrop instead of broken cards.
function useVerifiedPhotos() {
  const [verified, setVerified] = React.useState(null);
  React.useEffect(() => {
    let cancelled = false;
    const results = PHOTO_POOL.map((src) =>
    new Promise((resolve) => {
      const img = new Image();
      img.onload = () => resolve(src);
      img.onerror = () => resolve(null);
      img.src = src;
    })
    );
    Promise.all(results).then((arr) => {
      if (cancelled) return;
      setVerified(arr.filter(Boolean));
    });
    return () => { cancelled = true; };
  }, []);
  return verified;
}

// Shuffle that avoids back-to-back duplicates. When the pool is at least
// as big as `total`, we sample WITHOUT repeats (Fisher–Yates on a copy of
// the pool, then slice); otherwise we cycle pool[i % len] like before so
// shorter pools still fill the requested length.
function shuffleNoAdjacent(pool, total) {
  for (let attempt = 0; attempt < 20; attempt++) {
    let arr;
    if (pool.length >= total) {
      const shuffled = pool.slice();
      for (let i = shuffled.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
      }
      arr = shuffled.slice(0, total);
    } else {
      arr = [];
      for (let i = 0; i < total; i++) arr.push(pool[i % pool.length]);
      for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
      }
    }
    let ok = true;
    for (let i = 1; i < arr.length; i++) {
      if (arr[i] !== arr[i - 1]) continue;
      let swapped = false;
      for (let k = i + 1; k < arr.length; k++) {
        const prev = arr[i - 1],next = arr[i + 1];
        const kPrev = arr[k - 1],kNext = arr[k + 1];
        if (arr[k] !== prev && arr[k] !== next &&
        arr[i] !== kPrev && arr[i] !== kNext) {
          [arr[i], arr[k]] = [arr[k], arr[i]];
          swapped = true;
          break;
        }
      }
      if (!swapped) {ok = false;break;}
    }
    if (ok) return arr;
  }
  const fallback = [];
  for (let i = 0; i < total; i++) fallback.push(pool[i % pool.length]);
  return fallback;
}

// --- Polaroid (standalone, used in offset photos + footer) ------------------

function Polaroid({ src, caption, rotate = 0, w = 200, className = "", style = {}, ...rest }) {
  return (
    <figure
      className={"polaroid " + className}
      style={{ "--rot": rotate + "deg", "--w": w + "px", ...style }}
      {...rest}>
      <div className="polaroid-img" style={{ backgroundImage: `url(${src})` }} data-comment-anchor="617cfff616-div-67-7" />
      {caption && <figcaption className="polaroid-cap">{caption}</figcaption>}
    </figure>);

}

// --- Photo Band (full-bleed horizontal scrollers) ---------------------------

function BandRow({ photos, variant }) {
  // Double so the keyframe -50% translate loops seamlessly.
  const doubled = [...photos, ...photos];
  return (
    <div className={`photo-row photo-row--${variant}`}>
      {doubled.map((src, i) => {
        // Pseudo-random tilt in -3..+3 deg, deterministic per index.
        const r = i * 41 % 7 - 3;
        return (
          <div className="band-card" key={i} style={{ "--r": r + "deg" }}>
            <div className="band-img" style={{ backgroundImage: `url(${src})` }} />
          </div>);

      })}
    </div>);

}

// Bottom band — sits directly below the hero, the single layer of
// polaroids scrolling by before the page breaks into the next sections.
function PhotoBandBottom() {
  const row = useMemo(() => shuffleNoAdjacent(PHOTO_POOL, BAND_LEN), []);
  return (
    <div
      className="photo-band photo-band--edge photo-band--bottom"
      aria-hidden="true"
      data-comment-anchor="bb294274f3-div-78-5" style={{ height: "340px", padding: "30px 0px 52px" }}>
      <BandRow photos={row} variant="b" />
    </div>);

}

// --- Photo Table (top-down pile of polaroids dropping like snow) ----------
// Looking straight down at a table so covered in face-up polaroids the
// table is NEVER visible. On load a full mosaic drops in; then every ~1.2s
// one new UNIQUE polaroid is dropped onto a systematically-chosen cell,
// covering whatever was there. The covered (now-invisible) tile underneath
// is removed a beat later — so the pile constantly refreshes, never grows
// taller, never shows blank space, and never shows a duplicate.

// Columns by width; ROWS derived from the viewport aspect so the (portrait)
// tiles tile evenly instead of stacking deep vertically — this is what keeps
// mobile from burying photos under each other.
function gridDims() {
  const w = window.innerWidth, h = window.innerHeight || 800;
  const cols = w < 560 ? 3 : w < 900 ? 4 : w < 1300 ? 6 : 7;
  let rows = Math.round(0.78 * cols * (h / w));
  rows = Math.max(3, Math.min(8, rows));
  return { cols, rows };
}

// Position + tilt for a tile centered in its grid cell (small jitter only,
// so neighbours always overlap and no gap can open up).
function tilePose(cellId, cols, rows) {
  const c = cellId % cols, r = Math.floor(cellId / cols);
  const cellW = 100 / cols, cellH = 100 / rows;
  const x = (c + 0.5) * cellW + (Math.random() - 0.5) * cellW * 0.1;
  const y = (r + 0.5) * cellH + (Math.random() - 0.5) * cellH * 0.1;
  const rot = (Math.random() * 2 - 1) * 9;
  return { x, y, rot };
}

// Pose for a refreshed cell: near the cell centre (so it always covers the
// cell — no gaps) but with a clearly different tilt + a small shift from the
// photo it replaces, so it never reads as the exact same placement.
function tilePoseFresh(cellId, cols, rows, prev) {
  const c = cellId % cols, r = Math.floor(cellId / cols);
  const cellW = 100 / cols, cellH = 100 / rows;
  const baseX = (c + 0.5) * cellW, baseY = (r + 0.5) * cellH;
  let rot = (Math.random() * 2 - 1) * 9;
  if (prev) {
    let tries = 0;
    while (Math.abs(rot - prev.rot) < 8 && tries++ < 10) rot = (Math.random() * 2 - 1) * 9;
    if (Math.abs(rot - prev.rot) < 8) rot = prev.rot + (prev.rot >= 0 ? -10 : 10);
  }
  const x = baseX + (Math.random() - 0.5) * cellW * 0.14;
  const y = baseY + (Math.random() - 0.5) * cellH * 0.14;
  return { x, y, rot };
}

// Measure the hero headline (and logo) so new polaroids can avoid landing
// on top of the big white text. Returns a normalized rect or null.
function measureAvoid() {
  const el = document.querySelector(".hero-title");
  if (!el) return null;
  const r = el.getBoundingClientRect();
  const W = window.innerWidth, H = window.innerHeight;
  if (!r.width || !r.height) return null;
  // Pad a touch so drops keep clear of the glyph edges too.
  const padX = W * 0.035, padY = H * 0.02;
  return {
    x0: (r.left - padX) / W, x1: (r.right + padX) / W,
    y0: (r.top - padY) / H, y1: (r.bottom + padY) / H };

}

// Which cells sit under the hero headline — these are kept out of the
// refresh rotation (their initial photo just stays quietly behind the text).
function computeAvoidSet(cols, rows, avoid) {
  const s = new Set();
  if (!avoid) return s;
  for (let id = 0; id < cols * rows; id++) {
    const nx = (id % cols + 0.5) / cols;
    const ny = (Math.floor(id / cols) + 0.5) / rows;
    if (nx >= avoid.x0 && nx <= avoid.x1 && ny >= avoid.y0 && ny <= avoid.y1) s.add(id);
  }
  return s;
}

// One self-contained tile. It stays INVISIBLE until its image has actually
// loaded, then runs the drop-in animation — so a half-loaded photo can never
// flash its (black) box. `leaving` fades it out gracefully on replacement.
function Tile({ t }) {
  const [loaded, setLoaded] = useState(false);
  const onReady = () => setLoaded(true);
  return (
    <div
      className={"ptile" + (t.leaving ? " leaving" : "")}
      style={{ left: t.x + "%", top: t.y + "%", zIndex: t.z, "--rot": t.rot + "deg" }}>
      <div
        className={"ptile-inner" + (loaded ? " is-loaded" : "")}
        style={{ animationDelay: t.delay + "ms" }}>
        <div className="ptile-img">
          <img
            className="ptile-photo"
            src={t.photo}
            alt=""
            draggable="false"
            onLoad={onReady}
            onError={onReady} />
        </div>
      </div>
    </div>);

}

function PhotoTable() {
  const verifiedPool = useVerifiedPhotos();
  const [tiles, setTiles] = useState([]);
  const [inView, setInView] = useState(true);
  const tableRef = useRef(null);
  const keyRef = useRef(1);
  const zRef = useRef(1);
  const dimsRef = useRef(gridDims());
  const avoidRef = useRef(new Set());
  const timersRef = useRef([]);

  // Initial full mosaic (and rebuild on resize).
  useEffect(() => {
    if (!verifiedPool || verifiedPool.length === 0) return;
    const build = () => {
      const { cols, rows } = gridDims();
      dimsRef.current = { cols, rows };
      const n = cols * rows;
      avoidRef.current = computeAvoidSet(cols, rows, measureAvoid());

      // shuffled photo deck (unique per cell) for the starting mosaic
      const deck = verifiedPool.slice();
      for (let i = deck.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [deck[i], deck[j]] = [deck[j], deck[i]];
      }

      // Reveal order = organized chaos. Start with a fully-visible edge photo,
      // then WEAVE the centre (under-text) cells in gradually among the outer
      // ones — so the centre never floods first and no obvious pattern shows.
      const avoid = avoidRef.current;
      const outer = [], center = [];
      for (let cid = 0; cid < n; cid++) (avoid.has(cid) ? center : outer).push(cid);
      const shuf = (a) => {
        for (let i = a.length - 1; i > 0; i--) {
          const j = Math.floor(Math.random() * (i + 1));
          [a[i], a[j]] = [a[j], a[i]];
        }
      };
      shuf(outer); shuf(center);
      // First photo: a left/right edge cell so it's clearly visible, not buried.
      const edgeIdx = outer.findIndex((cid) => { const c = cid % cols; return c === 0 || c === cols - 1; });
      if (edgeIdx > 0) { const [e] = outer.splice(edgeIdx, 1); outer.unshift(e); }
      // Weave centre cells into the outer run, evenly spaced, never at the start.
      const orderArr = [];
      const gap = center.length ? outer.length / (center.length + 1) : Infinity;
      let ci = 0, nextAt = Math.max(2, Math.round(gap));
      for (let i = 0; i < outer.length; i++) {
        orderArr.push(outer[i]);
        if (ci < center.length && i + 1 >= nextAt) { orderArr.push(center[ci++]); nextAt += gap; }
      }
      while (ci < center.length) orderArr.push(center[ci++]);
      const rank = {};
      orderArr.forEach((cid, k) => { rank[cid] = k; });
      const step = Math.max(28, Math.min(60, Math.round(1500 / n)));

      const next = [];
      for (let cellId = 0; cellId < n; cellId++) {
        next.push({
          key: keyRef.current++,
          cell: cellId,
          photo: deck[cellId % deck.length],
          // z follows spiral order so each photo lands on TOP of the prior.
          z: rank[cellId] + 1,
          delay: rank[cellId] * step,
          ...tilePose(cellId, cols, rows) });

      }
      zRef.current = n + 1;
      setTiles(next);
    };
    build();
    let to;
    // Only rebuild when the WIDTH changes. Mobile browsers fire `resize`
    // on every scroll as the URL bar shows/hides (height changes), which
    // was re-dumping the whole pile mid-scroll. Ignoring height keeps the
    // initial reveal a one-time event.
    let lastW = window.innerWidth;
    const onResize = () => {
      if (window.innerWidth === lastW) return;
      lastW = window.innerWidth;
      clearTimeout(to);
      to = setTimeout(build, 250);
    };
    window.addEventListener("resize", onResize);
    return () => {
      window.removeEventListener("resize", onResize);
      clearTimeout(to);
      timersRef.current.forEach(clearTimeout);
      timersRef.current = [];
    };
  }, [verifiedPool]);

  // Pause the whole pile (the 2.5s drop cadence + its timers/animations)
  // whenever page 1 is scrolled out of view, so it costs nothing while the
  // user reads the rest of the page. Resumes when it scrolls back in.
  useEffect(() => {
    const el = tableRef.current;
    if (!el || typeof IntersectionObserver === "undefined") return;
    const obs = new IntersectionObserver(
      ([entry]) => setInView(entry.isIntersecting),
      { rootMargin: "120px" });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  // Every 2.5s refresh one cell: a fresh photo drops in (covering the cell)
  // and the cell's existing photo CROSS-FADES out. One stable photo per cell
  // means neighbours overlap only lightly (never a stable >60% double), and
  // a fade — not a pop — hides the outgoing one.
  const ready = !!verifiedPool && tiles.length > 0;
  useEffect(() => {
    if (!ready || !inView) return;
    const id = setInterval(() => {
      const { cols, rows } = dimsRef.current;
      const n = cols * rows;

      setTiles((prev) => {
        // Current (non-leaving) top photo + its z per cell. z = recency, so
        // a low z means that spot has been sitting untouched the longest.
        const topByCell = {};
        for (const t of prev) {
          if (t.leaving) continue;
          if (!topByCell[t.cell] || t.z > topByCell[t.cell].z) topByCell[t.cell] = t;
        }

        // Candidates = every cell except the ones under the headline. Sort by
        // staleness (oldest visible photo first) and pick from the stalest
        // ~30% — so we always refresh a long-untouched spot, never re-cover a
        // freshly-placed one, and cycle through the whole pile before
        // repeating. The random pick within the stale pool keeps it organic.
        let cand = [];
        for (let cid = 0; cid < n; cid++) if (!avoidRef.current.has(cid)) cand.push(cid);
        if (!cand.length) cand = Array.from({ length: n }, (_, i) => i);
        cand.sort((a, b) =>
        (topByCell[a] ? topByCell[a].z : -1) - (topByCell[b] ? topByCell[b].z : -1));
        const poolSize = Math.min(cand.length, Math.max(3, Math.round(cand.length * 0.3)));
        const cellId = cand[Math.floor(Math.random() * poolSize)];

        const visible = new Set(Object.values(topByCell).map((t) => t.photo));
        const avail = verifiedPool.filter((p) => !visible.has(p));
        const photo = (avail.length ? avail : verifiedPool)[
        Math.floor(Math.random() * (avail.length ? avail.length : verifiedPool.length))];

        const tile = {
          key: keyRef.current++,
          cell: cellId,
          photo,
          z: zRef.current++,
          delay: 0,
          ...tilePoseFresh(cellId, cols, rows, topByCell[cellId]) };

        // Cross-fade out the photo that was in this (stalest) cell.
        const leavingKeys = prev.filter((t) => t.cell === cellId && !t.leaving).map((t) => t.key);
        const arr = prev.map((t) => leavingKeys.includes(t.key) ? { ...t, leaving: true } : t);
        arr.push(tile);
        if (leavingKeys.length) {
          const tm = setTimeout(() => {
            setTiles((cur) => cur.filter((t) => !leavingKeys.includes(t.key)));
            timersRef.current = timersRef.current.filter((x) => x !== tm);
          }, 700);
          timersRef.current.push(tm);
        }
        return arr;
      });
    }, 2500);
    return () => clearInterval(id);
  }, [ready, inView]);

  const factor = (100 / dimsRef.current.cols * 1.3).toFixed(2);
  return (
    <div className="photo-table" aria-hidden="true" ref={tableRef}
    style={{ "--tile-w": `clamp(150px, ${factor}vw, 320px)` }}>
      {tiles.map((t) => <Tile key={t.key} t={t} />)}
    </div>);

}

// --- Sections ---------------------------------------------------------------

function Hero({ scrollToRoles }) {
  return (
    <section className="hero" id="top" data-comment-anchor="528b8c967e-section-120-5">
      <PhotoTable />
      <div className="hero-tint" aria-hidden="true"></div>
      <span role="img" aria-label="Family Friendly" className="hero-logo" data-comment-anchor="0804d09d1a-span-108-7" style={{ fontSize: "6px", height: "135px" }}></span>
      <h1 className="hero-title">
        Join the internet's<br />#1 comedy brand
      </h1>
      <div className="hero-cta">
        <button className="btn btn-primary" onClick={scrollToRoles}>
          <span className="btn-label">Apply Now</span>
        </button>
      </div>
    </section>);

}

function WhoAreWe() {
  return (
    <section className="col section-block">
      <h2 className="who-title">Who are we looking for?</h2>
      <span className="who-rule" aria-hidden="true"></span>
    </section>);

}

// --- CountUp (animated number that ticks from 0 → value on scroll in) ----

function CountUp({ value, duration = 2200, format }) {
  const [count, setCount] = useState(0);
  const [started, setStarted] = useState(false);
  const ref = useRef(null);

  // Kick off the animation the first time the element scrolls into view.
  useEffect(() => {
    const el = ref.current;
    if (!el || started) return;
    const check = () => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight * 0.9 && r.bottom > 0) {
        setStarted(true);
      }
    };
    check();
    window.addEventListener("scroll", check, { passive: true });
    window.addEventListener("resize", check, { passive: true });
    return () => {
      window.removeEventListener("scroll", check);
      window.removeEventListener("resize", check);
    };
  }, [started]);

  // Tween from 0 → value over `duration` ms, ease-out cubic.
  useEffect(() => {
    if (!started) return;
    const startT = performance.now();
    let raf;
    const tick = (t) => {
      const elapsed = t - startT;
      const p = Math.min(elapsed / duration, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setCount(value * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [started, value, duration]);

  const display = format ? format(count) : Math.floor(count).toLocaleString();
  return <span ref={ref}>{display}</span>;
}

// --- About Us (stats + paragraph + Apply Now, with offset polaroids) ----

function AboutUs({ scrollToRoles }) {
  return (
    <section className="about-us section-block">
      <div className="about-us-stage">
        {/* Upper-left polaroid — tilted, group/behind-the-scenes shot. */}
        <Polaroid
          src="assets/photo7_team.jpg"
          caption="16 hour shoot wrapped."
          rotate={-8}
          w={210}
          className="about-poly about-poly--tl" />

        <h2 className="about-us-title">About Us</h2>

        <div className="about-stats">
          <div className="about-stat">
            <div className="about-stat-value">
              <CountUp
                value={3.4}
                format={(v) => v.toFixed(1) + "M"} />

            </div>
            <div className="about-stat-label">Subscribers</div>
          </div>
          <div className="about-stat">
            <div className="about-stat-value">
              <CountUp
                value={500}
                format={(v) => Math.round(v) + "M+"} />

            </div>
            <div className="about-stat-label">Views</div>
          </div>
        </div>

        <div className="about-copy-wrap">
          <p className="about-copy">
            Family Friendly was created with the unwavering mission of
            becoming the biggest comedy brand on the planet.
          </p>
          <p className="about-copy">
            To accomplish this, we've centered our lives around one trait:
            Obsession. Obsessed with perfecting every detail. Obsessed with
            making people laugh. Obsessed with being the best.
          </p>
          <p className="about-copy about-copy--cta-line">
            If this sounds like you, hit apply now and let's talk.
          </p>
          <button
            className="btn btn-primary card-apply about-apply"
            onClick={scrollToRoles}>

            <span className="btn-label">Apply Now</span>
          </button>
        </div>
      </div>
    </section>);

}

function RoleRow({ role }) {
  return (
    <a className="role-row" href={role.url} target="_blank" rel="noopener">
      <span className="role-title">{role.title}</span>
      <span className="role-apply-btn">
        Apply Now
        <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
          <path d="M5 12h14M13 5l7 7-7 7" fill="none" stroke="currentColor"
          strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </span>
    </a>);

}

function Roles({ rolesRef }) {
  return (
    <section className="col section-block" id="roles" ref={rolesRef}>
      <h2 className="roles-title">Roles</h2>
      <p className="roles-sub">Apply now.</p>
      <div className="role-list">
        {ROLES.map((r) => <RoleRow role={r} key={r.title} />)}
      </div>
    </section>);

}

function Footer() {
  // The footer polaroid swings open to reveal a hidden CTA. Interaction
  // model differs by device:
  //   • Desktop (hover-capable): hover triggers the swing, leaving the
  //     element returns it. NO click toggling.
  //   • Touch (mobile): click toggles revealed state — first tap opens,
  //     second tap closes.
  const [revealed, setRevealed] = useState(false);
  const [isTouch, setIsTouch] = useState(false);

  useEffect(() => {
    // (hover: none) OR (pointer: coarse) → treat as touch.
    const mq = window.matchMedia("(hover: none), (pointer: coarse)");
    const update = () => setIsTouch(mq.matches);
    update();
    mq.addEventListener?.("change", update);
    return () => mq.removeEventListener?.("change", update);
  }, []);

  // On desktop, CSS handles the swing via .foot-polaroid-wrap:hover. The
  // `revealed` state only ever toggles for touch, so the JS class stays
  // off on desktop and doesn't fight the hover transition.
  const wrapClass =
  "foot-polaroid-wrap" + (
  isTouch && revealed ? " foot-polaroid-wrap--revealed" : "");
  const polaroidClass =
  "foot-polaroid" + (
  isTouch && revealed ? " foot-polaroid--revealed" : "");

  return (
    <footer className="site-foot" data-comment-anchor="06d1a4c74e-footer-208-5">
      <div className={wrapClass}>
        <p className="foot-secret">
          Why are you waiting?<br />APPLY NOW!
        </p>
        <Polaroid
          src="assets/photo2.jpg"
          caption="see you soon."
          rotate={-4}
          w={273}
          className={polaroidClass}
          onClick={isTouch ? () => setRevealed((v) => !v) : undefined}
          style={{ cursor: isTouch ? "pointer" : "default" }} />

      </div>
      <span role="img" aria-label="Family Friendly" className="foot-logo" data-comment-anchor="41c12b9130-span-217-7"></span>
      <p className="foot-tag">© {new Date().getFullYear()} Family Friendly</p>
    </footer>);

}

// --- Tweaks -----------------------------------------------------------------

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#ff6a1a"
} /*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const rolesRef = useRef(null);

  useEffect(() => {
    document.documentElement.style.setProperty("--c-accent", t.accent);
  }, [t]);

  const scrollToRoles = () => {
    const title = document.querySelector(".roles-title");
    if (title) {
      const top = title.getBoundingClientRect().top + window.scrollY - 32;
      window.scrollTo({ top, behavior: "smooth" });
    } else if (rolesRef.current) {
      window.scrollTo({ top: rolesRef.current.offsetTop - 24, behavior: "smooth" });
    }
  };

  return (
    <React.Fragment>
      <main>
        <Hero scrollToRoles={scrollToRoles} />
        {/* Smooth transition between page 1 (black hero) and page 2
                                             (orange body) — short gradient strip that fades black → dark
                                             orange → page orange so the two pages blend. */}
        <div className="hero-to-body-fade" aria-hidden="true"></div>
        <AboutUs scrollToRoles={scrollToRoles} />
        <Roles rolesRef={rolesRef} />
      </main>
      <Footer />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Color">
          <TweakColor
            label="Accent"
            value={t.accent}
            onChange={(v) => setTweak("accent", v)}
            options={["#ff6a1a", "#e85d1f", "#c1421a", "#ff8a3d"]} />
        </TweakSection>
      </TweaksPanel>
    </React.Fragment>);

}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);