Files
moreminimore-astroreal/src/pages/faq.astro
Kunthawat Greethong b5be45bcd6 feat(pages): Bento Grid redesign across all pages
Apply Bento Grid + decorative parallax orb system from about.astro
to all remaining pages (home, services, portfolio, faq, contact,
terms, privacy, blog list, blog detail, services detail).

Layout changes (consistent across all pages):
- Main content sections use <BentoGrid> + <BentoTile>
- 2-3 <DecoOrb> per page for decorative parallax (no parallax blobs
  behind text — orbs are pure decoration, z-index: 0, pointer-events: none)
- Surface variants: white, soft, yellow, purple-soft, teal, mint, dark
  (rotated across pages for visual variety)
- Asymmetric span strategy (8+4, 7+5, 6+6) instead of flat grids
- Process sections use clean 4x1 grid
- Pull quote + yellow CTA kept as-is (standalone sections)

Content rules preserved:
- All Thai content kept verbatim
- No fabricated statistics
- No emoji icons (use text numerals 01 02 03 04)
- All design tokens from global.css (no hardcoded hex)
- Existing global classes (.container, .section, .btn, .section-badge,
  .section-title, .cta-section, etc.) reused — no design system break

Build verified: 22 pages built in 1.80s, no errors.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-08 23:30:48 +07:00

