diff --git a/frontend/package.json b/frontend/package.json index 76c6a07..62fb0ca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "eject": "react-scripts eject" }, "dependencies": { + "gsap": "^3.13.0", "motion": "^12.23.22", "react": "^19.1.1", "react-dom": "^19.1.1" diff --git a/frontend/src/index.css b/frontend/src/index.css index d86eaf1..b2ff5db 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -32,3 +32,59 @@ section[id] { .animate-fade-in { animation: fadeIn 1s ease-out forwards; } + +@keyframes float { + 0%, 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } +} + +.animate-float { + animation: float 3s ease-in-out infinite; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes floatAndSpin { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + } + 50% { + transform: translateY(-10px) rotate(180deg); + } +} + +@keyframes floatAndSpinReverse { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + } + 50% { + transform: translateY(-8px) rotate(-180deg); + } +} + +.animate-spin-slow { + animation: spin 8s linear infinite; +} + +.animate-spin-reverse { + animation: spin 10s linear infinite reverse; +} + +.animate-float-spin { + animation: floatAndSpin 6s ease-in-out infinite; +} + +.animate-float-spin-reverse { + animation: floatAndSpinReverse 7s ease-in-out infinite; +} diff --git a/frontend/src/ui/common/assets/faq/base_brownpaper.jpg b/frontend/src/ui/common/assets/faq/base_brownpaper.jpg new file mode 100644 index 0000000..5fe40ce Binary files /dev/null and b/frontend/src/ui/common/assets/faq/base_brownpaper.jpg differ diff --git a/frontend/src/ui/common/assets/faq/base_texturedbrown.jpg b/frontend/src/ui/common/assets/faq/base_texturedbrown.jpg new file mode 100644 index 0000000..7739556 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/base_texturedbrown.jpg differ diff --git a/frontend/src/ui/common/assets/faq/base_whitepaper.jpg b/frontend/src/ui/common/assets/faq/base_whitepaper.jpg new file mode 100644 index 0000000..d424c6b Binary files /dev/null and b/frontend/src/ui/common/assets/faq/base_whitepaper.jpg differ diff --git a/frontend/src/ui/common/assets/faq/blackstars.png b/frontend/src/ui/common/assets/faq/blackstars.png new file mode 100644 index 0000000..fa771c4 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/blackstars.png differ diff --git a/frontend/src/ui/common/assets/faq/cost_a.png b/frontend/src/ui/common/assets/faq/cost_a.png new file mode 100644 index 0000000..faed382 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/cost_a.png differ diff --git a/frontend/src/ui/common/assets/faq/cost_q.png b/frontend/src/ui/common/assets/faq/cost_q.png new file mode 100644 index 0000000..1427fdc Binary files /dev/null and b/frontend/src/ui/common/assets/faq/cost_q.png differ diff --git a/frontend/src/ui/common/assets/faq/cover_back.png b/frontend/src/ui/common/assets/faq/cover_back.png new file mode 100644 index 0000000..96ab329 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/cover_back.png differ diff --git a/frontend/src/ui/common/assets/faq/cover_front.png b/frontend/src/ui/common/assets/faq/cover_front.png new file mode 100644 index 0000000..19160cf Binary files /dev/null and b/frontend/src/ui/common/assets/faq/cover_front.png differ diff --git a/frontend/src/ui/common/assets/faq/hack_a.png b/frontend/src/ui/common/assets/faq/hack_a.png new file mode 100644 index 0000000..6b79ad2 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/hack_a.png differ diff --git a/frontend/src/ui/common/assets/faq/hack_q.png b/frontend/src/ui/common/assets/faq/hack_q.png new file mode 100644 index 0000000..a0c538e Binary files /dev/null and b/frontend/src/ui/common/assets/faq/hack_q.png differ diff --git a/frontend/src/ui/common/assets/faq/inside_back_cover.png b/frontend/src/ui/common/assets/faq/inside_back_cover.png new file mode 100644 index 0000000..eb16aa5 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/inside_back_cover.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_1_date.png b/frontend/src/ui/common/assets/faq/leftp/left_1_date.png new file mode 100644 index 0000000..c7e23bb Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_1_date.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_1_heart.png b/frontend/src/ui/common/assets/faq/leftp/left_1_heart.png new file mode 100644 index 0000000..69d5c23 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_1_heart.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_1_photocard.png b/frontend/src/ui/common/assets/faq/leftp/left_1_photocard.png new file mode 100644 index 0000000..c435885 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_1_photocard.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_1_star.png b/frontend/src/ui/common/assets/faq/leftp/left_1_star.png new file mode 100644 index 0000000..077bda8 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_1_star.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_a.png b/frontend/src/ui/common/assets/faq/leftp/left_2_a.png new file mode 100644 index 0000000..94b4c43 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_a.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_butterfly.png b/frontend/src/ui/common/assets/faq/leftp/left_2_butterfly.png new file mode 100644 index 0000000..8e1c66b Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_butterfly.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_flower.png b/frontend/src/ui/common/assets/faq/leftp/left_2_flower.png new file mode 100644 index 0000000..b297bcd Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_flower.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_flowerstem.png b/frontend/src/ui/common/assets/faq/leftp/left_2_flowerstem.png new file mode 100644 index 0000000..0310242 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_flowerstem.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_q.png b/frontend/src/ui/common/assets/faq/leftp/left_2_q.png new file mode 100644 index 0000000..4e973a9 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_q.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_2_stamp.png b/frontend/src/ui/common/assets/faq/leftp/left_2_stamp.png new file mode 100644 index 0000000..43e8987 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_2_stamp.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_a.png b/frontend/src/ui/common/assets/faq/leftp/left_3_a.png new file mode 100644 index 0000000..dead043 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_a.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_priorities_a.png b/frontend/src/ui/common/assets/faq/leftp/left_3_priorities_a.png new file mode 100644 index 0000000..edf37ce Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_priorities_a.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_priority.png b/frontend/src/ui/common/assets/faq/leftp/left_3_priority.png new file mode 100644 index 0000000..56780e2 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_priority.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_q.png b/frontend/src/ui/common/assets/faq/leftp/left_3_q.png new file mode 100644 index 0000000..b5d633e Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_q.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_stamp.png b/frontend/src/ui/common/assets/faq/leftp/left_3_stamp.png new file mode 100644 index 0000000..0c187f5 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_stamp.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_stars.png b/frontend/src/ui/common/assets/faq/leftp/left_3_stars.png new file mode 100644 index 0000000..d3aba06 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_stars.png differ diff --git a/frontend/src/ui/common/assets/faq/leftp/left_3_ticket.png b/frontend/src/ui/common/assets/faq/leftp/left_3_ticket.png new file mode 100644 index 0000000..085abfd Binary files /dev/null and b/frontend/src/ui/common/assets/faq/leftp/left_3_ticket.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/a.png b/frontend/src/ui/common/assets/faq/letters/a.png new file mode 100644 index 0000000..25e7909 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/a.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/c.png b/frontend/src/ui/common/assets/faq/letters/c.png new file mode 100644 index 0000000..69a6d3f Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/c.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/d.png b/frontend/src/ui/common/assets/faq/letters/d.png new file mode 100644 index 0000000..5e3acd0 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/d.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/e.png b/frontend/src/ui/common/assets/faq/letters/e.png new file mode 100644 index 0000000..0a79976 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/e.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/e_2.png b/frontend/src/ui/common/assets/faq/letters/e_2.png new file mode 100644 index 0000000..a56d6c3 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/e_2.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/g.png b/frontend/src/ui/common/assets/faq/letters/g.png new file mode 100644 index 0000000..5eb7361 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/g.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/h.png b/frontend/src/ui/common/assets/faq/letters/h.png new file mode 100644 index 0000000..a4bdfb3 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/h.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/i.png b/frontend/src/ui/common/assets/faq/letters/i.png new file mode 100644 index 0000000..3330e3f Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/i.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/k.png b/frontend/src/ui/common/assets/faq/letters/k.png new file mode 100644 index 0000000..27fd1b8 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/k.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/r.png b/frontend/src/ui/common/assets/faq/letters/r.png new file mode 100644 index 0000000..6802c08 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/r.png differ diff --git a/frontend/src/ui/common/assets/faq/letters/u.png b/frontend/src/ui/common/assets/faq/letters/u.png new file mode 100644 index 0000000..ac2d94f Binary files /dev/null and b/frontend/src/ui/common/assets/faq/letters/u.png differ diff --git a/frontend/src/ui/common/assets/faq/lightbulb.png b/frontend/src/ui/common/assets/faq/lightbulb.png new file mode 100644 index 0000000..96f9b9c Binary files /dev/null and b/frontend/src/ui/common/assets/faq/lightbulb.png differ diff --git a/frontend/src/ui/common/assets/faq/page1_stars.png b/frontend/src/ui/common/assets/faq/page1_stars.png new file mode 100644 index 0000000..264d43b Binary files /dev/null and b/frontend/src/ui/common/assets/faq/page1_stars.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_a.png b/frontend/src/ui/common/assets/faq/rightp/right_2_a.png new file mode 100644 index 0000000..d433c86 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_a.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_croissiant.png b/frontend/src/ui/common/assets/faq/rightp/right_2_croissiant.png new file mode 100644 index 0000000..a2bece8 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_croissiant.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_exclam.png b/frontend/src/ui/common/assets/faq/rightp/right_2_exclam.png new file mode 100644 index 0000000..03625d9 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_exclam.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_plane.png b/frontend/src/ui/common/assets/faq/rightp/right_2_plane.png new file mode 100644 index 0000000..7dec902 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_plane.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_q.png b/frontend/src/ui/common/assets/faq/rightp/right_2_q.png new file mode 100644 index 0000000..62ee3ff Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_q.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_sandwich.png b/frontend/src/ui/common/assets/faq/rightp/right_2_sandwich.png new file mode 100644 index 0000000..1579e58 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_sandwich.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_2_texts.png b/frontend/src/ui/common/assets/faq/rightp/right_2_texts.png new file mode 100644 index 0000000..bb6bd56 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_2_texts.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_a.png b/frontend/src/ui/common/assets/faq/rightp/right_3_a.png new file mode 100644 index 0000000..62712c1 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_a.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_fish.png b/frontend/src/ui/common/assets/faq/rightp/right_3_fish.png new file mode 100644 index 0000000..de36ed2 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_fish.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_q.png b/frontend/src/ui/common/assets/faq/rightp/right_3_q.png new file mode 100644 index 0000000..cd0348d Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_q.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_ticket.png b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket.png new file mode 100644 index 0000000..d8731da Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_ticket2.png b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket2.png new file mode 100644 index 0000000..67e7a7b Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket2.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_3_ticket3.png b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket3.png new file mode 100644 index 0000000..bf445e1 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_3_ticket3.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_a.png b/frontend/src/ui/common/assets/faq/rightp/right_4_a.png new file mode 100644 index 0000000..540eeaa Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_a.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_a2.png b/frontend/src/ui/common/assets/faq/rightp/right_4_a2.png new file mode 100644 index 0000000..1758fb8 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_a2.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_flower.png b/frontend/src/ui/common/assets/faq/rightp/right_4_flower.png new file mode 100644 index 0000000..329aed5 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_flower.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_plant.png b/frontend/src/ui/common/assets/faq/rightp/right_4_plant.png new file mode 100644 index 0000000..8cc3aa2 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_plant.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_q.png b/frontend/src/ui/common/assets/faq/rightp/right_4_q.png new file mode 100644 index 0000000..8d27a7c Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_q.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_q2.png b/frontend/src/ui/common/assets/faq/rightp/right_4_q2.png new file mode 100644 index 0000000..7fbe11c Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_q2.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_sketch.png b/frontend/src/ui/common/assets/faq/rightp/right_4_sketch.png new file mode 100644 index 0000000..107dd3d Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_sketch.png differ diff --git a/frontend/src/ui/common/assets/faq/rightp/right_4_star.png b/frontend/src/ui/common/assets/faq/rightp/right_4_star.png new file mode 100644 index 0000000..868d07f Binary files /dev/null and b/frontend/src/ui/common/assets/faq/rightp/right_4_star.png differ diff --git a/frontend/src/ui/common/assets/faq/stars1.png b/frontend/src/ui/common/assets/faq/stars1.png new file mode 100644 index 0000000..7e24388 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/stars1.png differ diff --git a/frontend/src/ui/common/assets/faq/stars2.png b/frontend/src/ui/common/assets/faq/stars2.png new file mode 100644 index 0000000..36aea88 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/stars2.png differ diff --git a/frontend/src/ui/common/assets/faq/water.png b/frontend/src/ui/common/assets/faq/water.png new file mode 100644 index 0000000..2256cf2 Binary files /dev/null and b/frontend/src/ui/common/assets/faq/water.png differ diff --git a/frontend/src/ui/common/assets/sponsors/AmericanFidelity.png b/frontend/src/ui/common/assets/sponsors/AmericanFidelity.png new file mode 100644 index 0000000..6be138a Binary files /dev/null and b/frontend/src/ui/common/assets/sponsors/AmericanFidelity.png differ diff --git a/frontend/src/ui/pages/faq/index.tsx b/frontend/src/ui/pages/faq/index.tsx new file mode 100644 index 0000000..7c4c53e --- /dev/null +++ b/frontend/src/ui/pages/faq/index.tsx @@ -0,0 +1,1572 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; +import gsap from "gsap"; + +import CoverFront from "./section/CoverFront"; +import CoverBack from "./section/CoverBack"; +import FlipSpread, { Sticker } from "./section/FlipSpread"; + +import InsideFrontCover from "../../common/assets/faq/cover_back.png"; +import BrownPaper from "../../common/assets/faq/base_brownpaper.jpg"; +import WhitePaper from "../../common/assets/faq/base_whitepaper.jpg"; +import TexturedBrown from "../../common/assets/faq/base_texturedbrown.jpg"; +import BrownPaper2 from "../../common/assets/faq/base_brownpaper.jpg"; +import InsideBackCover from "../../common/assets/faq/inside_back_cover.png"; + + +// Right One (index 0)page stickers +import HackQ from "../../common/assets/faq/hack_q.png"; +import HackA from "../../common/assets/faq/hack_a.png"; +import CostQ from "../../common/assets/faq/cost_q.png"; +import CostA from "../../common/assets/faq/cost_a.png"; +import Lightbulb from "../../common/assets/faq/lightbulb.png"; +import Stars1 from "../../common/assets/faq/page1_stars.png"; + +//right page (index 1) stickers +import RightTwoAirplane from "../../common/assets/faq/rightp/right_2_plane.png"; +import RightTwoSandwich from "../../common/assets/faq/rightp/right_2_sandwich.png"; +import RightTwoCroissant from "../../common/assets/faq/rightp/right_2_croissiant.png"; +import RightTwoExclam from "../../common/assets/faq/rightp/right_2_exclam.png"; +import RightTwoQ from "../../common/assets/faq/rightp/right_2_q.png"; +import RightTwoA from "../../common/assets/faq/rightp/right_2_a.png"; +import RightTwoText from "../../common/assets/faq/rightp/right_2_texts.png"; +//right page (index 2) stickers +import RightThreeQ from "../../common/assets/faq/rightp/right_3_q.png"; +import RightThreeA from "../../common/assets/faq/rightp/right_3_a.png"; +import RightThreeTicket from "../../common/assets/faq/rightp/right_3_ticket.png"; +import RightThreeTicket2 from "../../common/assets/faq/rightp/right_3_ticket2.png"; +import RightThreeTicket3 from "../../common/assets/faq/rightp/right_3_ticket3.png"; +import RightThreeFish from "../../common/assets/faq/rightp/right_3_fish.png"; + +// right page (index 3) stickers +import RightFourQ from "../../common/assets/faq/rightp/right_4_q.png"; +import RightFourA from "../../common/assets/faq/rightp/right_4_a.png"; +import RightFourQ2 from "../../common/assets/faq/rightp/right_4_q2.png"; +import RightFourA2 from "../../common/assets/faq/rightp/right_4_a2.png"; +import RightFourFlower from "../../common/assets/faq/rightp/right_4_flower.png"; +import RightFourPlant from "../../common/assets/faq/rightp/right_4_plant.png"; +import RightFourSketch from "../../common/assets/faq/rightp/right_4_sketch.png"; +import RightFourStar from "../../common/assets/faq/rightp/right_4_star.png"; + +// Left page (index 0)stickers +import LeftOnePhotoCard from "../../common/assets/faq/leftp/left_1_photocard.png"; +import LeftOneHeart from "../../common/assets/faq/leftp/left_1_heart.png"; +import LeftOneDate from "../../common/assets/faq/leftp/left_1_date.png"; +import LeftOneStar from "../../common/assets/faq/leftp/left_1_star.png"; + +// Left page (index 1) stickers +import LeftTwoQ from "../../common/assets/faq/leftp/left_2_q.png"; +import LeftTwoA from "../../common/assets/faq/leftp/left_2_a.png"; +import LeftTwoFlowers from "../../common/assets/faq/leftp/left_2_flower.png"; +import LeftTwoFlower2 from "../../common/assets/faq/leftp/left_2_flower.png"; +import LeftTwoFlowerStem from "../../common/assets/faq/leftp/left_2_flowerstem.png"; +import LeftTwoButterfly from "../../common/assets/faq/leftp/left_2_butterfly.png"; +import LeftTwoStamp from "../../common/assets/faq/leftp/left_2_stamp.png"; + +// Left page (index 2) stickers +import LeftThreeQ from "../../common/assets/faq/leftp/left_3_q.png"; +import LeftThreeA from "../../common/assets/faq/leftp/left_3_a.png"; +import LeftThreeTicket from "../../common/assets/faq/leftp/left_3_ticket.png"; +import LeftThreeStamp from "../../common/assets/faq/leftp/left_3_stamp.png"; +import LeftThreeStar from "../../common/assets/faq/leftp/left_3_stars.png"; +import LeftThreePriority from "../../common/assets/faq/leftp/left_3_priority.png"; +import LeftThreePriorityA from "../../common/assets/faq/leftp/left_3_priorities_a.png"; + +// + +type Mode = "frontClosed" | "open" | "backClosed"; + +const FaqPages: React.FC = () => { + const containerRef = useRef(null); + const bookRef = useRef(null); + + const frontCoverRef = useRef(null); + + // Spread container (2 pages wide) + const spreadRef = useRef(null); + + // back cover underlay inside spread (right half) + const backCoverUnderRef = useRef(null); + + // solo back cover (when closed to back) + const backCoverSoloRef = useRef(null); + + const openFrontTl = useRef(null); + const openBackTl = useRef(null); + const closeTl = useRef(null); + + // History stacks for true backward navigation + const history = useRef([]); + const [mode, setMode] = useState("frontClosed"); + + const rightPages = useMemo( + () => [BrownPaper, WhitePaper, TexturedBrown, BrownPaper2, InsideBackCover], + [] + ); + + const [leftSrc, setLeftSrc] = useState(InsideFrontCover); + const [leftIndex, setLeftIndex] = useState(-1); // -1 = inside front cover + const leftIndexHistory = useRef([]); + + const [rightIndex, setRightIndex] = useState(0); + + const [popupImage, setPopupImage] = useState(null); + const popupRef = useRef(null); + const popupContentRef = useRef(null); + + // Mobile: show pages in a popup viewer instead of the 2-page book spread + const [isMobile, setIsMobile] = useState(false); + const [mobileViewerOpen, setMobileViewerOpen] = useState(false); + const [mobilePageIndex, setMobilePageIndex] = useState(0); + const mobileViewerRef = useRef(null); + const mobileViewerContentRef = useRef(null); + const mobilePageImageRef = useRef(null); + const mobilePageContainerRef = useRef(null); + + const rightSrc = rightPages[rightIndex]; + const nextRightSrc = + rightIndex + 1 < rightPages.length ? rightPages[rightIndex + 1] : undefined; + const prevLeftSrc = history.current.length + ? history.current[history.current.length - 1] + : undefined; + + + + const isOpen = mode === "open"; + const isBackClosed = mode === "backClosed"; + + const coverWidth = () => + frontCoverRef.current?.getBoundingClientRect().width || 450; + + // ---------------------------- + // Right page stickers - customize based on rightIndex + // ---------------------------- + const rightStickers: Sticker[] = useMemo(() => { + if (rightIndex === 0) { + return [ + { + id: "cost-q", + src: CostQ, + x: 40, // % + y: 70, // % + rotate: 0, + scale: .8, + onClick: () => setPopupImage(CostA), + }, + { + id: "hack-q", + src: HackQ, + x: 62, // % + y: 29, // % + rotate: -15, + scale: .68, + onClick: () => setPopupImage(HackA), + }, + { + id: "lightbulb", + src: Lightbulb, + x: 74, // % + y: 76, // % + rotate: -10, + scale: .50, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "stars", + src: Stars1, + x: 25, // % + y: 19, // % + rotate: -10, + scale: .4, + }, + ]; + } + + if (rightIndex === 1) { + return [ + { + id: "right-two-airplane", + src: RightTwoAirplane, + x: 40, // % + y: 55, // % + rotate: 10, + scale: 1.0, + }, + { + id: "right-two-sandwich", + src: RightTwoSandwich, + x: 15, // % + y: 25, // % + rotate: -5, + scale: 0.40, + hoverMoveX: 5, + hoverMoveY: -8, + hoverRotate: 15, + hoverScale: 1.15, + }, + { + id: "right-two-croissant", + src: RightTwoCroissant, + x: 42, // % + y: 15, // % + rotate: 10, + scale: 0.40, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1.2, + }, + { + id: "right-two-q", + src: RightTwoQ, + x: 63, // % + y: 80, // % + rotate: 8, + scale: 0.65, + onClick: () => setPopupImage(RightTwoA), + }, + { + id: "right-two-exclam", + src: RightTwoExclam, + x: 33, // % + y: 53, // % + rotate: -10, + scale: 0.3, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "right-two-text", + src: RightTwoText, + x: 32, // % + y: 36, // % + rotate: 0, + scale: 0.43, + }, + ]; + } + if (rightIndex === 2) { + return [ + { + id: "right-three-q", + src: RightThreeQ, + x: 63, // % + y: 80, // % + rotate: -2, + scale: 0.7, + onClick: () => setPopupImage(RightThreeA), + }, + { + id: "right-three-ticket", + src: RightThreeTicket, + x: 50, // % + y: 25, // % + rotate: -5, + scale: .9, + }, + { + id: "right-three-ticket2", + src: RightThreeTicket2, + x: 17, // % + y: 80, // % + rotate: -5, + scale: 0.50, + }, + { + id: "right-three-ticket3", + src: RightThreeTicket3, + x: 35, // % + y: 54, // % + rotate: -5, + scale: 0.35, + }, + { + id: "right-three-fish", + src: RightThreeFish, + x: 65, // % + y: 40, // % + rotate: 0, + scale: 0.6, + hoverMoveX: -10, + hoverMoveY: -20, + hoverRotate: -20, + hoverScale: 1, + }, + ]; + } + if (rightIndex === 3) { + return [ + { + id: "right-four-q", + src: RightFourQ, + x: 66, // % + y: 20, // % + rotate: 0, + scale: 0.7, + onClick: () => setPopupImage(RightFourA), + }, + { + id: "right-four-q2", + src: RightFourQ2, + x: 35, // % + y: 75, // % + rotate: -2, + scale: 0.8, + onClick: () => setPopupImage(RightFourA2), + }, + { + id: "right-four-flower", + src: RightFourFlower, + x: 33, // % + y: 35, // % + rotate: -2, + scale: 0.37, + hoverSpin: true, + }, + + { + id: "right-four-plant", + src: RightFourPlant, + x: 63, // % + y: 88, // % + rotate: 0, + scale: 0.8, + }, + { + id: "right-four-sketch", + src: RightFourSketch, + x: 83, // % + y:40, // % + rotate: -2, + scale: 0.4, + }, + { + id: "right-four-star", + src: RightFourStar, + x: 2, // % + y: 2, // % + rotate: 0, + scale: .5, + }, + ]; + } + return []; + + }, [rightIndex]); + + // Left page stickers - customize based on leftIndex + const leftStickers: Sticker[] = useMemo(() => { + + if (leftIndex === 0 ) { + return [ + { + id: "left-one-photo-card", + src: LeftOnePhotoCard, + x: 40, // % + y: 50, // % + rotate: 10, + scale: .9, + onClick: () => setPopupImage(LeftOnePhotoCard), + + }, + { + id: "left-one-heart", + src: LeftOneHeart, + x: 13, // % + y: 87, // % + rotate: 15, + scale: .25, + }, + { + id: "left-one-date", + src: LeftOneDate, + x: 60, // % + y: 87, // % + rotate: 0, + scale: .7, + }, + { + id: "left-one-star", + src: LeftOneStar, + x: 80, // % + y: 20, // % + rotate: 0, + scale: .5, + }, + ]; + } + if (leftIndex === 1) { + return [ + { + id: "left-two-flower-stem", + src: LeftTwoFlowerStem, + x: 42, // % + y: 30, // % + rotate: 0, + scale: 0.8, + + }, + { + id: "left-two-q", + src: LeftTwoQ, + x: 37, // % + y: 70, // % + rotate: 0, + scale: 0.8, + onClick: () => setPopupImage(LeftTwoA), + }, + { + id: "left-two-flowers", + src: LeftTwoFlowers, + x: 75, // % + y: 82, // % + rotate: 0, + scale: 0.48, + hoverSpin: true, + }, + { + id: "left-two-flower-2", + src: LeftTwoFlower2, + x: 85, // % + y: 63, // % + rotate: 0, + scale: 0.2, + hoverSpin: true, + }, + { + id: "left-two-butterfly", + src: LeftTwoButterfly, + x: 75, // % + y: 20, // % + rotate: 0, + scale: 0.4, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "left-two-stamp", + src: LeftTwoStamp, + x: 20, // % + y: 45, // % + rotate: 0, + scale: 0.38, + + }, + ]; + } + if (leftIndex === 2) { + return [ + { + id: "left-three-q", + src: LeftThreeQ, + x: 49, // % + y: 64, // % + rotate: 0, + scale: 1, + onClick: () => setPopupImage(LeftThreeA), + }, + { + id: "left-three-stamp", + src: LeftThreeStamp, + x: 87, // % + y: 15, // % + rotate: 0, + scale: 0.45, + }, + { + id: "left-three-star", + src: LeftThreeStar, + x: 65, // % + y: 30, // % + rotate: 0, + scale: 0.5, + }, + { + id: "left-three-priority", + src: LeftThreePriority, + x: 20, // % + y: 28, // % + rotate: 0, + scale: 0.7, + onClick: () => setPopupImage(LeftThreePriorityA), + }, + + { + id: "left-three-ticket", + src: LeftThreeTicket, + x: 20, // % + y: 65, // % + rotate: 0, + scale: 0.38, + }, + ]; + } + return []; + }, [leftIndex]); + + // The underlay (next right page) stickers (usually none) + const nextRightStickers: Sticker[] = useMemo(() => [], []); + + // Prev-left stickers (for backward underface) (usually none) + const prevLeftStickers: Sticker[] = useMemo(() => [], []); + + // Helper functions to get stickers by index (for mobile pages) + const getRightStickersByIndex = (idx: number): Sticker[] => { + if (idx === 0) { + return [ + { + id: "cost-q", + src: CostQ, + x: 37, + y: 70, + rotate: 0, + scale: 0.8, + mobileScale: 0.28, // Adjust this value (0-1) to scale this sticker on mobile + onClick: () => setPopupImage(CostA), + }, + { + id: "hack-q", + src: HackQ, + x: 62, + y: 29, + rotate: -15, + scale: 0.68, + mobileScale: 0.55, // Adjust this value (0-1) to scale this sticker on mobile + onClick: () => setPopupImage(HackA), + }, + { + id: "lightbulb", + src: Lightbulb, + x: 74, + y: 76, + rotate: -10, + scale: 0.50, + mobileScale: 0.35, // Adjust this value (0-1) to scale this sticker on mobile + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "stars", + src: Stars1, + x: 25, + y: 19, + rotate: -10, + scale: 0.4, + mobileScale: 0.3, // Adjust this value (0-1) to scale this sticker on mobile + }, + ]; + } + if (idx === 1) { + return [ + { + id: "right-two-airplane", + src: RightTwoAirplane, + x: 40, + y: 55, + rotate: 10, + scale: 1.0, + mobileScale: 0.35, + }, + { + id: "right-two-sandwich", + src: RightTwoSandwich, + x: 15, + y: 25, + rotate: -5, + scale: 0.40, + mobileScale: 0.5, // Adjust this value (0-1) to scale this sticker on mobile + hoverMoveX: 5, + hoverMoveY: -8, + hoverRotate: 15, + hoverScale: 1.15, + }, + { + id: "right-two-croissant", + src: RightTwoCroissant, + x: 42, + y: 15, + rotate: 10, + scale: 0.40, + mobileScale: 0.55, // Adjust this value (0-1) to scale this sticker on mobile + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1.2, + }, + { + id: "right-two-q", + src: RightTwoQ, + x: 63, + y: 80, + rotate: 8, + scale: 0.65, + mobileScale: 0.55, + onClick: () => setPopupImage(RightTwoA), + }, + { + id: "right-two-exclam", + src: RightTwoExclam, + x: 33, + y: 53, + rotate: -10, + scale: 0.3, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "right-two-text", + src: RightTwoText, + x: 32, + y: 36, + rotate: 0, + scale: 0.43, + mobileScale: 0.7, + }, + ]; + } + if (idx === 2) { + return [ + { + id: "right-three-q", + src: RightThreeQ, + x: 63, + y: 80, + rotate: -2, + scale: 0.7, + mobileScale: 0.6, + onClick: () => setPopupImage(RightThreeA), + }, + { + id: "right-three-ticket", + src: RightThreeTicket, + x: 50, + y: 25, + rotate: -5, + scale: 0.9, + mobileScale: 0.7, + }, + { + id: "right-three-ticket2", + src: RightThreeTicket2, + x: 17, + y: 80, + rotate: -5, + scale: 0.50, + mobileScale: 0.5, + }, + { + id: "right-three-ticket3", + src: RightThreeTicket3, + x: 35, + y: 54, + rotate: -5, + scale: 0.35, + mobileScale: 0.7, + }, + { + id: "right-three-fish", + src: RightThreeFish, + x: 65, + y: 45, + rotate: 0, + scale: 0.6, + mobileScale: 0.7, + hoverMoveX: -10, + hoverMoveY: -20, + hoverRotate: -20, + hoverScale: 1, + }, + ]; + } + if (idx === 3) { + return [ + { + id: "right-four-q", + src: RightFourQ, + x: 66, + y: 24, + rotate: 0, + scale: 0.7, + mobileScale: 0.6, + onClick: () => setPopupImage(RightFourA), + }, + { + id: "right-four-q2", + src: RightFourQ2, + x: 35, + y: 75, + rotate: -2, + scale: 0.8, + mobileScale: 0.6, + onClick: () => setPopupImage(RightFourA2), + }, + { + id: "right-four-flower", + src: RightFourFlower, + x: 33, + y: 35, + rotate: -2, + scale: 0.37, + mobileScale: 0.6, + hoverSpin: true, + }, + { + id: "right-four-plant", + src: RightFourPlant, + x: 63, + y: 85, + rotate: 0, + scale: 0.8, + mobileScale: 0.6, + }, + { + id: "right-four-sketch", + src: RightFourSketch, + x: 83, + y: 40, + rotate: -2, + scale: 0.4, + mobileScale: 0.6, + }, + { + id: "right-four-star", + src: RightFourStar, + x: 2, + y: 12, + rotate: 0, + scale: 0.5, + mobileScale: 0.6, + }, + ]; + } + return []; + }; + + const getLeftStickersByIndex = (idx: number): Sticker[] => { + if (idx === 0) { + return [ + { + id: "left-one-photo-card", + src: LeftOnePhotoCard, + x: 47, + y: 50, + rotate: 10, + scale: 0.9, + mobileScale: 0.8, + onClick: () => setPopupImage(LeftOnePhotoCard), + }, + { + id: "left-one-heart", + src: LeftOneHeart, + x: 13, + y: 82, + rotate: 15, + scale: 0.25, + mobileScale: 0.5, + + }, + { + id: "left-one-date", + src: LeftOneDate, + x: 60, + y: 83, + rotate: 0, + scale: 0.7, + mobileScale: 0.55, + }, + { + id: "left-one-star", + src: LeftOneStar, + x: 80, + y: 20, + rotate: 0, + scale: 0.5, + }, + ]; + } + if (idx === 1) { + return [ + { + id: "left-two-flower-stem", + src: LeftTwoFlowerStem, + x: 42, + y: 30, + rotate: 0, + scale: 0.8, + mobileScale: 0.6, + }, + { + id: "left-two-q", + src: LeftTwoQ, + x: 39, + y: 67, + rotate: 0, + scale: 0.8, + mobileScale: 0.7, + onClick: () => setPopupImage(LeftTwoA), + }, + { + id: "left-two-flowers", + src: LeftTwoFlowers, + x: 75, + y: 82, + rotate: 0, + scale: 0.48, + mobileScale: 0.55, + hoverSpin: true, + }, + { + id: "left-two-flower-2", + src: LeftTwoFlower2, + x: 85, + y: 63, + rotate: 0, + mobileScale: 0.6, + scale: 0.2, + hoverSpin: true, + }, + { + id: "left-two-butterfly", + src: LeftTwoButterfly, + x: 75, + y: 20, + rotate: 0, + scale: 0.4, + mobileScale: 0.6, + hoverMoveX: -8, + hoverMoveY: -5, + hoverRotate: -10, + hoverScale: 1, + }, + { + id: "left-two-stamp", + src: LeftTwoStamp, + x: 20, + y: 45, + rotate: 0, + scale: 0.38, + mobileScale: 0.6, + }, + ]; + } + if (idx === 2) { + return [ + { + id: "left-three-q", + src: LeftThreeQ, + x: 49, + y: 64, + rotate: 0, + scale: 1, + mobileScale: 0.385, + onClick: () => setPopupImage(LeftThreeA), + }, + { + id: "left-three-stamp", + src: LeftThreeStamp, + x: 87, + y: 20, + rotate: 0, + scale: 0.45, + }, + { + id: "left-three-star", + src: LeftThreeStar, + x: 65, + y: 30, + rotate: 0, + scale: 0.5, + }, + { + id: "left-three-priority", + src: LeftThreePriority, + x: 20, + y: 30, + rotate: 0, + scale: 0.7, + mobileScale: 0.6, + onClick: () => setPopupImage(LeftThreePriorityA), + }, + { + id: "left-three-ticket", + src: LeftThreeTicket, + x: 20, + y: 65, + rotate: 0, + scale: 0.38, + }, + ]; + } + return []; + }; + + type MobilePage = { + id: string; + src: string; + stickers: Sticker[]; + title?: string; + }; + + const mobilePages: MobilePage[] = useMemo(() => { + const pages: MobilePage[] = [ + { id: "inside-front", src: InsideFrontCover, stickers: [], title: "Inside cover" } + ]; + + rightPages.forEach((src, i) => { + pages.push({ + id: `right-${i}`, + src, + stickers: getRightStickersByIndex(i), + title: `Page ${i + 1}` + }); + pages.push({ + id: `left-${i}`, + src, + stickers: getLeftStickersByIndex(i), + title: `Page ${i + 1} (left)` + }); + }); + + return pages; + }, [rightPages]); + + // ---------------------------- + // Page state actions + // ---------------------------- + const advance = () => { + if (!nextRightSrc) { + closeToBackCover(); + return; + } + + // save previous left state + history.current.push(leftSrc); + leftIndexHistory.current.push(leftIndex); + + // new left becomes current right (use current rightIndex BEFORE it increments) + setLeftSrc(rightSrc); + setLeftIndex(rightIndex); + + setRightIndex((i) => Math.min(i + 1, rightPages.length - 1)); + }; + + const goBack = () => { + if (rightIndex === 0) { + closeToFrontCover(); + return; + } + + const prev = history.current.pop(); + const prevIdx = leftIndexHistory.current.pop(); + + setRightIndex((i) => Math.max(i - 1, 0)); + + if (prev !== undefined) setLeftSrc(prev); + if (prevIdx !== undefined) setLeftIndex(prevIdx); + }; + + + // ---------------------------- + // Animations + // ---------------------------- + const openFromFront = () => { + if (mode !== "frontClosed") return; + openFrontTl.current?.restart(); + }; + + const openFromBack = () => { + if (mode !== "backClosed") return; + openBackTl.current?.restart(); + }; + + const closeToBackCover = () => { + const book = bookRef.current; + const spread = spreadRef.current; + const backUnder = backCoverUnderRef.current; + if (!book || !spread) return; + + closeTl.current?.kill(); + closeTl.current = gsap.timeline({ defaults: { ease: "power3.inOut" } }); + + closeTl.current + .add(() => { + gsap.set(spread, { pointerEvents: "none" }); + }, 0) + .to(spread, { autoAlpha: 0, duration: 0.25 }, 0) + .to(book, { x: 0, duration: 0.9 }, 0) + .to(backUnder ?? {}, { autoAlpha: 0, duration: 0.2 }, 0.05) + .add(() => { + setMode("backClosed"); + }, 0.35); + }; + + const closeToFrontCover = () => { + const cover = frontCoverRef.current; + const book = bookRef.current; + const spread = spreadRef.current; + if (!cover || !book || !spread) return; + + closeTl.current?.kill(); + closeTl.current = gsap.timeline({ defaults: { ease: "power3.inOut" } }); + + closeTl.current + .add(() => { + gsap.set(spread, { pointerEvents: "none" }); + gsap.set(cover, { pointerEvents: "auto" }); + }, 0) + .to(spread, { autoAlpha: 0, duration: 0.25 }, 0) + .to(book, { x: 0, duration: 0.9 }, 0) + .to(cover, { rotationY: 0, duration: 0.9 }, 0) + .add(() => { + setMode("frontClosed"); + }, 0.35); + }; + + // ---------------------------- + // Popup + // ---------------------------- + const closePopup = () => { + if (!popupRef.current || !popupContentRef.current) { + setPopupImage(null); + return; + } + gsap.to(popupRef.current, { + opacity: 0, + duration: 0.2, + ease: "power2.out", + onComplete: () => setPopupImage(null), + }); + gsap.to(popupContentRef.current, { + scale: 0.9, + opacity: 0, + duration: 0.2, + ease: "power2.out", + }); + }; + + useEffect(() => { + if (popupImage && popupRef.current && popupContentRef.current) { + gsap.fromTo( + popupRef.current, + { opacity: 0 }, + { opacity: 1, duration: 0.2, ease: "power2.out" } + ); + gsap.fromTo( + popupContentRef.current, + { scale: 0.9, opacity: 0 }, + { scale: 1, opacity: 1, duration: 0.3, ease: "back.out(1.7)" } + ); + } + }, [popupImage]); + + useEffect(() => { + if (!popupImage) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") closePopup(); + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [popupImage]); + useEffect(() => { + if (popupImage) { + // lock scroll + const originalOverflow = document.body.style.overflow; + const originalPaddingRight = document.body.style.paddingRight; + + // prevent layout shift when scrollbar disappears + const scrollbarWidth = + window.innerWidth - document.documentElement.clientWidth; + + document.body.style.overflow = "hidden"; + document.body.style.paddingRight = `${scrollbarWidth}px`; + + return () => { + // restore scroll + document.body.style.overflow = originalOverflow; + document.body.style.paddingRight = originalPaddingRight; + }; + } + }, [popupImage]); + + // ---------------------------- + // Initial GSAP setup + timelines + // ---------------------------- + useEffect(() => { + const container = containerRef.current; + const book = bookRef.current; + const cover = frontCoverRef.current; + const spread = spreadRef.current; + const backUnder = backCoverUnderRef.current; + + if (!container || !book || !cover || !spread) return; + + gsap.set(container, { perspective: 3000, perspectiveOrigin: "center center" }); + gsap.set(book, { transformStyle: "preserve-3d", force3D: true, x: 0 }); + + gsap.set(cover, { + transformStyle: "preserve-3d", + transformOrigin: "left center", + backfaceVisibility: "hidden", + force3D: true, + rotationY: 0, + z: 20, + }); + + gsap.set(spread, { autoAlpha: 0, pointerEvents: "none" }); + gsap.set(backUnder ?? {}, { autoAlpha: 0 }); + + // --- open from front --- + openFrontTl.current?.kill(); + openFrontTl.current = gsap + .timeline({ paused: true, defaults: { ease: "power3.inOut" } }) + .add(() => { + gsap.set(spread, { autoAlpha: 0, pointerEvents: "none" }); + gsap.set(backUnder ?? {}, { autoAlpha: 0 }); + gsap.set(book, { x: 0 }); + gsap.set(cover, { autoAlpha: 1, pointerEvents: "auto" }); + }, 0) + .to(cover, { rotationY: -179.9, duration: 1.2 }, 0) + .to(book, { x: coverWidth() / 2, duration: 1.2 }, 0) + .add(() => setMode("open"), 0.12) + .to(spread, { autoAlpha: 1, duration: 0.28, ease: "power2.out" }, 0.22) + .to(backUnder ?? {}, { autoAlpha: 1, duration: 0.28, ease: "power2.out" }, 0.25) + .add(() => { + gsap.set(spread, { pointerEvents: "auto" }); + gsap.set(cover, { pointerEvents: "none" }); + }, 1.2); + + // --- open from back (hinge RIGHT) --- + openBackTl.current?.kill(); + openBackTl.current = gsap + .timeline({ paused: true, defaults: { ease: "power3.inOut" } }) + .add(() => { + gsap.set(spread, { autoAlpha: 0, pointerEvents: "none" }); + gsap.set(backUnder ?? {}, { autoAlpha: 0 }); + gsap.set(book, { x: 0 }); + + // keep front rotated away so it can't show + gsap.set(cover, { rotationY: -179.9, autoAlpha: 1, pointerEvents: "none" }); + + // ensure solo back cover is visible and ready + gsap.set(backCoverSoloRef.current ?? {}, { autoAlpha: 1, rotationY: 0 }); + }, 0) + .to(backCoverSoloRef.current ?? {}, { rotationY: 179.9, duration: 1.2 }, 0) + .to(book, { x: coverWidth() / 2, duration: 1.2 }, 0) + .add(() => setMode("open"), 0.12) + .to(spread, { autoAlpha: 1, duration: 0.28, ease: "power2.out" }, 0.25) + .to(backUnder ?? {}, { autoAlpha: 1, duration: 0.25, ease: "power2.out" }, 0.25) + .add(() => { + gsap.set(spread, { pointerEvents: "auto" }); + }, 1.2); + + return () => { + openFrontTl.current?.kill(); + openBackTl.current?.kill(); + closeTl.current?.kill(); + }; + }, []); + + // cover click handler + useEffect(() => { + const cover = frontCoverRef.current; + if (!cover) return; + const onClick = () => openFromFront(); + cover.addEventListener("click", onClick); + return () => cover.removeEventListener("click", onClick); + }, [mode]); + + // Mobile breakpoint detection + useEffect(() => { + if (typeof window === "undefined") return; + const mq = window.matchMedia("(max-width: 639px)"); + const update = () => setIsMobile(mq.matches); + update(); + const legacy = mq as unknown as { + addListener?: (cb: () => void) => void; + removeListener?: (cb: () => void) => void; + }; + if (mq.addEventListener) mq.addEventListener("change", update); + else if (legacy.addListener) legacy.addListener(update); + + return () => { + if (mq.removeEventListener) mq.removeEventListener("change", update); + else if (legacy.removeListener) legacy.removeListener(update); + }; + }, []); + + // Animate + ESC close for mobile viewer + useEffect(() => { + if (!mobileViewerOpen) return; + + const originalOverflow = document.body.style.overflow; + const originalPaddingRight = document.body.style.paddingRight; + const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; + document.body.style.overflow = "hidden"; + document.body.style.paddingRight = `${scrollbarWidth}px`; + + if (mobileViewerRef.current && mobileViewerContentRef.current) { + gsap.fromTo( + mobileViewerRef.current, + { opacity: 0 }, + { opacity: 1, duration: 0.2, ease: "power2.out" } + ); + gsap.fromTo( + mobileViewerContentRef.current, + { scale: 0.95, opacity: 0 }, + { scale: 1, opacity: 1, duration: 0.25, ease: "power2.out" } + ); + } + + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") setMobileViewerOpen(false); + }; + window.addEventListener("keydown", onKey); + + return () => { + window.removeEventListener("keydown", onKey); + document.body.style.overflow = originalOverflow; + document.body.style.paddingRight = originalPaddingRight; + }; + }, [mobileViewerOpen]); + + // If switching from mobile -> desktop, ensure modal is closed + useEffect(() => { + if (!isMobile) setMobileViewerOpen(false); + }, [isMobile]); + + // Mobile-only UI: pages as a popup viewer (no book spread) + if (isMobile) { + const current = mobilePages[Math.max(0, Math.min(mobilePageIndex, mobilePages.length - 1))]; + const canPrev = mobilePageIndex > 0; + const canNext = mobilePageIndex < mobilePages.length - 1; + + return ( +
+ + + {mobileViewerOpen && ( +
setMobileViewerOpen(false)} + > +
e.stopPropagation()} + > + {/* Header: title + page number */} +
+
+ {current.title ?? "FAQ page"} +
+
+ {mobilePageIndex + 1} / {mobilePages.length} +
+
+ + {/* Page: fill remaining space, stickers scale with image */} +
+
+ {current.title + + {/* Stickers overlay - positioned relative to page image using percentages */} + {/* Container matches the image dimensions exactly, so percentage positioning scales correctly */} + {/* Mobile scale factor to reduce sticker sizes for mobile screens */} +
+ {current.stickers.map((s) => { + // Each sticker can have its own mobileScale, otherwise use default + const defaultMobileScale = 0.5; + const mobileScale = s.mobileScale ?? defaultMobileScale; + const finalScale = (s.scale ?? 1) * mobileScale; + + return ( + + ); + })} +
+
+
+ + {/* Navigation buttons */} +
+ + + + + +
+
+
+ )} + + {/* Popup modal for sticker Q/A images */} + {popupImage && ( +
+
e.stopPropagation()} + > + Popup + +
+
+ )} +
+ ); + } + + return ( +
+
+ {/* FRONT COVER (hidden only when backClosed) */} +
+ +
+ + {/* OPEN SPREAD */} +
+ {/* BACK COVER UNDER RIGHT HALF */} +
+ +
+ + {/* FLIP SPREAD */} +
+ +
+
+ + {/* BACK CLOSED: show ONLY back cover; clicking opens from back at same page state */} + {isBackClosed && ( +
+ +
+ )} +
+ + {/* POPUP MODAL */} + {popupImage && ( +
+
e.stopPropagation()} + > + Popup + +
+
+ )} +
+ ); +}; + +export default FaqPages; diff --git a/frontend/src/ui/pages/faq/section/CoverBack.tsx b/frontend/src/ui/pages/faq/section/CoverBack.tsx new file mode 100644 index 0000000..549b3cc --- /dev/null +++ b/frontend/src/ui/pages/faq/section/CoverBack.tsx @@ -0,0 +1,56 @@ +// CoverBack.tsx +import React from "react"; +import CoverBackImage from "../../../common/assets/faq/cover_back.png"; +import InsideCoverBackImage from "../../../common/assets/faq/inside_back_cover.png"; + +type CoverBackProps = { + side?: "inside" | "outside"; +}; + +const CoverBack: React.FC = ({ side = "inside" }) => { + const THICKNESS = 2; + + // If we want OUTSIDE facing camera, rotate the whole thing 180 + const containerRotate = side === "outside" ? "rotateY(180deg)" : "rotateY(0deg)"; + + return ( +
+ {/* INSIDE face */} +
+ Inside back cover +
+ + {/* OUTSIDE face (must be rotated 180 so it’s the opposite side) */} +
+ Back cover + {/* Text overlay */} +
+

