/* ==========================================================
   scroll-fx.css — เอฟเฟกต์ตอนเลื่อนหน้า (CSS scroll-driven animation)

   ★ ของหลักในไฟล์นี้คือ "ฉากหลัง" — ดูหัวข้อ 4) ข้างล่าง
   ลูกค้าบอกว่าไล่สีนิ่งๆ อย่างเดียวมันเพลน ชั้นเมฆ/ดาว/แสงจึงเป็นงานหลัก
   ส่วนหัวข้อ 1)-3) (การ์ด · ประกายหัวข้อ · หัวข้อลอย) เป็นของรอง
   ตั้งใจทำให้เบา ไม่ให้แย่งความสนใจจากฉากหลัง — ถ้าจะปรับ ให้ลดไม่ใช่เพิ่ม

   ทำไมไม่ใช้ scroll listener ใน JS
   เอฟเฟกต์พวกนี้ขับด้วย `animation-timeline` ล้วน (view() สำหรับของที่ผูกกับ
   ตำแหน่ง element · scroll(root) สำหรับฉากหลัง) เบราว์เซอร์รันบน compositor
   thread ไม่ผ่าน main thread เลย จึงไม่แย่งเวลากับ canvas ดาว
   (js/starfield.js) ที่กิน main thread อยู่แล้ว

   ⚠ ข้อห้ามของโปรเจกต์นี้ที่ไฟล์นี้ยึดตาม
   - animate ได้แค่ transform / opacity (ยกเว้น background-position ของหัวข้อ
     ซึ่ง PM สั่งมาเอง และเป็น paint ของ text layer สั้นๆ ไม่ใช่ layout)
   - ห้าม filter / blur / backdrop-filter เด็ดขาด — เคยเป็นต้นเหตุ 129 ms/เฟรม
   - สีทุกสีมาจาก tokens.css (ที่นี่ใช้ผ่าน color-mix เท่านั้น ไม่มี hex)

   ⚠ ทุกกฎอยู่ใต้ `html.peg-sfx` ซึ่ง js/scroll-fx.js เป็นคนเติมให้
   ถ้า JS ไม่รัน / เบราว์เซอร์ไม่รองรับ / ผู้ใช้ขอลดการเคลื่อนไหว
   → class นี้ไม่ถูกเติม → ไม่มีอะไรในไฟล์นี้ทำงาน → ได้ .reveal เดิม (fade-up)
   ========================================================== */

/* ถ้า JS ไม่ได้เปิดสวิตช์ ชั้นฉากหลังต้องไม่โผล่มาเลย (กฎนี้อยู่นอก @supports ตั้งใจ) */
.sfx-bg { display: none; }