325 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import Base from '../layouts/Base.astro';
import Navigation from '../components/Navigation.astro';
import Footer from '../components/Footer.astro';
import PageHero from '../components/PageHero.astro';
import BentoGrid from '../components/BentoGrid.astro';
import BentoTile from '../components/BentoTile.astro';
import DecoOrb from '../components/DecoOrb.astro';
import { getCollection } from 'astro:content';
const faqItems = await getCollection('faq');
const categories = ['บริการ', 'ราคา', 'ระยะเวลา', 'AI & เทคนิค', 'หลังการขาย'];
// Group FAQ items by category (preserve original order within each category)
const groupedFaq = categories
.map(cat => ({
category: cat,
items: faqItems.filter(f => f.data.category === cat),
}))
.filter(g => g.items.length > 0);
// Split each category's items into chunks of 2 (or 3 for the last chunk if odd count)
function chunkItems<T>(arr: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
// Pre-compute all FAQ tiles with assigned spans + surfaces for visual rhythm
// Rotation: yellow, soft, purple-soft, mint, teal, dark, coral, purple, mint
const surfaceRotation = ['yellow', 'soft', 'purple-soft', 'mint', 'teal', 'dark', 'coral', 'purple', 'mint'] as const;
// Span strategy for category tile pairs: 7+5, 5+7, 8+4, 4+8, 7+5 ... avoid 6+6
const spanPairs = [[7, 5], [5, 7], [8, 4], [4, 8], [7, 5]] as const;
interface FaqTile {
category: string;
items: typeof faqItems;
surface: typeof surfaceRotation[number];
span: 4 | 5 | 7 | 8;
tileIndex: number; // 0, 1 within category
isFirst: boolean;
isLast: boolean;
}
const faqTiles: FaqTile[] = [];
groupedFaq.forEach((group, gIdx) => {
const chunks = chunkItems(group.items, 2);
const pair = spanPairs[gIdx % spanPairs.length];
chunks.forEach((chunk, cIdx) => {
const tileSurface = surfaceRotation[faqTiles.length % surfaceRotation.length];
faqTiles.push({
category: group.category,
items: chunk,
surface: tileSurface,
span: (pair[cIdx] ?? 6) as 4 | 5 | 7 | 8,
tileIndex: cIdx,
isFirst: cIdx === 0,
isLast: cIdx === chunks.length - 1,
});
});
});
---
<Base title="คำถามที่พบบ่อย | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
badge="FAQ"
title="คำถามที่ลูกค้าถามบ่อยที่สุด"
subtitle="30+ คำถามที่รวบรวมจากแชต LINE จริง ๆ ไม่ใช่แต่งขึ้นเอง"
/>
<!-- FAQ CATEGORIES (BENTO) -->
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', left: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', right: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
{faqTiles.map((tile) => (
<BentoTile
span={tile.span}
surface={tile.surface}
eyebrow={tile.isFirst ? tile.category : `${tile.category} · ต่อ`}
>
<div class="faq-list">
{tile.items.map((item) => (
<details class="faq-item">
<summary class="faq-question">
<span class="question-text">{item.data.question}</span>
<span class="faq-toggle">+</span>
</summary>
<div class="faq-answer">
<p>{item.data.answer}</p>
</div>
</details>
))}
</div>
</BentoTile>
))}
<!-- OTHER TOPICS — full-width tile with tag cloud -->
<BentoTile span={12} surface="soft" eyebrow="เรื่องอื่น ๆ" title="คำถามอื่น ๆ ที่ลูกค้าถามบ่อย">
<div class="tag-cloud">
<span class="topic-tag">โฮสติ้ง</span>
<span class="topic-tag">โดเมน</span>
<span class="topic-tag">SSL</span>
<span class="topic-tag">ใบเสนอราคา</span>
<span class="topic-tag">ใบกำกับภาษี</span>
<span class="topic-tag">สัญญา</span>
<span class="topic-tag">NDA</span>
<span class="topic-tag">ลิขสิทธิ์งาน</span>
<span class="topic-tag">ทีมงาน</span>
<span class="topic-tag">ขนาดทีม</span>
<span class="topic-tag">ที่ตั้งบริษัท</span>
<span class="topic-tag">ตัวอย่างงาน</span>
<span class="topic-tag">ขอดูเว็บจริง</span>
<span class="topic-tag">นัดคุยนอกสถานที่</span>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- QUICK CHANNELS (BENTO) -->
<section class="section section-bento">
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', right: '20%' }} opacity={0.2} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', left: '-100px' }} opacity={0.3} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="section-header reveal">
<span class="section-badge">ช่องทางติดต่อ</span>
<h2 class="section-title">ไม่เจอคำตอบ? <span class="highlight">ถามตรง ๆ เลย</span></h2>
</div>
<BentoGrid>
<BentoTile span={4} surface="yellow" eyebrow="LINE" title="@moreminimore">
<p>คนที่อยากคุยเร็ว ๆ แบบเป็นกันเอง</p>
<p><strong>ตอบใน 30 นาที (เวลาทำการ)</strong></p>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-dark" style="margin-top: 16px;">ทักเลย →</a>
</BentoTile>
<BentoTile span={4} surface="soft" eyebrow="โทร" title="080-995-5945">
<p>คนที่อยากคุยยาว ๆ 510 นาที ถามตอบสด</p>
<p><strong>จ-ศ 09:00-18:00</strong></p>
<a href="tel:0809955945" class="btn btn-dark" style="margin-top: 16px;">โทรเลย →</a>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="Email" title="contact@moreminimore.com">
<p>คนที่อยากส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
<p><strong>ตอบภายใน 1 วัน</strong></p>
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 16px;">ส่งอีเมล →</a>
</BentoTile>
</BentoGrid>
</div>
</section>
<section class="section section-yellow cta-section">
<div class="container">
<div class="cta-content reveal">
<h2 class="cta-title">พร้อมคุยรายละเอียด?</h2>
<p class="cta-desc">นัดปรึกษาฟรี 30 นาที ผ่าน Zoom หรือนัดเจอที่ออฟฟิศ (กรุงเทพ/สมุทรสาคร)</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">นัดปรึกษา →</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">ทัก LINE ตอนนี้</a>
</div>
</div>
</div>
</section>
<Footer />
</Base>
<style>
.section-bento {
position: relative;
overflow: hidden;
}
/* FAQ inside BentoTile */
.faq-list { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.faq-item {
background: var(--color-white);
border-radius: var(--radius-md);
border: 1px solid var(--color-gray-200);
overflow: hidden;
transition: all 0.3s ease;
}
.faq-item[open] { border-color: var(--color-primary); }
.faq-item:hover { box-shadow: var(--shadow-sm); }
/* When inside a yellow surface, change item background to be visible */
.surface-yellow .faq-item { background: var(--color-white); }
.surface-yellow .faq-item[open] { border-color: var(--color-black); }
.faq-question {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 18px;
cursor: pointer;
list-style: none;
transition: background 0.2s ease;
}
.faq-question::-webkit-details-marker { display: none; }
.faq-question:hover { background: var(--color-bg-alt); }
.question-text {
flex: 1;
font-family: var(--font-display);
font-size: 15px;
font-weight: 700;
color: var(--color-black);
padding-right: 16px;
}
.faq-toggle {
font-size: 22px;
font-weight: 300;
color: var(--color-primary-dark);
flex-shrink: 0;
line-height: 1;
}
.faq-item[open] .faq-toggle { transform: rotate(45deg); }
.faq-answer {
padding: 0 18px 16px;
}
.faq-answer p {
font-size: 14px;
line-height: 1.7;
color: var(--color-gray-700);
white-space: pre-line;
}
/* Tag cloud */
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.topic-tag {
display: inline-block;
padding: 8px 16px;
background: var(--color-white);
color: var(--color-gray-700);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-full);
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
}
.topic-tag:hover {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-black);
}
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 16px;
}
.section-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
line-height: 1.15;
color: var(--color-black);
margin-bottom: 16px;
}
.section-title .highlight { color: var(--color-primary-dark); }
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
/* CTA */
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
.cta-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
color: var(--color-black);
margin-bottom: 16px;
}
.cta-desc {
font-size: 18px;
color: rgba(0, 0, 0, 0.7);
margin-bottom: 32px;
}
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
}
@media (max-width: 640px) {
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
.faq-question { padding: 14px 16px; }
.question-text { font-size: 14px; }
}
</style>
<script>
// Parallax orbs (use data-parallax-speed from DecoOrb)
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
function updateParallax() {
const scrolled = window.scrollY;
parallaxEls.forEach(el => {
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
const ty = scrolled * speed * -0.3;
el.style.transform = `translate3d(0, ${ty}px, 0)`;
});
}
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
</script>