+ Live Schedule coming soon.... +

+
+
+
+ ); +}; + +export default CoverBack; diff --git a/frontend/src/ui/pages/faq/section/CoverFront.tsx b/frontend/src/ui/pages/faq/section/CoverFront.tsx new file mode 100644 index 0000000..4bc8706 --- /dev/null +++ b/frontend/src/ui/pages/faq/section/CoverFront.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import CoverFrontImage from "../../../common/assets/faq/cover_front.png"; +import Stars1 from "../../../common/assets/faq/stars1.png"; +import Stars2 from "../../../common/assets/faq/stars2.png"; +import LetterH from "../../../common/assets/faq/letters/h.png"; +import LetterA from "../../../common/assets/faq/letters/a.png"; +import LetterC from "../../../common/assets/faq/letters/c.png"; +import LetterK from "../../../common/assets/faq/letters/k.png"; +import LetterE from "../../../common/assets/faq/letters/e.png"; +import LetterR from "../../../common/assets/faq/letters/r.png"; +import LetterG from "../../../common/assets/faq/letters/g.png"; +import LetterU from "../../../common/assets/faq/letters/u.png"; +import LetterI from "../../../common/assets/faq/letters/i.png"; +import LetterD from "../../../common/assets/faq/letters/d.png"; +import LetterE2 from "../../../common/assets/faq/letters/e_2.png"; + +type Letter = { id: string; src: string; className: string }; + +const Letters: Letter[] = [ + { id: "H", src: LetterH, className: "left-[13%] top-[36%] rotate-[-8deg]" }, + { id: "A", src: LetterA, className: "left-[26%] top-[37%] rotate-[4deg]" }, + { id: "C", src: LetterC, className: "left-[40%] top-[36%] rotate-[-3deg]" }, + { id: "K", src: LetterK, className: "left-[56%] top-[37%] rotate-[2deg]" }, + { id: "E", src: LetterE, className: "left-[70%] top-[36%] rotate-[-1deg]" }, + { id: "R", src: LetterR, className: "left-[83%] top-[37%] rotate-[1deg]" }, + { id: "G", src: LetterG, className: "left-[20%] top-[51%] rotate-[5deg]" }, + { id: "U", src: LetterU, className: "left-[36%] top-[52%] rotate-[5deg]" }, + { id: "I", src: LetterI, className: "left-[53%] top-[51%] rotate-[5deg]" }, + { id: "D", src: LetterD, className: "left-[60%] top-[51%] rotate-[5deg]" }, + { id: "E2", src: LetterE2, className: "left-[72%] top-[51%] rotate-[5deg]" }, +]; + +const CoverFront: React.FC = () => { + const THICKNESS = 2; + + return ( +
+ {/* OUTSIDE front cover */} +
+ Diary cover + + Stars decoration + Stars decoration + + {Letters.map((letter, index) => ( + {letter.id} + ))} +
+ +
+ ); +}; + +export default CoverFront; diff --git a/frontend/src/ui/pages/faq/section/FlipSpread.tsx b/frontend/src/ui/pages/faq/section/FlipSpread.tsx new file mode 100644 index 0000000..9acbfbe --- /dev/null +++ b/frontend/src/ui/pages/faq/section/FlipSpread.tsx @@ -0,0 +1,411 @@ +import React, { useEffect, useMemo, useRef } from "react"; +import gsap from "gsap"; +import { Draggable } from "gsap/Draggable"; + +gsap.registerPlugin(Draggable); + +export type Sticker = { + id: string; + src: string; + x: number; // percent + y: number; // percent + rotate?: number; + scale?: number; + onClick?: () => void; + // Hover animation properties + hoverMoveX?: number; // pixels to move on hover + hoverMoveY?: number; // pixels to move on hover + hoverRotate?: number; // additional rotation on hover (degrees) + hoverScale?: number; // scale multiplier on hover + hoverSpin?: boolean; // continuous rotation animation on hover + // Mobile-specific scaling + mobileScale?: number; // scale multiplier for mobile +}; + +type FlipSpreadProps = { + leftSrc: string; + rightSrc: string; + + nextRightSrc?: string; + prevLeftSrc?: string; + + leftStickers?: Sticker[]; + rightStickers?: Sticker[]; + nextRightStickers?: Sticker[]; + prevLeftStickers?: Sticker[]; + + onAdvance: () => void; + onBack: () => void; + onFinish: () => void; + onCloseFront: () => void; + + disabled?: boolean; +}; + +const clamp01 = (n: number) => Math.max(0, Math.min(1, n)); + +const FlipSpread: React.FC = ({ + leftSrc, + rightSrc, + nextRightSrc, + prevLeftSrc, + leftStickers = [], + rightStickers = [], + nextRightStickers = [], + + onAdvance, + onBack, + onFinish, + onCloseFront, + disabled = false, +}) => { + const fwdRef = useRef(null); + const backRef = useRef(null); + + const fwdTl = useRef(null); + const backTl = useRef(null); + const fwdDragRef = useRef(null); + const backDragRef = useRef(null); + + const fwdProxy = useMemo(() => document.createElement("div"), []); + const backProxy = useMemo(() => document.createElement("div"), []); + + const locked = useRef(false); + + const canGoNext = !!nextRightSrc; + const canGoBack = !!prevLeftSrc; + + /* ---------------- FORWARD FLIP ---------------- */ + useEffect(() => { + const el = fwdRef.current; + if (!el) return; + + gsap.set(el, { + rotationY: 0, + transformOrigin: "left center", + transformStyle: "preserve-3d", + force3D: true, + }); + + fwdTl.current = gsap.to(el, { + rotationY: -180, + duration: 1.2, + ease: "expo.out", + paused: true, + }); + + fwdDragRef.current?.kill(); + fwdDragRef.current = Draggable.create(fwdProxy, { + trigger: el, + type: "x", + bounds: { minX: -1, maxX: 0 }, + + onPress() { + if (disabled || locked.current) return; + const w = el.offsetWidth || 300; + (this as any).applyBounds({ minX: -w, maxX: 0 }); + gsap.set(fwdProxy, { x: -w * (fwdTl.current?.progress() || 0) }); + }, + + onDrag() { + if (disabled || locked.current) return; + const w = el.offsetWidth || 300; + fwdTl.current?.progress(clamp01(-this.x / w)); + }, + + onRelease() { + if (disabled || locked.current) return; + const commit = (fwdTl.current?.progress() || 0) > 0.4; + + locked.current = true; + gsap.to(fwdTl.current!, { + progress: commit ? 1 : 0, + duration: 0.6, + ease: "expo.out", + onComplete: () => { + locked.current = false; + if (!commit) return; + if (!canGoNext) onFinish(); + else onAdvance(); + }, + }); + }, + })[0]; + + return () => { + fwdTl.current?.kill(); + fwdDragRef.current?.kill(); + }; + }, [disabled, canGoNext, onAdvance, onFinish, fwdProxy]); + + /* ---------------- BACKWARD FLIP ---------------- */ + useEffect(() => { + const el = backRef.current; + if (!el) return; + + gsap.set(el, { + rotationY: 0, + transformOrigin: "right center", + transformStyle: "preserve-3d", + force3D: true, + }); + + backTl.current = gsap.to(el, { + rotationY: 180, + duration: 1.2, + ease: "expo.out", + paused: true, + }); + + backDragRef.current?.kill(); + backDragRef.current = Draggable.create(backProxy, { + trigger: el, + type: "x", + + onPress() { + if (disabled || locked.current || !canGoBack) return; + const w = el.offsetWidth || 300; + (this as any).applyBounds({ minX: 0, maxX: w }); + gsap.set(backProxy, { x: w * (backTl.current?.progress() || 0) }); + }, + + onDrag() { + if (disabled || locked.current || !canGoBack) return; + const w = el.offsetWidth || 300; + backTl.current?.progress(clamp01(this.x / w)); + }, + + onRelease() { + if (disabled || locked.current || !canGoBack) return; + const commit = (backTl.current?.progress() || 0) > 0.4; + + locked.current = true; + gsap.to(backTl.current!, { + progress: commit ? 1 : 0, + duration: 0.6, + ease: "expo.out", + onComplete: () => { + locked.current = false; + if (commit) onBack(); + }, + }); + }, + })[0]; + + return () => { + backTl.current?.kill(); + backDragRef.current?.kill(); + }; + }, [disabled, canGoBack, onBack, backProxy]); + + /* ---------------- STICKER RENDERER ---------------- */ + const StickerItem = ({ sticker }: { sticker: Sticker }) => { + const imgRef = useRef(null); + const spinTweenRef = useRef(null); + const hasHover = sticker.hoverMoveX !== undefined || sticker.hoverMoveY !== undefined || + sticker.hoverRotate !== undefined || sticker.hoverScale !== undefined || + sticker.hoverSpin === true; + + const baseTransform = ` + translate(-50%, -50%) + rotate(${sticker.rotate ?? 0}deg) + scale(${sticker.scale ?? 1}) + `; + + return ( + { + e.stopPropagation(); + sticker.onClick?.(); + }} + onMouseEnter={() => { + if (!hasHover || !imgRef.current) return; + const target = imgRef.current; + const moveX = sticker.hoverMoveX ?? 0; + const moveY = sticker.hoverMoveY ?? 0; + const baseRotation = sticker.rotate ?? 0; + const rotate = baseRotation + (sticker.hoverRotate ?? 0); + const scale = (sticker.scale ?? 1) * (sticker.hoverScale ?? 1); + + // Kill any existing spin animation + spinTweenRef.current?.kill(); + + if (sticker.hoverSpin) { + // Continuous spin animation + gsap.to(target, { + x: moveX, + y: moveY, + scale: scale, + duration: 0.5, + ease: "power2.out", + }); + + // Start continuous rotation from base rotation + spinTweenRef.current = gsap.to(target, { + rotation: baseRotation + 360, + duration: 2, + ease: "none", + repeat: -1, + }); + } else { + // Regular hover animation + gsap.to(target, { + x: moveX, + y: moveY, + rotation: rotate, + scale: scale, + duration: 0.3, + ease: "power2.out", + }); + } + }} + onMouseLeave={() => { + if (!hasHover || !imgRef.current) return; + const target = imgRef.current; + + // Kill spin animation + spinTweenRef.current?.kill(); + spinTweenRef.current = null; + + gsap.to(target, { + x: 0, + y: 0, + rotation: sticker.rotate ?? 0, + scale: sticker.scale ?? 1, + duration: 0.3, + ease: "power2.out", + }); + }} + style={{ + position: "absolute", + left: `${sticker.x}%`, + top: `${sticker.y}%`, + transform: baseTransform, + transformOrigin: "center center", + cursor: sticker.onClick ? "pointer" : hasHover ? "pointer" : "default", + filter: "drop-shadow(0 6px 10px rgba(0,0,0,.25))", + willChange: "transform", + }} + /> + ); + }; + + const Stickers = ({ items }: { items: Sticker[] }) => ( + <> + {items.map((s) => ( + + ))} + + ); + + return ( +
+ {/* LEFT STATIC */} +
+ + + {/* Left side shadow gradient towards spine */} +
+
+ + {/* RIGHT UNDERLAY */} +
+ + + {/* Right side shadow gradient towards spine */} +
+
+ + {/* CENTER SPINE - Creates 3D binding effect */} +
+ + {/* Center crease highlight (subtle) */} +
+ + {/* BACK TURN */} + {canGoBack && ( +
+
+ + + {/* Shadow gradient for turning left page */} +
+
+
+ )} + + {/* FORWARD TURN */} +
+
+ + + {/* Shadow gradient for turning right page */} +
+
+
+ + {/* CLICK ZONES */} +
!disabled && (canGoBack ? onBack() : onCloseFront())} /> +
!disabled && (canGoNext ? onAdvance() : onFinish())} /> +
+ ); +}; + +export default FlipSpread; diff --git a/frontend/src/ui/pages/landing/index.tsx b/frontend/src/ui/pages/landing/index.tsx index 407e761..f59eb30 100644 --- a/frontend/src/ui/pages/landing/index.tsx +++ b/frontend/src/ui/pages/landing/index.tsx @@ -5,6 +5,7 @@ import SplashQuote from "./sections/SplashQuote"; import PhotoCollage from "./components/photoCollage/PhotoCollage"; import About from "../about/sections/About"; import Sponsors from "./sections/Sponsors"; +import FAQPages from "../faq"; const LandingPage: React.FC = () => { return ( @@ -22,6 +23,7 @@ const LandingPage: React.FC = () => { +
); diff --git a/frontend/src/ui/pages/landing/sections/LandingView.tsx b/frontend/src/ui/pages/landing/sections/LandingView.tsx index e8b5c74..9ebf596 100644 --- a/frontend/src/ui/pages/landing/sections/LandingView.tsx +++ b/frontend/src/ui/pages/landing/sections/LandingView.tsx @@ -60,6 +60,10 @@ const LandingView: React.FC = () => {
diff --git a/frontend/src/ui/pages/landing/sections/Sponsors.tsx b/frontend/src/ui/pages/landing/sections/Sponsors.tsx index e35f11a..981b456 100644 --- a/frontend/src/ui/pages/landing/sections/Sponsors.tsx +++ b/frontend/src/ui/pages/landing/sections/Sponsors.tsx @@ -7,6 +7,7 @@ import NorthropGrummanLogo from "../../../common/assets/sponsors/NorthropGrumman import WilliamsLogo from "../../../common/assets/sponsors/Williams.png"; import PaycomLogo from "../../../common/assets/sponsors/Paycom.png"; import CocaColaLogo from "../../../common/assets/sponsors/CocaCola.png"; +import AmericanFidelityLogo from "../../../common/assets/sponsors/AmericanFidelity.png"; interface SponsorLogo { src: string; @@ -51,6 +52,11 @@ const sponsorLogos: SponsorLogo[] = [ alt: "Sponsor 7 Coca-Cola", href: "https://www.coca-cola.com/", }, + { + src: AmericanFidelityLogo, + alt: "Sponsor 8 American Fidelity", + href: "https://www.americanfidelity.com/", + }, ]; const Sponsors: React.FC = () => {