@supports (animation-timeline: view()) {

  /* ------------------------------------------------------------
     0) ปลดล็อก view() ในโซนน้ำเงิน

     blue.css ใส่ `overflow:hidden` ที่ .blue-sec ซึ่งทำให้ element นั้น
     กลายเป็น "scroll container" ของตัวเอง → view() ของลูกหลานทุกตัว
     จะผูกกับกล่องที่เลื่อนไม่ได้ ⇒ timeline inactive ⇒ เอฟเฟกต์ไม่ทำงานทั้งโซน

     `overflow:clip` ตัดขอบเหมือนกันทุกประการแต่ *ไม่* สร้าง scroll container
     (นี่คือเหตุผลที่ clip ถูกเพิ่มเข้ามาในสเปค) จึงเปลี่ยนที่นี่แทนการแก้
     blue.css ซึ่งเป็นไฟล์ของ section อื่น

     อย่าเปลี่ยนกลับเป็น hidden "ให้เหมือนไฟล์อื่น" — โซนน้ำเงินจะกลับไปเพลนทันที
     ------------------------------------------------------------ */
  html.peg-sfx [data-zone-panel="blue"] .blue-sec { overflow: clip; }


  /* ------------------------------------------------------------
     1) การ์ดเข้าจอแบบไล่ทีละใบ (stagger + scale)

     เห็นที่: กริดการ์ด VJ กำลังไลฟ์ (#live), VJ ในสังกัด (#vjs),
     4 จุดขาย + คุณสมบัติผู้สมัคร (#recruit), 4 เสาหลัก (#about),
     ขั้นตอนเติมเงิน (#topup), เรตโบนัส (#rates), ยศ/รางวัล (#perks)

     *** ทำไมต้องแยกเป็น 2 keyframes แทนที่จะรวมเป็นอันเดียว ***
     เพราะ opacity กับ transform ต้องการ fill-mode คนละแบบ

     opacity → fill:both
       ต้องค้างที่ 1 ตลอดหลังเลื่อนผ่าน ไม่งั้นจะตกกลับไปใช้ .reveal{opacity:0}
       ของ base.css ซึ่งรอ IntersectionObserver ใน main.js มาเติม .is-in ให้
       ปัญหาคือ IO พลาดได้จริง เช่นกดลิงก์ #recruit บน topbar แล้วหน้ากระโดด
       ข้ามการ์ดไปทั้งชุดในเฟรมเดียว → IO ไม่เคยรายงานว่า intersect → .is-in ไม่มา
       (วัดเจอจริงตอนทดสอบ: การ์ด .vjcard ค้างที่ opacity 0 ทั้งที่เลื่อนผ่านไปแล้ว)
       fill:both ตัดการพึ่งพา .is-in ทิ้งทั้งหมด = หลักประกันกฎ "ห้ามค้างที่ opacity 0"

     transform → fill:both เหมือนกัน แต่ถูก "ปลด" ตอน :hover (ดูกฎถัดไป)
       ถ้าปล่อยให้ค้าง อนิเมชันจะทับ transform ตลอดไป แล้ว :hover ที่ยกการ์ดขึ้น
       (.vjcard:hover / .pk-sell__item:hover / .pk-cond__item:hover ฯลฯ) จะตายหมด
       เพราะ CSS animation ชนะ declaration ปกติเสมอ
     ------------------------------------------------------------ */
  @keyframes peg-card-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes peg-card-lift {
    from { transform: translate3d(0, 18px, 0) scale(.985); }
    to   { transform: translate3d(0, 0, 0) scale(1); }
  }

  html.peg-sfx .live__grid    > .vjcard,
  html.peg-sfx .pk-vj-grid    > .pk-vj,
  html.peg-sfx .pk-about-grid > .pk-pillar,
  html.peg-sfx .pk-sell       > .pk-sell__item,
  html.peg-sfx .pk-cond__list > .pk-cond__item,
  html.peg-sfx .steps         > .step,
  html.peg-sfx .rates         > .rate,
  html.peg-sfx .perks         > .perk {
    /* ใช้ longhand ไม่ใช้ shorthand `animation:` เพราะ shorthand จะรีเซ็ต
       animation-duration กลับเป็นค่าเริ่มต้น ซึ่งอ่านง่ายกว่ามากถ้าเขียนตรงๆ ว่า
       auto = "กินความยาวของ range ทั้งช่วง" (ค่าที่ scroll-driven animation ต้องใช้) */
    animation-name: peg-card-fade, peg-card-lift;
    animation-duration: auto, auto;
    animation-timing-function: linear, linear;
    animation-fill-mode: both, both;
    animation-timeline: view(), view();
    /* จบที่ entry 60% ไม่ใช่ cover xx% — entry ผูกกับ "ตัวการ์ดโผล่พ้นจอ"
       ซึ่งไปถึงแน่นอนทุกใบ ส่วน cover% ของการ์ดใบท้ายๆ ของเอกสาร
       อาจเลื่อนไปไม่ถึงเพราะหน้าหมดก่อน แล้วค้างที่ opacity กลางๆ */
    animation-range: entry 0% entry 60%;
  }

  /* เหลื่อมกันทีละใบ: ขยับ "ช่วง" ของแต่ละคอลัมน์ทีละ 12%
     ใช้ nth-child(4n+k) เพราะกริดกว้างสุดของเว็บนี้คือ 4 คอลัมน์ (.live__grid)
     บนมือถือกริดเป็น 1 คอลัมน์อยู่แล้ว ค่าเหลื่อมจึงแทบไม่มีผล ไม่เสียอะไร */
  html.peg-sfx .live__grid    > .vjcard:nth-child(4n+2),
  html.peg-sfx .pk-vj-grid    > .pk-vj:nth-child(4n+2),
  html.peg-sfx .pk-about-grid > .pk-pillar:nth-child(4n+2),
  html.peg-sfx .pk-sell       > .pk-sell__item:nth-child(4n+2),
  html.peg-sfx .pk-cond__list > .pk-cond__item:nth-child(4n+2),
  html.peg-sfx .steps         > .step:nth-child(4n+2),
  html.peg-sfx .rates         > .rate:nth-child(4n+2),
  html.peg-sfx .perks         > .perk:nth-child(4n+2) {
    animation-range: entry 12% entry 72%;
  }
  html.peg-sfx .live__grid    > .vjcard:nth-child(4n+3),
  html.peg-sfx .pk-vj-grid    > .pk-vj:nth-child(4n+3),
  html.peg-sfx .pk-about-grid > .pk-pillar:nth-child(4n+3),
  html.peg-sfx .pk-sell       > .pk-sell__item:nth-child(4n+3),
  html.peg-sfx .pk-cond__list > .pk-cond__item:nth-child(4n+3),
  html.peg-sfx .steps         > .step:nth-child(4n+3),
  html.peg-sfx .rates         > .rate:nth-child(4n+3),
  html.peg-sfx .perks         > .perk:nth-child(4n+3) {
    animation-range: entry 24% entry 84%;
  }
  html.peg-sfx .live__grid    > .vjcard:nth-child(4n+4),
  html.peg-sfx .pk-vj-grid    > .pk-vj:nth-child(4n+4),
  html.peg-sfx .pk-about-grid > .pk-pillar:nth-child(4n+4),
  html.peg-sfx .pk-sell       > .pk-sell__item:nth-child(4n+4),
  html.peg-sfx .pk-cond__list > .pk-cond__item:nth-child(4n+4),
  html.peg-sfx .steps         > .step:nth-child(4n+4),
  html.peg-sfx .rates         > .rate:nth-child(4n+4),
  html.peg-sfx .perks         > .perk:nth-child(4n+4) {
    animation-range: entry 36% entry 96%;
  }

  /* ตอน :hover ถอด peg-card-lift ออกจากรายการ เหลือแต่ peg-card-fade
     → transform หลุดจากมือของอนิเมชัน กลับไปเป็นของไฟล์ section
     ⇒ .vjcard:hover{transform:translateY(-4px)} ฯลฯ ทำงานได้ตามเดิม
     ทำแบบนี้แทนการมาเขียนค่า transform ของ hover ซ้ำที่นี่ด้วย !important
     เพราะค่าพวกนั้นเป็นของไฟล์ section ถ้า copy มาไว้สองที่ เดี๋ยวก็ไม่ตรงกัน
     (peg-card-fade ยังชื่อเดิมตำแหน่งเดิม จึงวิ่งต่อ ไม่รีสตาร์ท opacity ไม่กระพริบ) */
  html.peg-sfx .live__grid    > .vjcard:hover,
  html.peg-sfx .pk-vj-grid    > .pk-vj:hover,
  html.peg-sfx .pk-about-grid > .pk-pillar:hover,
  html.peg-sfx .pk-sell       > .pk-sell__item:hover,
  html.peg-sfx .pk-cond__list > .pk-cond__item:hover,
  html.peg-sfx .steps         > .step:hover,
  html.peg-sfx .rates         > .rate:hover,
  html.peg-sfx .perks         > .perk:hover {
    animation-name: peg-card-fade;
  }


  /* ------------------------------------------------------------
     2) ประกายทองกวาดผ่านหัวข้อ section

     เห็นที่: ทุก <h2> ที่มี <span class="gold-text"> ข้างใน —
     "VJ ที่กำลังไลฟ์ตอนนี้" (#live) · "VJ ในสังกัด" (#vjs) ·
     "เปิดรับสมัคร VJ LIVE" (#recruit) · "ขั้นตอนเติมเงิน" (#topup) ·
     "เรตโบนัสเติมเงิน" (#rates) · "ยศและของรางวัล" (#perks)

     *** ไม่แตะค่าสีเลยแม้แต่สต็อปเดียว ***
     .gold-text ใช้ --grad-gold-text ซึ่งโซนชมพูเป็น "ทองเข้ม" ตั้งใจให้ contrast ผ่าน
     (ดูคอมเมนต์ท้าย tokens.css — ทองสว่างบนพื้นชมพูได้ ~1.8:1 อ่านไม่ออก)
     ที่นี่แค่ "ยืด" gradient เดิมออกเป็น 300% แล้วเลื่อนตำแหน่ง
     ⇒ แถบสว่างที่มีอยู่แล้วกวาดผ่านตัวอักษร แต่ไม่มีเฉดไหนสว่างเกินของเดิม
     ห้ามเปลี่ยนไปประกาศ background-image ใหม่ที่นี่ เพราะจะทำให้โซนชมพู
     หลุดไปใช้ทองสว่างแล้วหัวข้ออ่านไม่ออกทันที
     ไม่ใช้ filter/blur ตามกฎข้อ 1 ของไฟล์นี้ */
  @keyframes peg-sheen {
    0%   { background-position: 88% 50%; }
    55%  { background-position: 12% 50%; }
    100% { background-position: 50% 50%; }
  }

  html.peg-sfx .section h2 .gold-text {
    background-size: 300% 100%;
    /* ค่าพักคือ 50% = แถบสว่างอยู่กลางตัวอักษร เท่ากับหน้าตาเดิมก่อนมีไฟล์นี้
       เผื่อกรณี timeline ไม่ทำงาน (เช่นถูกครอบด้วย scroll container ที่ไม่เลื่อน) */
    background-position: 50% 50%;
    animation-name: peg-sheen;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: view();
    /* ใช้ cover ไม่ใช่ entry — วัดแล้วเจอว่า entry ของ <span> เล็กๆ ยาวแค่
       "ความสูงของ span" ประมาณ 40px ⇒ ประกายกวาดจบภายในการเลื่อน 40px
       เร็วจนมองไม่ทัน (สแกนที่ 375px แล้วแทบไม่เคยจับค่ากลางทางได้เลย)
       cover ยาว = ความสูงจอ + ความสูง element ⇒ ช่วง 5%–50% ที่ 812px
       คิดเป็นการเลื่อนราว 380px กำลังพอดีกับตอนหัวข้อไต่ขึ้นกลางจอ */
    animation-range: cover 5% cover 50%;
  }


  /* ------------------------------------------------------------
     3) หัวข้อ section ขยับคนละจังหวะกับการ์ดข้างล่าง (ความลึก)

     เห็นที่: ส่วนหัวของทุก section — .live__head / .pk-head / .blue-head
     หัวข้อจะไหลขึ้นช้ากว่าการ์ดเล็กน้อยตลอดที่ section อยู่ในจอ
     เกิดเป็น parallax ระหว่าง "หัวเรื่อง" กับ "เนื้อหา"

     ทำไมไม่ใช้ [data-parallax] ที่มีอยู่แล้ว
     js/effects.js บรรทัด applyParallax() มี `if (y > innerHeight * 1.2) return;`
     คือหยุดคำนวณทันทีที่พ้น hero → ใช้กับ section ล่างไม่ได้ และแก้ไฟล์นั้นไม่ได้

     transform ล้วน ไม่มี opacity → ไม่ชนกับ .reveal ที่คุม opacity ของหัวข้ออยู่
     (fill backwards ด้วยเหตุผลเดียวกับข้อ 1) */
  @keyframes peg-drift {
    from { transform: translate3d(0, var(--sfx-drift), 0); }
    to   { transform: translate3d(0, calc(var(--sfx-drift) * -1), 0); }
  }

  html.peg-sfx .live__head,
  html.peg-sfx .pk-head,
  html.peg-sfx .blue-head {
    --sfx-drift: 10px;            /* มือถือ: เบาไว้ก่อน */
    animation-name: peg-drift;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: backwards;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }

  @media (min-width: 768px) {
    html.peg-sfx .live__head,
    html.peg-sfx .pk-head,
    html.peg-sfx .blue-head { --sfx-drift: 22px; }
  }


  /* ------------------------------------------------------------
     4) ★ ฉากหลังทั้งหน้าเคลื่อนเป็นชั้นๆ ตอนเลื่อน (งานหลักของไฟล์นี้)

     เห็นที่: ทุกที่ตั้งแต่พ้น hero ลงไป — #live · #vjs · #recruit · #about
     (โซนชมพู) และ #topup · #rates · #perks (โซนน้ำเงิน)
     ที่ hero มองไม่เห็นเพราะชั้นนี้ถูกไล่ opacity จาก 0 ขึ้นมา ดูเหตุผลข้างล่าง

     โครงสร้าง: js/scroll-fx.js แปะ <div class="sfx-bg"> เป็นลูกตัวแรกของ body
     ข้างในมี 5 ชั้น แต่ละชั้นเลื่อนคนละระยะ = parallax แนวตั้งของ "ท้องฟ้า"
       sfx-bg__stars-far   ดาวเล็กสลัว ขยับน้อยสุด (ชั้นไกลสุด)
       sfx-bg__mist        หมอกบาง
       sfx-bg__clouds      เมฆก้อนใหญ่ ขยับมาก (ชั้นใกล้)
       sfx-bg__sparks      ประกายทองลอยขึ้น ขยับมากสุด
       sfx-bg__glow        ก้อนแสงนวลที่ค่อยๆ กวาดทแยงจอ
       sfx-bg__ground      หมอก/ควันที่ก้นจอ โผล่เฉพาะช่วงท้ายหน้า (ชั้นหน้าสุด)

     *** ทำไมเป็น scroll(root) ไม่ใช่ view() ***
     view() วัดจาก "ตำแหน่งของ element เองเทียบกับจอ" ซึ่งใช้กับฉากหลังไม่ได้
     เพราะชั้นพวกนี้เป็น position:fixed ไม่เคยขยับเทียบกับจอเลย progress จะนิ่งตลอด
     scroll(root) วัดจาก "หน้าเลื่อนไปกี่ % ของทั้งเอกสาร" ซึ่งตรงกับที่ต้องการ

     *** ทำไมเมฆเป็น radial-gradient ไม่ใช่ filter:blur ***
     ลอกวิธีมาจาก .hero__cloud (css/hero.css บรรทัด 328-336) — radial ที่จางหาย
     เป็น transparent ให้ขอบนุ่มเท่า blur แต่ไม่ต้องคำนวณ blur ใหม่ทุกเฟรมตอนเมฆเลื่อน
     ซึ่งเป็นต้นเหตุแลคเดิมของโปรเจกต์นี้ ห้ามเปลี่ยนกลับไปใช้ blur เด็ดขาด

     *** กันไม่ให้เกิด horizontal scroll ***
     ตัวครอบ .sfx-bg เป็น position:fixed + overflow:hidden
     ของข้างในจึงล้นออกนอกจอไม่ได้ ไม่ว่าจะ translateX เท่าไหร่
     (แต่ละชั้นยังกางเกินขอบจอไว้ -5vw/-32vh เพื่อไม่ให้เห็นขอบว่างตอนเลื่อน
      ระยะ translate ทุกอันถูกคุมให้ต่ำกว่าระยะกางนี้เสมอ)

     *** ลำดับชั้น (ดู css/starfield.css) ***
     พื้นหลัง body → [.sfx-bg z-index:1] → canvas ดาวตามเมาส์ z-index:2
     → เนื้อหาใน .wrap z-index:3
     ห้ามยก z-index เกิน 1 เพราะจะไปบังดาวของ starfield.js
     ------------------------------------------------------------ */

  /* ชั้นฉากหลังจะโผล่ก็ต่อเมื่อพ้น hero แล้ว
     เหตุผล: .hero__sky (z-index:-1) อยู่ "ต่ำกว่า" ชั้นนี้ ถ้าไม่หรี่ไว้
     เมฆชุดนี้จะไปลอยทับปราสาท/ออโรร่า/ดาว 9 ดวง ที่ hero จัดฉากไว้แล้ว */
  @keyframes sfx-bg-in { from { opacity: 0; } to { opacity: 1; } }

  html.peg-sfx .sfx-bg {
    display: block;
    position: fixed;
    inset: 0;
    overflow: hidden;            /* หัวใจของการกัน horizontal scroll */
    pointer-events: none;
    z-index: 1;
    opacity: 0;
    animation-name: sfx-bg-in;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(root);
    animation-range: 20vh 110vh;

    /* สีของฉากหลัง — มาจาก tokens.css ทั้งหมด ผ่าน color-mix เท่านั้น
       โซนชมพูพื้นสว่าง: ดาวต้องเป็นทอง (ขาวบนชมพูอ่อนมองไม่เห็น) */
    --sfx-star:  color-mix(in srgb, var(--peg-gold) 78%, transparent);
    --sfx-spark: color-mix(in srgb, var(--peg-gold-deep) 70%, transparent);
    --sfx-mist:  color-mix(in srgb, var(--peg-white) 34%, transparent);
    --sfx-cloud: color-mix(in srgb, var(--peg-white) 52%, transparent);
    --sfx-glow:  color-mix(in srgb, var(--peg-gold-light) 26%, transparent);

    /* หมอกพื้นดิน — --ground-tint มาจาก css/backdrop.css (สลับค่าตามโซนที่นั่น)
       body = ตัวเนื้อหมอกที่ลอยคลุมพื้น · haze = ยอดหมอกที่ฟุ้งขึ้นข้างบน
       โซนชมพูพื้นสว่าง: ยอดหมอกต้อง "สว่างกว่าพื้น" ไม่งั้นอ่านเป็นเงาสกปรก */
       ⚠ ครีม 70% ไม่ใช่ 44% — ค่าแรกที่ลองคือ 44% แล้ว "มองไม่เห็นหมอกเลย"
       เพราะผสมครีมน้อยไป สีที่ได้เกือบเท่าพื้นดินข้างหลังพอดี (ต่างกันไม่ถึง
       10 ค่าต่อแชนแนล) หมอกต้องสว่างกว่าพื้นชัดเจนถึงจะอ่านออกว่าเป็นหมอก */
    --sfx-ground-body: color-mix(in srgb, var(--ground-tint) 58%, transparent);
    --sfx-ground-haze: color-mix(in srgb,
                         color-mix(in srgb, var(--peg-cream) 70%, var(--ground-tint))
                         50%, transparent);
  }

  /* โซนน้ำเงินพื้นเข้ม: ดาวกลับไปเป็นขาว เมฆบางลงมากไม่งั้นทึบเป็นแผ่น */
  html.peg-sfx[data-zone="blue"] .sfx-bg {
    --sfx-star:  color-mix(in srgb, var(--peg-white) 85%, transparent);
    --sfx-spark: color-mix(in srgb, var(--peg-gold-light) 80%, transparent);
    --sfx-mist:  color-mix(in srgb, var(--blue-ice) 14%, transparent);
    --sfx-cloud: color-mix(in srgb, var(--peg-white) 14%, transparent);
    --sfx-glow:  color-mix(in srgb, var(--blue-ice) 20%, transparent);

    /* โซนน้ำเงินพื้นเข้ม: ยอดหมอกผสมน้ำแข็งแทนครีม (ครีมบนนาวีอ่านเป็นสีเหลืองขุ่น)
       และเจือจางกว่าชมพู เพราะอะไรที่สว่างบนพื้นเข้มจะเด่นขึ้นมาเองอยู่แล้ว */
    --sfx-ground-body: color-mix(in srgb, var(--ground-tint) 70%, transparent);
    --sfx-ground-haze: color-mix(in srgb,
                         color-mix(in srgb, var(--blue-ice) 34%, var(--ground-tint))
                         42%, transparent);
  }

  html.peg-sfx .sfx-bg__l {
    position: absolute;
    /* กางเกินจอทุกด้าน เผื่อระยะ translate ข้างล่าง (สูงสุด 24vh / 3vw)
       ถ้าจะเพิ่มระยะ translate ต้องมาขยายตัวเลขตรงนี้ด้วย ไม่งั้นจะเห็นขอบว่าง */
    left: -5vw; right: -5vw; top: -32vh; bottom: -32vh;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(root);
    animation-range: 0% 100%;
  }

  /* ยิ่งชั้นไกล ยิ่งขยับน้อย = หลักการ parallax */
  @keyframes sfx-par-far    { from { transform: translate3d(0,      6vh, 0); } to { transform: translate3d(0,      -9vh, 0); } }
  @keyframes sfx-par-mist   { from { transform: translate3d(-2vw,   9vh, 0); } to { transform: translate3d(2vw,   -15vh, 0); } }
  @keyframes sfx-par-cloud  { from { transform: translate3d(2vw,   14vh, 0); } to { transform: translate3d(-2vw,  -24vh, 0); } }
  @keyframes sfx-par-spark  { from { transform: translate3d(0,     16vh, 0); } to { transform: translate3d(0,     -24vh, 0); } }
  @keyframes sfx-par-glow   { from { transform: translate3d(-3vw,  18vh, 0); } to { transform: translate3d(3vw,   -20vh, 0); } }

  /* ---- ดาวชั้นไกล: จุดเล็กๆ ปูเป็นตาราง 280×340 แล้วซ้ำไปทั้งชั้น ---- */
  html.peg-sfx .sfx-bg__stars-far {
    opacity: .55;
    background-image:
      radial-gradient(1.7px 1.7px at 14% 12%, var(--sfx-star) 0 58%, transparent 60%),
      radial-gradient(1.2px 1.2px at 63% 26%, var(--sfx-star) 0 58%, transparent 60%),
      radial-gradient(1.9px 1.9px at 38% 54%, var(--sfx-star) 0 58%, transparent 60%),
      radial-gradient(1.1px 1.1px at 87% 67%, var(--sfx-star) 0 58%, transparent 60%),
      radial-gradient(1.5px 1.5px at 24% 83%, var(--sfx-star) 0 58%, transparent 60%),
      radial-gradient(1.2px 1.2px at 73% 93%, var(--sfx-star) 0 58%, transparent 60%);
    background-size: 280px 340px;
    animation-name: sfx-par-far;
  }

  /* ---- หมอกบาง: วงรีกว้างจางๆ ---- */
  html.peg-sfx .sfx-bg__mist {
    background-image:
      radial-gradient(62% 20% at 12% 20%, var(--sfx-mist) 0%, transparent 72%),
      radial-gradient(54% 17% at 88% 48%, var(--sfx-mist) 0%, transparent 72%),
      radial-gradient(70% 24% at 42% 78%, var(--sfx-mist) 0%, transparent 72%);
    animation-name: sfx-par-mist;
  }

  /* ---- เมฆชั้นหน้า: ก้อนใหญ่ ทึบกว่าหมอก ขยับมากกว่า ---- */
  html.peg-sfx .sfx-bg__clouds {
    background-image:
      radial-gradient(38% 11% at 20% 30%,
        var(--sfx-cloud) 0%,
        color-mix(in srgb, var(--sfx-cloud) 55%, transparent) 46%,
        transparent 73%),
      radial-gradient(30% 9% at 80% 58%,
        var(--sfx-cloud) 0%,
        color-mix(in srgb, var(--sfx-cloud) 55%, transparent) 46%,
        transparent 73%),
      radial-gradient(44% 12% at 46% 88%,
        var(--sfx-cloud) 0%,
        color-mix(in srgb, var(--sfx-cloud) 50%, transparent) 46%,
        transparent 73%);
    animation-name: sfx-par-cloud;
  }

  /* ---- ประกายวิ้งลอยขึ้น: จุดทองห่างๆ ขยับขึ้นมากที่สุดในทุกชั้น ---- */
  html.peg-sfx .sfx-bg__sparks {
    opacity: .6;
    background-image:
      radial-gradient(2.6px 2.6px at 18% 22%, var(--sfx-spark) 0 52%, transparent 55%),
      radial-gradient(2.1px 2.1px at 71% 44%, var(--sfx-spark) 0 52%, transparent 55%),
      radial-gradient(3px   3px   at 43% 71%, var(--sfx-spark) 0 52%, transparent 55%),
      radial-gradient(2.2px 2.2px at 90% 88%, var(--sfx-spark) 0 52%, transparent 55%);
    background-size: 440px 520px;
    animation-name: sfx-par-spark;
  }

  /* ---- ก้อนแสงนวลกวาดทแยงจอ ---- */
  html.peg-sfx .sfx-bg__glow {
    background-image:
      radial-gradient(44% 24% at 50% 38%, var(--sfx-glow) 0%, transparent 74%);
    animation-name: sfx-par-glow;
  }

  /* ------------------------------------------------------------
     5) ★ หมอก/ควันพื้นดินที่ก้นจอ — "จุดเริ่มต้น" ที่ลูกค้าขอ

     โจทย์: เลื่อนลงไปเรื่อยๆ แล้วต้องรู้สึกว่าลงมาถึงพื้นหญ้า/ป่า
     แต่ "ห้ามเขียวโดด" และต้องสื่อด้วยหมอก ไม่ใช่ภาพต้นไม้

     แบ่งงานกับ backdrop.css ชัดเจน
       backdrop.css  = สี "พื้น" ของก้นเอกสาร (--grad-ground ยึดก้นเอกสาร นิ่ง)
       ที่นี่         = "หมอก" ที่ลอยอยู่หน้าพื้นนั้น (ยึดก้นจอ ขยับตามการเลื่อน)
     สองอย่างนี้ต้องมาคู่กัน ถ้ามีแต่หมอกจะลอยอยู่บนลาเวนเดอร์เฉยๆ ไม่เป็นพื้นดิน

     *** ทำไมยึดก้น "จอ" ไม่ใช่ก้นเอกสาร ***
     หมอกที่เกาะขอบล่างของจอตลอดเวลา = มุมกล้องที่กำลังร่อนลงต่ำ
     ยิ่งเลื่อนยิ่งหนา (sfx-ground-rise) จึงอ่านเป็น "ใกล้พื้นขึ้นเรื่อยๆ"
     ถ้ายึดก้นเอกสารจะกลายเป็นแผ่นนิ่งๆ ที่โผล่มาทีเดียวตอนสุดหน้า

     *** ทำไมไม่ใช้ blur ***
     เหตุผลเดียวกับเมฆ (ดูข้อ 4) — ขอบนุ่มทำด้วย radial ที่จางเป็น transparent
     ชั้นนี้ขยับทุกเฟรมตอนเลื่อน ถ้าใส่ blur คือคำนวณ blur ใหม่ทุกเฟรม = แลคเดิม

     *** เรื่องคอนทราสต์ของ footer ***
     .sfx-bg z-index:1 สูงกว่า .pegfoot (position:relative, z-index:auto)
     ⇒ หมอกลอย "ทับ" พื้น footer จริง (เนื้อหาใน .wrap z-index:3 ยังอยู่บนสุด)
     จึงตั้งจุดศูนย์กลางของเนื้อหมอกไว้ที่ 112% คือ "ใต้ขอบล่างของจอ"
     ส่วนที่เห็นบนจอจึงเป็นแค่หางที่จางแล้ว ไม่ใช่ใจกลางที่ทึบ
     ถ้าจะเพิ่มความทึบ ต้องไปวัดคอนทราสต์ข้อความใน footer ใหม่ทั้ง 2 โซน
     ------------------------------------------------------------ */
  @keyframes sfx-ground-rise { from { opacity: 0; } to { opacity: 1; } }
  /* ขยับน้อยกว่าทุกชั้น (10vh -> -4vh) ตั้งใจ — พื้นดินคือของที่ "อยู่กับที่"
     ถ้าขยับมากจะอ่านเป็นเมฆอีกก้อนหนึ่ง ไม่ใช่พื้น */
  @keyframes sfx-par-ground { from { transform: translate3d(0, 10vh, 0); } to { transform: translate3d(0, -4vh, 0); } }

  html.peg-sfx .sfx-bg__ground {
    /* ทับ inset ของ .sfx-bg__l — ชั้นนี้ไม่ต้องกางเต็มจอ ขอแค่แถบล่าง
       top:auto + height คู่กัน (ถ้าปล่อย top:-32vh ไว้ แถบจะสูงเต็มจอแล้ว
       ตำแหน่ง % ของ radial ข้างในจะเลื่อนไปอยู่กลางจอ ไม่ใช่ที่พื้น)
       left/right -5vw ยังได้จาก .sfx-bg__l และ .sfx-bg overflow:hidden
       ยังเป็นตัวกัน horizontal scroll อยู่เหมือนเดิม */
    top: auto;
    bottom: -10vh;
    height: 64vh;

    /* ชั้นบน = ยอดหมอกฟุ้งเป็นหย่อม (วางคนละตำแหน่งกันเพื่อไม่ให้เป็นแถบเรียบ)
       ชั้นล่าง = เนื้อหมอกคลุมพื้น ศูนย์กลางอยู่ใต้จอ */
    background-image:
      radial-gradient(46% 40% at 16% 62%, var(--sfx-ground-haze) 0%, transparent 70%),
      radial-gradient(52% 34% at 78% 74%, var(--sfx-ground-haze) 0%, transparent 72%),
      radial-gradient(128% 78% at 50% 112%,
        var(--sfx-ground-body) 0%,
        color-mix(in srgb, var(--sfx-ground-body) 46%, transparent) 44%,
        transparent 78%);

    /* 2 อนิเมชัน timeline เดียวกันแต่ "ช่วง" ต่างกัน จึงต้องเขียน
       animation-range ซ้ำที่นี่ (ค่าใน .sfx-bg__l เป็น 0% 100% ค่าเดียว)
       duration/timing/fill/timeline ค่าเดียวใน .sfx-bg__l ถูกใช้ซ้ำทั้งสองตัวเอง
       ตามกฎ list ของ CSS animation — ไม่ต้องเขียนใหม่

       52% 100% = เริ่มจางเข้ามาเมื่อเลื่อนผ่านครึ่งหน้า ทึบเต็มที่ตอนสุดหน้า
       ไม่เริ่มเร็วกว่านี้เพราะครึ่งบนของหน้ายังเป็น "ฟ้า" อยู่ */
    animation-name: sfx-par-ground, sfx-ground-rise;
    animation-range: 0% 100%, 52% 100%;
  }

  /* มือถือเหลือ 3 ชั้น (ดาวไกล · เมฆ · แสงนวล)
     traffic 100% มาจากมือถือ และแต่ละชั้นคือ texture เต็มจอหนึ่งผืนใน GPU
     ตัดหมอกกับประกายออกก่อนเพราะเป็นสองชั้นที่สังเกตเห็นยากที่สุดบนจอเล็ก
     เมฆเก็บไว้ทุกขนาดจอ เพราะเป็นตัวที่เข้าธีมปราสาทลอยฟ้าที่สุด

     ★ sfx-bg__ground ไม่อยู่ในรายการที่ตัด — มันคือของที่ลูกค้าสั่งมาตรงๆ
     ("ส่วนสุดท้ายให้เป็นพื้นหญ้า") ตัดบนมือถือคือตัดงานทิ้ง
     ชดเชยด้วยการให้มันสูงแค่ 64vh ไม่ใช่เต็มจอ และมีแค่ 3 radial */
  html.peg-sfx .sfx-bg__mist,
  html.peg-sfx .sfx-bg__sparks { display: none; }

  @media (min-width: 768px) {
    html.peg-sfx .sfx-bg__mist,
    html.peg-sfx .sfx-bg__sparks { display: block; }
  }

}  /* /@supports */


/* ==========================================================
   prefers-reduced-motion — ต้องเขียนเอง อย่าลบ

   kill switch กลางใน css/base.css ใช้ `animation-duration:.01ms!important`
   ซึ่งใช้ไม่ได้กับ scroll-driven animation เพราะอนิเมชันพวกนี้ขับด้วย
   "ตำแหน่งการเลื่อน" ไม่ใช่เวลา — duration ถูกเมิน มันจะยังขยับอยู่ดี
   จึงต้องสั่ง animation:none ตรงๆ ที่นี่

   js/scroll-fx.js ก็ถอด class peg-sfx ให้อีกชั้นหนึ่ง แต่บล็อกนี้ต้องยืนได้เอง
   เผื่อ JS ไม่รัน (เว็บนี้เปิดด้วย file:// บางเครื่องบล็อกสคริปต์)
   จึงไม่มี .peg-sfx นำหน้า — ให้ผลแม้ class ยังค้างอยู่
   ========================================================== */
@media (prefers-reduced-motion: reduce) {
  .live__grid    > .vjcard,
  .pk-vj-grid    > .pk-vj,
  .pk-about-grid > .pk-pillar,
  .pk-sell       > .pk-sell__item,
  .pk-cond__list > .pk-cond__item,
  .steps         > .step,
  .rates         > .rate,
  .perks         > .perk,
  .live__head,
  .pk-head,
  .blue-head {
    /* สั่งแค่ animation:none พอ — ไม่สั่ง transform:none!important
       เพราะ !important จะไปทับ :hover ของการ์ด (.pk-vj:hover ฯลฯ) ด้วย
       ผู้ใช้ reduced-motion ก็ยังควรเห็นว่ากดการ์ดได้ ส่วนท่าเริ่มต้นของ .reveal
       ถูก base.css คืนค่าเป็น transform:none ให้อยู่แล้วในโหมดนี้ */
    animation: none !important;
  }

  .section h2 .gold-text {
    animation: none !important;
    background-size: 100% 100% !important;   /* คืนค่า default ของ gradient */
    background-position: 0 0 !important;
  }

  /* ฉากหลังเคลื่อนไหวทั้งชุด: ซ่อนทิ้งไปเลย ไม่ใช่แค่หยุด
     เพราะมันเป็นของตกแต่งล้วน ถ้าหยุดเฉยๆ จะค้างอยู่ที่เฟรมแรกแบบไม่ได้ตั้งใจ
     ซ่อนแล้วได้พื้นหลัง --grad-page เดิมของ backdrop.css กลับมาเต็มๆ */
  .sfx-bg {
    display: none !important;
    animation: none !important;
  }
  /* .sfx-bg__ground เขียนซ้ำแม้จะมี class .sfx-bg__l อยู่แล้ว — ตั้งใจ
     เพื่อให้คนที่มาอ่านบล็อกนี้เห็นว่าหมอกพื้นดินถูกคุมด้วย ไม่ต้องไปไล่หาว่า
     ชั้นใหม่ที่เพิ่มเข้ามาตกหล่นหรือเปล่า (เพิ่มชั้นใหม่ครั้งหน้าให้เติมที่นี่ด้วย)
     ส่วน --grad-ground ใน backdrop.css ไม่ต้องปิด — มันเป็นสีพื้นที่นิ่งอยู่แล้ว
     ไม่ใช่การเคลื่อนไหว ผู้ใช้ reduced-motion ก็ยังควรเห็นว่าก้นหน้าเป็นพื้นดิน */
  .sfx-bg__l,
  .sfx-bg__ground { animation: none !important; transform: none !important; }
}
