Compare commits

12 Commits

Author SHA1 Message Date
Kunthawat Greethong
a1b1b16288 feat(portfolio): move what_we_did to hover overlay + show 4 specific clients
Per user spec:
1. Home portfolio now shows only 4 specific clients in order:
   Dataroot → Jet Industries → ทวนทอง 99 → สำนักงานกฎหมาย ตถาตา
   (was filtering by featured: true, now filters by slug)

2. PortfolioCard: what_we_did moved from always-visible to
   yellow hover overlay (shown above "เยี่ยมชมเว็บไซต์" button).
   All cards now have uniform layout:
   - Default: image + name + industry + category + result
   - On hover (desktop): overlay shows what_we_did text + visit button
   - No hover effect on mobile (tap navigates to URL)

3. Added what_we_did + result to 3 cards that were missing them:
   - Jet Industries, ทวนทอง 99, สำนักงานกฎหมาย ตถาตา

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-10 09:55:50 +07:00
Kunthawat Greethong
0ff6ae9020 content(home): simplify hero copy to 3 punchy lines
- badge: "เราจะช่วยคุณเพิ่มกำไร" → "Moreminimore"
- title: "เราช่วยวางระบบงาน..." → "เราจะช่วยคุณเพิ่มกำไร"
- subtitle: "รับทำเว็บ ที่ปรึกษา..." → "เราช่วยวางระบบงาน และใช้สถิติวางกลยุทธ์ทางการตลาด"

Update Hero.astro default props to match so other pages
that consume <Hero /> without props get the same copy.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 21:17:16 +07:00
Kunthawat Greethong
f47949c4b3 content(home): X1+X2 hero text + 2-col hero with pain stack + 2x2 services
Per user spec, restructured home page:

1. Hero copy (X1 + X2):
   - badge: "เราจะช่วยคุณเพิ่มกำไร"
   - title: "เราช่วยวางระบบงาน และใช้สถิติวางกลยุทธ์ทางการตลาด"
   - subtitle unchanged
   - replaced old "เว็บขายไม่ได้ โฆษณาเปลือง..." headline

2. Hero 2-column layout (text | pain stack):
   - Left 50%: badge, title, accent line, subtitle, CTA
   - Right 50%: 4 pain cards in vertical stack with colored
     surfaces (yellow, purple-soft, mint, teal) — staggered
     fade-in animation, each card has eyebrow "คุณกำลังเจอปัญหา"
     + pain question
   - Pain cards on right: "ยิ่งขาย กำไรยิ่งลด?", "มีเว็บไซต์
     เหมือนไม่มี?", "พนักงานทำงานได้น้อยกว่าที่ต้องการ?",
     "เอา AI มาให้ใช้ แต่งานไม่ได้มากขึ้นตามที่คิด?"
   - Responsive: stacks vertically on mobile (<1024px)
   - Pain card surfaces match bento tile vocabulary

3. Services section: 4x1 → 2x2 layout:
   - Each tile now span 6 (6+6 per row, 2 rows)
   - 4 tiles total: AI Consult (yellow), Automation (purple-soft),
     Marketing (mint), Web (soft)

4. Section title (X2):
   - "เริ่มจากอันที่ปวดที่สุด ค่อยขยายไปอันอื่น"
     → "เลือกบริการที่ตรงกับปัญหาของคุณ"

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 21:13:43 +07:00
Kunthawat Greethong
c92d446ff7 content(home+services): 19 text updates per user spec
Home (index.astro) updates:
- Section title: "มีวิธีแก้ที่เจาะจง" → "มีวิธีแก้ที่ต่างกัน"
- Problem 1 (โฆษณา): symptom "งบหมดไปกับคนที่ไม่ซื้อ" → "แต่คนที่ไม่ซื้อ"
- Problem 1 (โฆษณา): fix expanded with "ถ้ายังไม่มีการวางระบบเก็บข้อมูล..." caveat
- Problem 2 (เว็บ): symptom removed "ตะกร้าค้าง"
- Problem 2 (เว็บ): fix expanded with conditional "กรณีที่เว็บไม่มีโค้ดเก็บสถิติ..."
- Problem 3 (งานซ้ำ): title "ใช้เวลาคนเป็นชั่วโมง" → "เสียเวลาเป็นชั่วโมง"
- Problem 3 (งานซ้ำ): cause "ระบบเก่า" → "ระบบไม่มีการเชื่อม"
- Problem 3 (งานซ้ำ): fix expanded with "ลดเวลาจากชั่วโมงเป็นนาที"
- Problem 4 (AI): title expanded with cost context
- Problem 4 (AI): cause expanded with "พนักงานไม่เข้าใจ..."
- Problem 4 (AI): fix expanded with "วางระบบ AI Agent ให้มี skill"
- CTA: "ถาม 5 ข้อ...ทำได้หรือทำไม่ได้" → "แนะนำแนวทางเบื้องต้น...อะไรควรทำหรือไม่ควรทำ"

Service tile eyebrows (home):
- AI Consult → ที่ปรึกษาด้าน AI
- Automation Consult → วางระบบ Automation
- Marketing Consult → ที่ปรึกษาการตลาดออนไลน์
- Web Development → พัฒนาเว็บไซต์

Service tile subtitles + objectives (home) — per spec.

Service MDX content (affects service detail page + services/index):
- ai-consult-new.mdx: badge "AI Consult" → "ที่ปรึกษาด้าน AI"
- automation-new.mdx: badge "Automation Consult" → "วางระบบ Automation"
- marketing-new.mdx: badge "Marketing Consult" → "ที่ปรึกษาการตลาดออนไลน์", title → "Online Marketing Consult"
- webdev-new.mdx: badge "Web Development" → "พัฒนาเว็บไซต์"

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 20:52:51 +07:00
Kunthawat Greethong
bd1c979f1a fix(home): remove <a> wrapper around bento-tile (was breaking grid)
The home service section had each bento-tile wrapped in an
<a href="/services/..."> tag. This caused two problems:

1. The <a> became the grid item instead of the .bento-tile,
   so the .bento-tile's grid-column: span 3 was ignored.
   The <a> defaulted to display: block and stretched to
   100% width, so all 4 tiles in a row had the same wide
   width — but the grid was no longer dividing them equally.

2. Astro's scoped CSS only applies to elements with the
   matching data-astro-cid attribute. The <a> wrapper had
   its own scope, and the .bento-grid > a selector wasn't
   styled — so the grid layout didn't propagate correctly.

Fix: remove the <a> wrapper. Make the whole tile clickable
by adding an absolutely-positioned <a class="tile-link-overlay">
inside the tile (covers the entire tile, sits behind text via
z-index). The .bento-tile is now a direct child of .bento-grid
and grid-column: span 3 works as expected.

Result: 4 tiles width 3/12 each, exactly filling one row,
clickable from anywhere on the tile.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 20:00:52 +07:00
Kunthawat Greethong
9e7d27c03c fix(bento): add global !important override for tile width
Scoped CSS in BentoTile.astro was supposed to enforce
min-width: 0 + width: 100%, but the tiles were still
rendering at different widths in the same row.

Adding a global !important override in global.css that
applies to ALL .bento-tile instances regardless of
scoping, plus a flex column layout so tile-body fills
available space and pushes the CTA to the bottom.

This guarantees equal width + equal height in the row.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 18:31:05 +07:00
Kunthawat Greethong
b49931a87a fix(bento): force equal-width tiles via min-width: 0
Tiles in the same row had different widths because grid items
default to min-width: auto, which makes them grow to fit their
intrinsic content width instead of dividing the row equally.

Add min-width: 0, width: 100%, and box-sizing: border-box to
bento-tile so all tiles in a row are exactly equal width.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 14:19:18 +07:00
Kunthawat Greethong
525dc358a3 fix(layout): widen container to 1600px for more usable space
--container-max was 1400px which left visible whitespace on
screens 1600px+ wide. Bump to 1600px so service/problem bento
tiles fill more of the viewport.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 13:42:18 +07:00
Kunthawat Greethong
43f609a794 fix(home): equal-height service tiles + dedupe services
Service section on home page had two issues:
1. Tiles were different heights (longer content = taller tile)
   because .bento-tile had no min-height
2. Two service tiles showed the same title (AI Consult + Automation
   Consult) because src/content/services/ has 4 old + 4 -new mdx
   files; .slice(0, 4) grabbed the first 4 alphabetically which
   contained duplicate base slugs

Fixes:
- Add min-height: 380px + flex column to BentoTile so all tiles
  in a row are visually equal regardless of content length
- Add dedupedServices helper in index.astro that groups services
  by base slug and prefers the -new version when both exist
- Use dedupedServices.slice(0, 4) instead of services.slice(0, 4)

Result: 4 unique services (ai-consult-new, automation-new,
marketing-new, webdev-new) at equal height, filling the row.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 13:40:42 +07:00
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
Kunthawat Greethong
789473271e feat(about): Bento Grid layout + 7 content updates
Layout redesign:
- Add BentoGrid, BentoTile, DecoOrb components
- Add accent palette (purple/teal/mint/coral) to global tokens
- Rewrite about.astro using Bento Grid (12-col, surface variants)
- Decorative parallax blur orbs in 4 sections
- Value/process tiles use numerals 01-04 instead of emoji

Content updates per user feedback:
- Remove "Soloprenuer" emphasis
- Remove TOC ("เนื้อหาในหน้านี้") — not needed for short content
- Outsource + bot acknowledged as part of approach
- Replace AI stack with marketing intelligence tools
  (ad spy, SEO gap, trend tracking)
- No fabricated stat numbers — placeholders show "—"
  (only "5+ ปี" kept as verifiable, founded 2020)
- Post-delivery: small changes free, charge only for new features
- No Sprint methodology — flexible timeline, demo when ready

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-08 19:06:55 +07:00
Kunthawat Greethong
ca7f99ed41 fix(about): values grid 2x2 layout + Astro v6 content API
- about.astro: change .values-grid from 4 columns to 2x2 (max-width 900px)
  for visual balance with 4 cards. Drop the duplicate 2-col breakpoint
  that became redundant after the desktop change.
- about.astro: switch from deprecated entry.render() to render(entry)
  to match Astro v6 content collection API.
- content.config.ts: pages collection glob pattern '**/*.{md,mdx}'
  silently matched zero files when the collection only had root-level
  files; switch to '*.{md,mdx}' so the loader actually picks up
  about.md and home.md.
2026-06-08 00:21:05 +07:00
26 changed files with 2959 additions and 2159 deletions

View File

@@ -0,0 +1,39 @@
---
/**
* BentoGrid — 12-column asymmetric bento grid container.
* Use <BentoTile> as children.
*
* Example:
* <BentoGrid>
* <BentoTile span={6} surface="yellow">...</BentoTile>
* <BentoTile span={4} surface="purple-soft">...</BentoTile>
* </BentoGrid>
*/
---
<div class="bento-grid stagger">
<slot />
</div>
<style>
.bento-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-rows: minmax(80px, auto);
gap: 16px;
position: relative;
}
@media (max-width: 1024px) {
.bento-grid {
grid-template-columns: repeat(6, 1fr);
}
}
@media (max-width: 640px) {
.bento-grid {
grid-template-columns: 1fr;
gap: 12px;
}
}
</style>

View File

@@ -0,0 +1,168 @@
---
/**
* BentoTile — a single bento grid cell.
*
* Props:
* span: 3 | 4 | 5 | 6 | 7 | 8 | 12 (default 6)
* rows: 1 | 2 | 3 (default 1)
* surface: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'mint' | 'dark' | 'coral'
* minHeight: optional inline min-height CSS value
* eyebrow: optional small uppercase label above title
* title: optional H2 title
* reveal: boolean, animate on scroll into view (default true)
*
* Example:
* <BentoTile span={8} surface="yellow" eyebrow="วิธีทำงาน" title="ไม่ได้ทำงานแบบเดียวกับทุกที่">
* <p>...content...</p>
* </BentoTile>
*/
interface Props {
span?: 3 | 4 | 5 | 6 | 7 | 8 | 12;
rows?: 1 | 2 | 3;
surface?: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'mint' | 'dark' | 'coral';
minHeight?: string;
eyebrow?: string;
title?: string;
reveal?: boolean;
class?: string;
}
const {
span = 6,
rows = 1,
surface = 'white',
minHeight,
eyebrow,
title,
reveal = true,
class: className = '',
} = Astro.props;
const spanClass = `span-${span}`;
const rowsClass = rows > 1 ? `rows-${rows}` : '';
const surfaceClass = `surface-${surface}`;
const revealClass = reveal ? 'reveal' : '';
---
<div
class:list={['bento-tile', spanClass, rowsClass, surfaceClass, revealClass, className]}
style={minHeight ? `min-height: ${minHeight};` : undefined}
>
{eyebrow && <div class:list={['tile-eyebrow', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'inv' : '']}>{eyebrow}</div>}
{title && <h2 class:list={['tile-title', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'light' : '']}>{title}</h2>}
<div class="tile-body">
<slot />
</div>
</div>
<style>
.bento-tile {
background: var(--color-white);
color: var(--color-black);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
position: relative;
overflow: hidden;
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease;
transform-style: preserve-3d;
min-height: 380px;
min-width: 0;
width: 100% !important;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.bento-tile:hover {
transform: translateY(-4px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
}
/* Spans */
.span-3 { grid-column: span 3; }
.span-4 { grid-column: span 4; }
.span-5 { grid-column: span 5; }
.span-6 { grid-column: span 6; }
.span-7 { grid-column: span 7; }
.span-8 { grid-column: span 8; }
.span-12 { grid-column: span 12; }
.rows-2 { grid-row: span 2; }
.rows-3 { grid-row: span 3; }
@media (max-width: 1024px) {
.span-3, .span-4, .span-5 { grid-column: span 3; }
.span-6, .span-7, .span-8 { grid-column: span 6; }
.span-12 { grid-column: span 6; }
}
@media (max-width: 640px) {
[class*="span-"] { grid-column: span 1; }
.rows-2, .rows-3 { grid-row: span 1; }
}
/* Surface variants */
.surface-soft { background: var(--color-bg-soft); border-color: var(--color-gray-200); }
.surface-yellow { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-black); }
.surface-purple { background: var(--color-purple); border-color: var(--color-purple); color: var(--color-white); }
.surface-purple-soft { background: var(--color-purple-soft); border-color: var(--color-purple-soft); }
.surface-teal { background: var(--color-teal); border-color: var(--color-teal); color: var(--color-white); }
.surface-mint { background: var(--color-mint-soft); border-color: var(--color-mint-soft); }
.surface-coral { background: var(--color-coral); border-color: var(--color-coral); color: var(--color-white); }
.surface-dark { background: var(--color-black); border-color: var(--color-black); color: var(--color-white); }
/* Typography */
.tile-eyebrow {
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
opacity: 0.7;
margin-bottom: 12px;
}
.tile-eyebrow.inv {
opacity: 1;
color: var(--color-primary);
}
.tile-title {
font-family: var(--font-display);
font-size: clamp(22px, 3vw, 36px);
font-weight: 900;
line-height: 1.1;
margin-bottom: 16px;
}
.tile-title.light { color: var(--color-white); }
.tile-body { font-size: 16px; line-height: 1.7; }
.tile-body :global(p) { margin-bottom: 12px; }
.tile-body :global(p:last-child) { margin-bottom: 0; }
.tile-body :global(ul) { padding-left: 20px; margin-top: 12px; }
.tile-body :global(li) { margin-bottom: 8px; }
.tile-body :global(strong) { font-weight: 800; }
/* Light text on dark/colored surface */
.surface-dark .tile-body,
.surface-purple .tile-body,
.surface-teal .tile-body,
.surface-coral .tile-body {
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.surface-dark .tile-body :global(strong),
.surface-purple .tile-body :global(strong),
.surface-teal .tile-body :global(strong),
.surface-coral .tile-body :global(strong) { color: var(--color-primary); }
/* Yellow tile — always black text */
.surface-yellow .tile-title,
.surface-yellow .tile-body,
.surface-yellow .tile-body :global(strong) { color: var(--color-black); }
.surface-yellow .tile-eyebrow { color: var(--color-black); opacity: 0.7; }
/* Checkmark list variant (for .surface-yellow with .checklist) */
.tile-body :global(.checklist) {
list-style: none;
padding: 0;
}
.tile-body :global(.checklist li) {
padding-left: 0;
}
</style>

View File

@@ -0,0 +1,68 @@
---
/**
* DecoOrb — decorative parallax blur orb for section backgrounds.
* Pure decoration, never blocks clicks, always behind content.
*
* Props:
* color: 'yellow' | 'soft' | 'purple' | 'mint' | 'teal' (default 'yellow')
* size: CSS dimension (default '400px')
* speed: parallax speed 0.01.0 (default 0.4)
* position: { top?, right?, bottom?, left? } CSS positions
* blur: CSS blur value (default '60px')
* opacity: CSS opacity 01 (default 0.5)
*
* Example:
* <DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-100px', left: '-100px' }} />
*/
interface Props {
color?: 'yellow' | 'soft' | 'purple' | 'mint' | 'teal';
size?: string;
speed?: number;
position?: { top?: string; right?: string; bottom?: string; left?: string };
blur?: string;
opacity?: number;
}
const {
color = 'yellow',
size = '400px',
speed = 0.4,
position = {},
blur = '60px',
opacity = 0.5,
} = Astro.props;
const styleStr = [
`width: ${size};`,
`height: ${size};`,
`filter: blur(${blur});`,
`opacity: ${opacity};`,
position.top ? `top: ${position.top};` : '',
position.right ? `right: ${position.right};` : '',
position.bottom ? `bottom: ${position.bottom};` : '',
position.left ? `left: ${position.left};` : '',
].join(' ');
---
<div
class:list={['deco-orb', `orb-${color}`]}
data-parallax-speed={speed}
style={styleStr}
aria-hidden="true"
></div>
<style>
.deco-orb {
position: absolute;
border-radius: 50%;
pointer-events: none;
z-index: 0;
will-change: transform;
}
.orb-yellow { background: radial-gradient(circle, var(--color-primary) 0%, transparent 70%); }
.orb-soft { background: radial-gradient(circle, var(--color-primary-soft) 0%, transparent 70%); }
.orb-purple { background: radial-gradient(circle, var(--color-purple) 0%, transparent 70%); }
.orb-mint { background: radial-gradient(circle, var(--color-mint) 0%, transparent 70%); }
.orb-teal { background: radial-gradient(circle, var(--color-teal) 0%, transparent 70%); }
</style>

View File

@@ -15,12 +15,18 @@ interface Props {
}
const {
badge = "AI AGENCY ในประเทศไทย",
title = "เว็บไซต์ที่ขายได้ ไม่ใช่แค่สวย",
subtitle = "เราออกแบบเว็บไซต์ + AI Chatbot + Marketing Automation ให้ธุรกิจไทย เพิ่มยอดขายเฉลี่ย 24 เท่า ภายใน 6 เดือน ด้วยงบที่จับต้องได้ เริ่มต้น 15,000 บาท",
badge = 'Moreminimore',
title = 'เราจะช่วยคุณเพิ่มกำไร',
subtitle = 'เราช่วยวางระบบงาน และใช้สถิติวางกลยุทธ์ทางการตลาด',
showCTA = true,
ctaText = "เริ่มปรึกษาฟรี",
ctaLink = "/contact",
ctaText = 'เริ่มปรึกษาฟรี',
ctaLink = '/contact',
pains = [
{ surface: 'yellow', text: 'ยิ่งขาย กำไรยิ่งลด?' },
{ surface: 'purple-soft', text: 'มีเว็บไซต์ เหมือนไม่มี?' },
{ surface: 'mint', text: 'พนักงานทำงานได้น้อยกว่าที่ต้องการ?' },
{ surface: 'teal', text: 'เอา AI มาให้ใช้ แต่งานไม่ได้มากขึ้นตามที่คิด?' },
],
} = Astro.props;
// Split title into words for kinetic animation
@@ -46,13 +52,17 @@ const titleWords = title.split(' ');
</div>
<div class="container hero-container">
<!-- Badge (data-animate for kineticHeadline words too) -->
<!-- 2-COLUMN LAYOUT: text (left) + pain stack (right) -->
<div class="hero-grid">
<!-- LEFT: text content -->
<div class="hero-text">
<!-- Badge -->
<div class="hero-badge" data-animate="fade-in">
<span class="badge-dot"></span>
{badge}
</div>
<!-- Main Title - Kinetic Typography (animated by lib/animations.ts kineticHeadline) -->
<!-- Main Title - Kinetic Typography -->
<h1 class="hero-title kinetic-title">
{titleWords.map((word, index) => (
<span class="word-wrapper">
@@ -92,6 +102,18 @@ const titleWords = title.split(' ');
<slot name="hero-trust" />
</div>
<!-- RIGHT: pain card stack -->
<div class="hero-pain-stack" data-animate="fade-in-up">
{pains.map((p, i) => (
<div class={`pain-card pain-${p.surface}`} style={`--pain-delay: ${0.6 + i * 0.15}s`}>
<div class="pain-eyebrow">คุณกำลังเจอปัญหา</div>
<div class="pain-text">{p.text}</div>
</div>
))}
</div>
</div>
</div>
<!-- Giant Background Typography -->
<div class="hero-bg-text" aria-hidden="true">
AI
@@ -274,7 +296,103 @@ const titleWords = title.split(' ');
position: relative;
z-index: 1;
padding: 140px var(--gutter) 100px;
max-width: 1200px;
max-width: 1600px;
}
/* ============================================
HERO GRID — 2 COLUMNS (text + pain stack)
============================================ */
.hero-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
}
.hero-text {
min-width: 0;
}
.hero-pain-stack {
display: flex;
flex-direction: column;
gap: 20px;
min-width: 0;
}
.pain-card {
padding: 28px 32px;
border-radius: var(--radius-xl);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
border: 1px solid transparent;
opacity: 0;
transform: translateY(20px);
animation: painReveal 0.7s var(--ease-out-expo) var(--pain-delay, 0s) forwards;
transition: transform 0.4s var(--ease-out-expo);
}
.pain-card:hover {
transform: translateY(-4px);
}
@keyframes painReveal {
to {
opacity: 1;
transform: translateY(0);
}
}
.pain-yellow {
background: var(--color-primary);
color: var(--color-black);
}
.pain-purple-soft {
background: var(--color-purple-soft);
color: var(--color-black);
}
.pain-mint {
background: var(--color-mint-soft);
color: var(--color-black);
}
.pain-teal {
background: var(--color-teal);
color: var(--color-white);
}
.pain-teal .pain-eyebrow { color: var(--color-primary); }
.pain-eyebrow {
font-family: var(--font-display);
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
opacity: 0.7;
margin-bottom: 8px;
}
.pain-text {
font-family: var(--font-display);
font-size: 22px;
font-weight: 800;
line-height: 1.3;
}
@media (max-width: 1024px) {
.hero-grid {
grid-template-columns: 1fr;
gap: 40px;
}
.hero-text {
order: 1;
}
.hero-pain-stack {
order: 2;
}
}
@media (max-width: 640px) {
.pain-text { font-size: 18px; }
.pain-card { padding: 20px 24px; }
}
/* ============================================

View File

@@ -22,6 +22,7 @@ const { name, url, category, category_label, industry, thumbnail, description, w
<div class="portfolio-image">
<img src={thumbnail} alt={name} loading="lazy" />
<div class="portfolio-overlay">
{what_we_did && <p class="overlay-did">{what_we_did}</p>}
<span class="visit-btn">
เยี่ยมชมเว็บไซต์
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -34,7 +35,6 @@ const { name, url, category, category_label, industry, thumbnail, description, w
{industry && <span class="portfolio-industry">{industry}</span>}
<span class="portfolio-category">{category_label}</span>
<h3 class="portfolio-name">{name}</h3>
{what_we_did && <p class="portfolio-did">{what_we_did}</p>}
{result && <p class="portfolio-result">→ {result}</p>}
{!what_we_did && description && <p class="portfolio-desc">{description}</p>}
</div>
@@ -84,8 +84,11 @@ const { name, url, category, category_label, industry, thumbnail, description, w
inset: 0;
background: rgba(254, 212, 0, 0.95);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 24px;
opacity: 0;
transition: opacity 0.3s ease;
}
@@ -94,6 +97,14 @@ const { name, url, category, category_label, industry, thumbnail, description, w
opacity: 1;
}
.overlay-did {
font-size: 13px;
line-height: 1.6;
color: var(--color-black);
text-align: center;
max-width: 280px;
}
.visit-btn {
display: inline-flex;
align-items: center;

View File

@@ -12,6 +12,7 @@ const services = defineCollection({
badge: z.string(),
category: z.string(),
objective: z.string(),
short_desc: z.string().optional(), // NEW: short description
usp_free_server: z.string().optional(),
usp_content_edit: z.string().optional(),
}),
@@ -25,13 +26,29 @@ const portfolio = defineCollection({
schema: z.object({
name: z.string(),
url: z.string().optional(),
category: z.string(), // webdev | ecommerce | marketing
category: z.string(), // webdev | ecommerce | marketing | ai-consult | automation
category_label: z.string(),
industry: z.string().optional(), // NEW: industry for filtering
thumbnail: z.string(),
description: z.string(),
what_we_did: z.string().optional(), // NEW: separate from description
result: z.string().optional(), // NEW: outcome metric
industry: z.string().optional(), // industry for filtering
thumbnail: z.string().optional(), // optional now
description: z.string().optional(),
what_we_did: z.string().optional(),
result: z.string().optional(),
featured: z.boolean().optional(), // NEW: show on home page
order: z.number().optional(), // NEW: display order
case_study: z.boolean().optional(), // NEW: full case study body
}),
});
// =============================================================================
// PAGES — static page content (about, home, etc.)
// =============================================================================
const pages = defineCollection({
loader: glob({ pattern: '*.{md,mdx}', base: './src/content/pages' }),
schema: z.object({
title: z.string().optional(),
subtitle: z.string().optional(),
hero_badge: z.string().optional(),
badge: z.string().optional(),
}),
});
@@ -85,4 +102,5 @@ export const collections = {
faq,
settings,
blog,
pages,
};

View File

@@ -0,0 +1,86 @@
---
title: "เกี่ยวกับเรา"
subtitle: "ที่ปรึกษาที่ทำงานเอง ไม่ใช่ทีมขายที่ส่งงานต่อ"
hero_badge: "เกี่ยวกับเรา"
---
# เกี่ยวกับมอร์มินิมอร์
## เริ่มจากตรงนี้
มอร์มินิมอร์ ก่อตั้งปี 2020 จากประสบการณ์ตรงที่เห็น SME ไทยเสียเงินหลายแสนไปกับ
- เว็บไซต์ที่ "สวยแต่ไม่มีคนเข้า"
- โฆษณาที่ "ยิงเยอะแต่ไม่มีคนซื้อ"
- AI tools ที่ "ว้าวแต่ใช้ไม่เป็น"
เราเลยตั้งใจว่าจะทำให้ต่าง
## นโยบายของเรา
**เป้าหมายของเราคือการเพิ่มกำไรให้ลูกค้า** เพราะถ้าลูกค้ามีกำไรมากขึ้น ลูกค้าก็จะสามารถใช้บริการเราต่อไปได้
ทุกระบบที่ส่งมอบต้องตอบคำถามเดียวให้ได้: **"ลูกค้ามีกำไรเพิ่มขึ้นจริงไหม"** — ไม่ใช่แค่ส่งงานตามสัญญา
## วิธีทำงานของเรา
เราไม่ได้ทำงานแบบเดียวกับทุกที่ — เพราะลูกค้าแต่ละคนมีบริบท งบประมาณ และทีมต่างกัน
- **งานที่ต้องใช้ความเชี่ยวชาญเฉพาะทาง** — เราจะทำเอง เพราะถ้าเราไม่ทำเอง เราจะตอบคำถามลูกค้าไม่ได้
- **งานที่ต้องทำซ้ำ ๆ ปริมาณเยอะ** — เราจะใช้ bot หรือ outsource ให้คนที่เชี่ยวชาญเฉพาะด้าน เพราะเรื่องบางเรื่อง คนที่ทำเป็นอาชีพจะทำได้ดีกว่า
- **งานที่ต้องตอบลูกค้าตลอด 24 ชั่วโมง** — เราจะใช้ bot ช่วยคัดกรองเบื้องต้น แล้วเราจะตามด้วยคน เพราะบางเรื่อง bot ตอบไม่ได้
สรุปคือ — **ผลงานที่ออกมาจะขึ้นอยู่กับบริบทของลูกค้า ไม่ใช่วิธีทำงานของเรา** เราเลือกวิธีที่จะทำให้ลูกค้าได้ผลลัพธ์ที่ดีที่สุด ไม่ใช่วิธีที่เราถนัดที่สุด
## เราเชื่ออะไร
- **เว็บสวยไม่ได้แปลว่าขายได้** — เว็บหลายเว็บที่ดูดีที่สุด ขายแย่ที่สุดก็มี
- **AI ไม่ได้แทนทุกอย่าง** — แต่ถ้ารู้จักใช้ จะประหยัดหรือเปิดโอกาสใหม่ที่คาดไม่ถึง
- **จ่ายแพงไม่ได้แปลว่าดี** — งาน 80% ใช้ของถูกได้ ส่วนที่เหลือค่อยใช้ของแพง
- **ทำเผื่อมากเกินไป** มักจะทำให้จ่ายเกินความจำเป็น
- **"สิ่งที่ถูกต้อง" ไม่ใช่ "สิ่งที่ลูกค้าต้องการ" เสมอไป** — เราเช็คเสมอว่าสิ่งที่เราจะทำตรงกับที่ลูกค้าต้องการจริงไหม
- **ระบบที่สุดยอดที่คนใช้ไม่เป็น = เสียเงินเปล่า** — เราเน้นให้คนใช้ได้จริง ไม่ใช่แค่ส่งของแพง ๆ
## เบื้องหลังการทำงาน
เรานั่งทำงานที่บ้าน ใช้ MacBook ตัวเดียว แต่มี **เครื่องมือด้าน marketing intelligence** ที่ช่วยให้เราเข้าใจตลาดได้ลึกกว่าคนทั่วไป:
- **ระบบดูโฆษณาคู่แข่ง** — ดูว่าคู่แข่งยิงโฆษณาอะไร คำไหน convert ดี งบประมาณเท่าไหร่ เพื่อให้ลูกค้าวางกลยุทธ์ได้โดยไม่ต้องเดา
- **ระบบวิเคราะห์ keyword + SEO gap** — หาโอกาสที่คู่แข่งยังไม่ได้ทำ เพื่อให้ลูกค้าได้ traffic ก่อน
- **ระบบติดตาม trend ตลาด** — รู้ว่าตลาดกำลังไปทางไหนก่อนที่ลูกค้าจะรู้ตัว
- **Stack:** JavaScript, Python, PHP, n8n, OpenAI API, Meta API, Google Analytics, Looker Studio — แล้วแต่งาน
## เรื่องเล่าจากลูกค้า
> "จ่ายไม่อั้นเปิดตลาดใหม่ให้ที"
> — ลูกค้าหลังขยายช่องทางการตลาดสำเร็จ
> "ผลตอบรับดีเกินคาดแฮะ"
> — ลูกค้าหลังปรับกลยุทธ์โฆษณา
> "ผมตามประชุมแต่ลูกค้าบอกว่า 'ไม่ต้องหรอก เพราะคนทักมาจนยุ่งไปหมดแล้ว'"
> — ลูกค้าหลังใช้ระบบจองออนไลน์/แชทบอททำงานแทน
> "พี่คนโทรมาเพิ่มขึ้นครับ แถมถามสินค้าที่ไม่เคยมีคนถามด้วย"
> — ลูกค้าหลัง SEO ติดอันดับและมีคนเข้าเว็บเพิ่ม
(เรื่องเล่าจากคำตอบลูกค้าจริง ที่ระบุไว้ในบริบทการสนทนาตอนเริ่มโปรเจกต์)
## ลูกค้าที่เหมาะกับเรา
- SME ที่ต้องการคนช่วยดูเรื่อง Online Marketing และระบบ IT
- ลูกค้าที่ไม่มีความรู้ IT มาก แต่จำเป็นต้องใช้ระบบ IT
- คนที่ต้องการที่ปรึกษาจริง ๆ ไม่ใช่คนมาขายของ
## ลูกค้าที่ไม่เหมาะ
- องค์กรขนาดใหญ่ที่ต้องการความเป็นทางการสูง + การเมืองเยอะ + ต้องสร้างภาพลักษณ์เกินตัว
- คนที่อยากได้ของฟรี หรือจ่ายน้อยระดับพัน แต่คาดหวังงานระดับแสน
- กลุ่ม IT ทั่วไป — เป็นคู่แข่งเกือบทั้งหมด ยกเว้นกลุ่มขาย Software เฉพาะทาง เช่น ERP
## กระบวนการทำงาน
1. **ปรึกษาฟรี 30-60 นาที** — คุยกับเจ้าของธุรกิจ ฟังปัญหา เป้าหมาย งบประมาณ ให้คำแนะนำเบื้องต้น ไม่ผูก commitment
2. **วางแผน + ส่ง Proposal** — วิเคราะห์เชิงลึก ดูคู่แข่ง ส่ง Proposal เป็น PDF คุณอ่าน ถามคำถาม แก้ไข scope ได้ก่อนเซ็น
3. **ดำเนินการ** — เราจะแจ้งความคืบหน้าของงานเป็นช่วง ๆ ผ่าน LINE Group ของโปรเจกต์ คุณเห็นว่าเราทำอะไรไปแล้วบ้าง และจะมี demo เมื่อเราพร้อมส่งงานรอบใหญ่ ๆ เท่านั้น — เราไม่อยากผูกมัดตัวเองกับ deadline แบบ sprint เพราะงานจริงไม่ได้แบ่งเป็นรอบสั้น ๆ ได้ตลอด
4. **ส่งมอบ + ดูแล** — ส่งมอบงาน + อบรมทีม + มอบคู่มือ หลังส่งมอบ ถ้ามีการปรับเปลี่ยนเล็ก ๆ น้อย ๆ หรือแก้ไขจุดบกพร่อง เราจะดูแลให้โดยไม่คิดค่าใช้จ่ายเพิ่ม ยกเว้นกรณีที่เป็นการสร้าง feature ใหม่ หรือปรับแต่งครั้งใหญ่ เราจะคิดค่าใช้จ่ายเป็นครั้ง ๆ ไปตามขอบเขตงาน

View File

@@ -1,11 +1,12 @@
---
name: Jet Industries
url: https://jetindustries.co.th
name: "Jet Industries"
url: "https://jetindustries.co.th"
category: webdev
category_label: พัฒนาเว็บไซต์
industry: 🏭 โรงงาน
thumbnail: /images/portfolio/jetindustries.png
description: ผู้ผลิตพลาสติกฉีดขึ้นรูปอย่างแม่นยำ (Precision Plastic Injection Molding) มีประสบการณ์กว่า 40 ปี
what_we_did: ออกแบบเว็บ B2B + SEO เทคนิค + Multi-language (TH/EN/JP)
result: ลูกค้าจากญี่ปุ่นและออสเตรเลียติดต่อเข้ามา 58 ราย/เดือน
category_label: "พัฒนาเว็บไซต์"
industry: "โรงงาน / B2B"
what_we_did: "ออกแบบ + พัฒนาเว็บไซต์ B2B ทันสมัย พร้อมระบบขอใบเสนอราคา"
result: "เว็บไซต์ที่สื่อสารตรงกับกลุ่มลูกค้า B2B สมัครใช้งานง่ายขึ้น"
thumbnail: "/images/portfolio/jetindustries.png"
featured: true
order: 18
---

View File

@@ -1,11 +1,12 @@
---
name: สำนักงานกฎหมาย ตถาตา
url: https://lawyernoom.com
name: "สำนักงานกฎหมาย ตถาตา"
url: "https://lawyernoom.com"
category: webdev
category_label: พัฒนาเว็บไซต์
industry: ⚖️ สำนักงานกฎหมาย
thumbnail: /images/portfolio/lawyernoom.png
description: สำนักงานกฎหมายโดย ทนายความ คมสัน ศรีวนิชย์ - บริการด้านคดีความ คดีแพ่ง คดีอาญา
what_we_did: เว็บไซต์มาตรฐาน + SEO คีย์เวิร์ด "ทนายความ" ในจังหวัด
result: ติดหน้า 1 Google ใน 4 เดือน ลูกค้าโทรเข้ามา 10+ ราย/เดือน
category_label: "พัฒนาเว็บไซต์"
industry: "สำนักงานกฎหมาย"
what_we_did: "ออกแบบเว็บไซต์สำนักงานกฎหมาย น่าเชื่อถือ + SEO ท้องถิ่น + ระบบนัดหมาย"
result: "เว็บไซต์มืออาชีพที่สร้างความเชื่อมั่นให้ลูกความ"
thumbnail: "/images/portfolio/lawyernoom.png"
featured: true
order: 17
---

View File

@@ -1,11 +1,12 @@
---
name: ทวนทอง 99
url: https://tuanthong99.com
name: "ทวนทอง 99"
url: "https://tuanthong99.com"
category: ecommerce
category_label: อีคอมเมิร์ซ
industry: 🛒 E-commerce
thumbnail: /images/portfolio/tuanthong.png
description: ร้านค้าออนไลน์สมุนไพรไทยคุณภาพสูง
what_we_did: เว็บไซต์ + ระบบสั่งซื้อ + ผูก Facebook Pixel
result: ROAS โฆษณาเฉลี่ย 4.8 เท่า
category_label: "อีคอมเมิร์ซ"
industry: "สินค้าอุปโภคบริโภค"
what_we_did: "พัฒนาเว็บไซต์ขายของออนไลน์ + ระบบตะกร้า + จัดการสินค้าด้วยตัวเอง"
result: "เว็บไซต์ขายสินค้าอุปโภคบริโภค ดูแลเองได้ สะดวกรวดเร็ว"
thumbnail: "/images/portfolio/tuanthong.png"
featured: true
order: 11
---

View File

@@ -0,0 +1,72 @@
---
title: "AI Consult"
subtitle: "AI ที่ใช้ในองค์กร เน้นความคุ้มค่าและรักษาความลับ"
badge: "ที่ปรึกษาด้าน AI"
category: "tech-consult"
objective: "รักษาความรู้ขององค์กร"
short_desc: "เก็บความรู้และประสบการณ์ของพนักงานให้อยู่กับองค์กร ใช้ Local LLM สำหรับงานที่ต้องการความลับ"
---
# AI Consult
## เราทำ AI แบบนี้
AI ไม่ได้แทนทุกอย่างได้ แต่ถ้ารู้จักใช้ จะประหยัดหรือเปิดโอกาสใหม่ที่คาดไม่ถึง
**เราไม่ได้เลือก AI ที่แพงที่สุดเสมอไป** — งานหลายอย่างใช้ Model ราคาถูกก็ได้ผลเท่า ๆ กัน ช่วยประหยัดค่าใช้จ่ายได้มากกว่าครึ่ง
**เราเน้น Local LLM** สำหรับงานที่ต้องการความลับ — เพราะข้อมูลของลูกค้าเป็นเรื่องสำคัญ
## วัตถุประสงค์หลัก: เก็บความรู้ให้อยู่กับองค์กร (KM)
ถ้าพนักงานลาออกหรือเปลี่ยน พนักงานใหม่ที่เข้ามาจะเรียนรู้งานเองได้ไว ลดการลองผิดลองถูก ทำให้การทำงานไม่สะดุด ลดความผิดพลาดในระยะยาว
องค์กรจะพึ่งพนักงานที่มีความสามารถเฉพาะตัวน้อยลง เพราะข้อมูลการทำงานมีการเก็บเข้าคลังความรู้ตลอด
## AI เปรียบเสมือน "พี่เลี้ยง" ในองค์กร
- **พนักงานใหม่** ถาม AI ได้เลย เหมือนมีพี่เลี้ยงคอยแนะนำ
- **ผู้บริหาร** ถามข้อมูลเชิงลึก หรือตั้งค่าให้ AI รายงานตามเวลา/เหตุการณ์ที่กำหนด
- **ยิ่งพนักงานคุยกับ AI เยอะ** ยิ่งเก็บข้อมูลเชิงลึกได้มาก AI ก็ยิ่งฉลาดขึ้น
## ใช้ AI ตัวไหน? เราเลือกให้เหมาะกับงาน
| ประเภทงาน | AI ที่ใช้ | ทำไม |
|---|---|---|
| ตอบแชท/งานทั่วไป | Model ราคาถูก (Haiku, Mini, local) | ประหยัด ได้ผลพอ |
| วิเคราะห์ข้อมูล/เขียนรายงาน | Model ระดับกลาง | สมดุลระหว่างคุณภาพและราคา |
| งานที่ต้องการความลับ/PDPA | Local LLM | ข้อมูลไม่ออกจากเครื่อง |
| งานเฉพาะทางที่ซับซ้อนมาก | Model ระดับ Frontier | ใช้เฉพาะเมื่อจำเป็น |
## กระบวนการทำงาน
1. **AI Audit (ฟรี)** — วิเคราะห์ว่าธุรกิจควรใช้ AI ตัวไหน ตรงไหน
2. **วาง AI Strategy** — เลือกเครื่องมือ วาง Roadmap การใช้ AI ในองค์กร
3. **พัฒนา + Integrate** — เชื่อม AI เข้ากับ Workflow เดิม
4. **Train + ดูแล** — สอนทีมใช้ ปรับปรุงตามผลตอบรับ
## เมื่อไหร่ที่เหมาะกับเรา
- พนักงานเปลี่ยนบ่อย ความรู้สะสมหาย
- มีข้อมูลเยอะ แต่ใช้ไม่เป็น
- อยากใช้ AI แต่กังวลเรื่องความลับ/PDPA
- อยากลดเวลา Onboard พนักงานใหม่
- ผู้บริหารอยากได้ AI เหมือนเลขาส่วนตัว
## เมื่อไหร่ที่ไม่เหมาะ
- แค่อยากลองเล่น ChatGPT — ไม่ต้องจ้างเรา ใช้เองได้เลย
- คิดว่า AI แทนพนักงานได้ทุกอย่าง — ยังทำไม่ได้ AI เป็นเครื่องมือ ไม่ใช่คน
- มีข้อมูลน้อยมาก AI เรียนรู้ไม่ได้
## ตัวอย่างงาน
ดูตัวอย่างงานจริงทั้งหมดได้ที่หน้า Portfolio
## สิ่งที่กำลังทดลอง
- ระบบ Harness และ AI Memory ใหม่ ๆ
- Model ที่ถูกกว่า เพื่อให้ได้ผลลัพธ์ใกล้เคียง Model แพง
- รูปแบบการตลาดใหม่ ๆ เช่น SEO กับ GEO ในยุค AI
- ความเสี่ยงที่คนจะเชื่อ AI จนไม่สนใจว่าข้อมูลถูกหรือผิด — เรื่องนี้สำคัญ
- ช่องทางการขายใหม่ ๆ ที่นอกเหนือจากที่คนส่วนใหญ่ใช้

View File

@@ -0,0 +1,75 @@
---
title: "Automation Consult"
subtitle: "ระบบอัตโนมัติที่ออกแบบเฉพาะธุรกิจคุณ ไม่ใช่ใช้ของสำเร็จรูป"
badge: "วางระบบ Automation"
category: "tech-consult"
objective: "ลดต้นทุนและเวลา"
short_desc: "เลือกเครื่องมือที่เหมาะกับงาน — n8n, Script เฉพาะทาง, หรือ AI วิเคราะห์"
---
# Automation Consult
## เราทำ Automation แบบนี้
เริ่มจากดู Workflow ปัจจุบันของคุณก่อน แล้วถามว่า "ขั้นตอนไหนที่ซ้ำ ทำมือ แล้วเสียเวลา"
แล้วค่อยเลือกเครื่องมือที่เหมาะ — ไม่ใช่ทุกงานต้องใช้เครื่องมือเดียวกัน
- **งานบางอย่าง** ใช้ n8n (Workflow automation)
- **งานบางอย่าง** เขียน Script เฉพาะทาง
- **งานบางอย่าง** ใช้ AI วิเคราะห์หรือตัดสินใจ
- **งานบางอย่าง** ใช้ทั้ง 3 อย่างผสมกัน
## บริการนี้เหมือนได้ AI Developer มาใช้
เราไม่ได้เขียนแอปทั้งแอป เราโฟกัสที่ **งานเฉพาะจุดที่ Developer ปกติไม่อยากทำ** — แต่เป็นงานที่กินเวลาทีมเยอะ
## กระบวนการทำงาน
1. **รับ Requirement + ศึกษา Workflow (1-2 สัปดาห์)** — ดูว่างานไหนทำซ้ำ ตรงไหนคอขวด
2. **เลือกเครื่องมือ + ออกแบบ (1-2 สัปดาห์)** — เลือก Tool ที่เหมาะ วางแผนว่าจะเชื่อมต่อระบบอะไร
3. **พัฒนา + ทดสอบ (2-6 สัปดาห์)** — พัฒนาแบบ Sprint ส่ง Demo ให้ทดสอบ
4. **Deploy + ดูแล (ต่อเนื่อง)** — ติดตั้งจริง สอนทีมใช้ ดูแลต่อ
## ประเภทงานที่ทำ
### งานเชื่อมต่อระบบ
- เชื่อม POS + บัญชี + สต็อก เข้าด้วยกัน
- เชื่อม CRM + Email + LINE OA
- เชื่อม Excel/Google Sheets กับระบบภายใน
### งาน Automate รายงาน
- รายงานยอดขายรายวัน/สัปดาห์/เดือน
- แจ้งเตือนอัตโนมัติเมื่อตัวเลขผิดปกติ
- ดึงข้อมูลจากหลายแหล่งมารวมในที่เดียว
### งาน Automate การสื่อสาร
- Chatbot LINE OA ตอบคำถามซ้ำ
- ตอบ DM อัตโนมัติ
- แจ้งเตือนลูกค้า/ทีม ตามเงื่อนไข
### งานเฉพาะทาง
- เขียน Script สำหรับงานเฉพาะของธุรกิจ
- AI วิเคราะห์ข้อมูล/รูปภาพ
- ระบบจอง/คิว/นัดหมาย
## เมื่อไหร่ที่เหมาะกับเรา
- ทีมเสียเวลากับงานซ้ำ ๆ เป็นชั่วโมงทุกวัน
- มีระบบหลายตัวที่ไม่คุยกัน
- อยากลดเวลาในการทำรายงาน/ตอบแชตซ้ำ
- มี Workflow ที่ชัดเจน แต่ขาดคนมาทำให้อัตโนมัติ
## เมื่อไหร่ที่ไม่เหมาะ
- Workflow ยังไม่ชัด ยังเปลี่ยนบ่อย — รอให้นิ่งก่อนแล้วค่อย Automate
- อยากได้ระบบใหม่ทั้งหมด — เราเชี่ยว Automate ของเดิม ไม่ใช่สร้างแอปทั้งแอป
- งานที่ต้องใช้ดุลยพินิจคนเยอะ ๆ — AI ยังทำแทนไม่ได้ทุกเรื่อง
## ตัวอย่างงาน
ดูตัวอย่างงานจริงทั้งหมดได้ที่หน้า Portfolio — มีเคสหลากหลายตั้งแต่คลินิก ร้านอาหาร สถานศึกษา ไปจนถึงที่ปรึกษา

View File

@@ -0,0 +1,61 @@
---
title: "Online Marketing Consult"
subtitle: "วางกลยุทธ์จากข้อมูล ไม่ใช่จากประสบการณ์ล้วน ๆ"
badge: "ที่ปรึกษาการตลาดออนไลน์"
category: "marketing"
objective: "เพิ่มยอดขาย"
short_desc: "ดูสถิติก่อนยิงโฆษณา — เลือก Platform ที่คุ้ม ตัดอันที่เสียเงินเปล่า"
---
# Marketing Consult
## เราทำการตลาดแบบนี้
เราไม่ได้เริ่มจากอยากยิงโฆษณาอะไร เราเริ่มจากดูข้อมูลของลูกค้าก่อน — สถิติคนเข้าเว็บ สถิติคนคลิกโฆษณา ยอดขายแยกตามสินค้าและกลุ่มลูกค้า
แล้วค่อยวางกลยุทธ์ว่าจะยิงที่ไหน ใช้ Creative แบบไหน เจาะกลุ่มเป้าหมายใคร
**กลยุทธ์ของเราไม่ได้จำกัดอยู่แค่เว็บ Social Media หรือโฆษณา** — เรามองภาพรวมและวางแผนในทุกช่องทางให้สอดคล้องกัน เลือกช่องทางที่เหมาะกับสินค้าและกลุ่มเป้าหมายจริง ๆ
## กระบวนการทำงาน
1. **ตั้งระบบเก็บข้อมูล (1-2 สัปดาห์)** — GA4, Meta Pixel, ระบบแยกยอดขายตามสินค้า/กลุ่มสินค้า/กลุ่มลูกค้า (ถ้าทำได้)
2. **วิเคราะห์ + วางกลยุทธ์ (1-2 สัปดาห์)** — ดูว่าสินค้าไหนขายดี กลุ่มไหนซื้อ ช่องทางไหนคุ้ม → เขียนแผนกลยุทธ์
3. **ลงมือ + วัดผล (ต่อเนื่อง)** — ปรับ Creative, ปรับกลุ่มเป้าหมาย, ขยาย/ตัดช่องทาง พร้อมดูตัวเลขทุกสัปดาห์
4. **สอนทีม (ต่อเนื่อง)** — ถ้าทีมลูกค้าขาดความรู้ในเรื่องใด เราจะสอนเสริมให้ เพื่อให้ทำต่อได้ด้วยตัวเอง
## สิ่งที่เราทำ
- **วางแผนกลยุทธ์ช่องทาง** — เลือก Platform ที่เหมาะกับสินค้า ไม่ใช่ยิงทุกที่
- **เจาะกลุ่มเป้าหมาย** — ดูข้อมูลว่าใครซื้อจริง ไม่ใช่ใครคลิกง่าย
- **ปรับ Creative + Copy** — ให้ดึงดูดคนที่พร้อมจ่าย ไม่ใช่คนที่เทียบราคา
- **ปรับ Funnel** — ให้คนเข้ามาแล้วเดินต่อจนซื้อ
- **วัดผล + รายงาน** — ตัวเลขจริง ไม่ใช่ "ยอดดีขึ้น" แบบกำกวม
## เมื่อไหร่ที่เหมาะกับเรา
- มีเว็บไซต์และกิจกรรมออนไลน์อยู่แล้ว แต่ยอดไม่โต
- อยากเปลี่ยนจาก "ยิงแล้วหวัง" เป็น "ยิงแล้วรู้ว่าเกิดอะไรขึ้น"
- อยากให้ทีมเรียนรู้ไปด้วย ไม่ใช่จ้างเราตลอด
## เมื่อไหร่ที่ไม่เหมาะ
- งบน้อยมาก ไม่พอจะยิงโฆษณาเลย — เริ่มจาก SEO/Content ดีกว่า
- อยากได้ผลใน 1 สัปดาห์ — เราเน้นงานที่ยั่งยืน ไม่ใช่แค่ยิงให้ขายได้แล้วหยุด
- คิดว่า "ยิงเยอะ ๆ แล้วจะขายได้เอง" — ไม่จริงเสมอไป บางทียิงน้อยแต่ตรงกลุ่มดีกว่า
## ตัวอย่างงาน
- **Dataroot** (Flagship) — เพิ่ม Impression 373%, Click 114.2%, ลดงบโฆษณา 28.3% ในเดือนแรก — ดูเคสเต็มได้ที่หน้า Portfolio
ดูตัวอย่างงานจริงอื่น ๆ ทั้งหมดได้ที่หน้า Portfolio
## บทเรียนจากเคสที่เกือบพัง
เคยยิงโฆษณาไปกลุ่ม "คนสนใจเรื่องการเงิน" เพราะคิดว่าสินค้าลูกค้าเกี่ยวกับการลงทุน — ผลคือมีแต่คนมาขอกู้เงิน เพราะเข้าใจว่าลูกค้าเราเป็นสถาบันการเงิน
**บทเรียน:** เลือกกลุ่มเป้าหมายต้องดู "คนที่จะซื้อ" ไม่ใช่ "คนที่คลิกง่าย"
อีกเคส — ขายสินค้าบน Platform โดยไม่ได้คำนวณค่าธรรมเนียมแฝง (ค่าธรรมเนียม Platform, ค่าจัดส่ง, ค่าคืนสินค้า, ส่วนลด) ขายได้เยอะ แต่กำไรหายเกือบหมด
**บทเรียน:** ขายดี ≠ มีกำไร ต้องคำนวณต้นทุนแท้จริงก่อนวางแผน

View File

@@ -0,0 +1,81 @@
---
title: "Website Development"
subtitle: "เว็บไซต์ที่ขายได้ ไม่ใช่เว็บที่สวย"
badge: "พัฒนาเว็บไซต์"
category: "webdev"
objective: "ทำเว็บให้ขายได้"
short_desc: "เว็บไซต์ที่ออกแบบมาเพื่อให้คนซื้อ ไม่ใช่เพื่อให้คนชอบ — Astro สำหรับเว็บทั่วไป, WordPress สำหรับ E-commerce"
# ไม่ได้อยู่ใน Astro case study เพราะเป็น static frontmatter
---
# Website Development
## เราทำเว็บแบบนี้
เริ่มจากถามว่า "เว็บนี้ทำเพื่ออะไร" ก่อน — ขายของ? เก็บ Lead? สร้างความน่าเชื่อถือ? เพราะเว็บแต่ละแบบต้องออกแบบต่างกัน
เราไม่ได้เน้นให้เว็บสวย เราเน้นให้เว็บทำหน้าที่ของมันได้ เพราะเว็บหลายเว็บที่ดูดีที่สุด ขายแย่ที่สุดก็มี
## เลือกระบบตามงาน
### Astro — เว็บทั่วไป, Blog, เว็บบริษัท, Landing Page
**เหมาะกับ:** เว็บที่ไม่ซับซ้อน เน้นความเร็ว SEO และการโหลดเร็ว
**ระยะเวลา:** 14-30 วัน
**ได้อะไร:**
- เว็บไซต์ที่โหลดเร็วมาก (Google PageSpeed 90+)
- SEO พร้อมตั้งแต่วันแรก — ติด Google ได้เร็ว
- Chatbot AI ที่ปรับแก้เว็บไซต์ได้เอง (เฉพาะเว็บที่ใช้ Host ของเรา)
- ระบบแก้ไขเนื้อหาด้วย AI — คุยกับ AI ให้ช่วยเขียน/แก้บทความ
### WordPress — E-commerce, เว็บที่มีระบบหลังบ้านซับซ้อน
**เหมาะกับ:** เว็บที่ต้องมี Database เยอะ ระบบซับซ้อน หรือมี Design มาให้แล้ว
**ระยะเวลา:** 2-4 เดือน
**ได้อะไร:**
- ระบบหลังบ้านที่ลูกค้าแก้เองได้ (เพิ่มสินค้า แก้รูป เขียนบทความ)
- E-commerce ครบชุด (ตะกร้า, ชำระเงิน, ติดตามพัสดุ)
- Plugin เสริมได้ตามต้องการ
## กระบวนการทำงาน
1. **คุย Requirement (1-2 วัน)** — ถามว่าเว็บนี้ต้องทำอะไร ใครเป็นคนใช้ เน้นเรื่องอะไร
2. **วางโครง + ออกแบบ (3-7 วัน)** — ส่ง Proposal โครงสร้างเว็บ + Mockup ให้ดูก่อนเซ็น
3. **พัฒนา (10-20 วัน)** — ส่ง Demo ให้ดูทุกสัปดาห์ ไม่ใช่ส่งทีเดียวตอนจบ
4. **ทดสอบ + แก้ไข (3-5 วัน)** — ทดสอบจริง แก้ตามฟีดแบ็ค
5. **ส่งมอบ + อบรม (1 วัน)** — ส่งมอบ + สอนใช้งาน + มอบคู่มือ
## เมื่อไหร่ที่เหมาะกับเรา
- เว็บที่ต้องโหลดเร็ว ติด Google ง่าย
- เว็บที่ลูกค้าต้องแก้เนื้อหาเองได้บ่อย ๆ
- เว็บ E-commerce ที่ต้องการระบบครบ
## เมื่อไหร่ที่ไม่เหมาะ
- ต้องการเว็บ Landing Page ราคาถูก ๆ ไม่กี่พัน — เราไม่ทำ เราเน้นเว็บที่ต้องทำงานจริง
- ต้องการเว็บแบบเดียวกับที่อื่น — เราชอบออกแบบให้เหมาะกับงาน ไม่ใช่ใช้ Template เดียวกับทุกคน
- ต้องการเว็บที่ไม่ต้องดูแลต่อ — เราแนะนำให้มีแพ็คเกจดูแลรายเดือน เพราะเว็บที่ปล่อยทิ้งจะตาย
## ตัวอย่างงาน
ดูตัวอย่างงานจริงทั้งหมดได้ที่หน้า Portfolio
## คำถามที่ลูกค้าถามบ่อย
**แก้เว็บเองยากไหม?**
ไม่ยาก — สำหรับ Astro จะคุยกับ AI ให้ช่วยแก้เนื้อหา ส่วน WordPress มีระบบหลังบ้านใช้งานง่าย เปลี่ยนข้อความ รูป สินค้า ได้ด้วยตัวเอง
**มีค่าใช้จ่ายอะไรเพิ่มไหม?**
ค่า Host + โดเมนฟรีปีแรก ปีถัดไปคิดตามจริง — เริ่มต้น 5,000 บาท/ปี
**ทำเว็บ E-commerce ได้ไหม?**
ได้ ใช้ WordPress + WooCommerce พร้อมระบบชำระเงิน ติดตามพัสดุ ครบ
**GEO คืออะไร?**
Generative Engine Optimization — ปรับเว็บให้ถูกอ้างอิงโดย ChatGPT, Perplexity, Google AI Overviews ทำให้ธุรกิจปรากฏในคำตอบ AI

View File

@@ -3,21 +3,78 @@ 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 Icon from '../components/Icon.astro';
import BentoGrid from '../components/BentoGrid.astro';
import BentoTile from '../components/BentoTile.astro';
import DecoOrb from '../components/DecoOrb.astro';
import { getEntry, render } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
const about = await getEntry('pages', 'about') as CollectionEntry<'pages'>;
const { Content } = await render(about);
---
<Base title="เกี่ยวกับเรา | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
badge="เกี่ยวกับเรา"
title="เิ่มยอดขาย ลดต้นทุน ลดเวลา — ให้ธุรกิจคุณ"
badge={about.data.hero_badge}
title="เิ่มจากตรงนี้"
subtitle="บริษัท มอร์มินิมอร์ จำกัด — นโยบายของเราคือสร้างระบบที่ทำให้ลูกค้ามีกำไรมากขึ้น ไม่ใช่ทำเว็บไซต์ออกมาเยอะแล้วลืม เราเป็น Partner ที่นั่งทำงานข้างคุณจนระบบติดและใช้งานได้จริง"
/>
<!-- ABOUT CONTENT (BENTO) -->
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', left: '-150px' }} 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>
<BentoTile span={12} surface="soft" eyebrow="นโยบายของเรา" title="เป้าหมายคือเพิ่มกำไรให้ลูกค้า">
<p>เพราะถ้าลูกค้ามีกำไรมากขึ้น ลูกค้าก็จะสามารถใช้บริการเราต่อไปได้</p>
<p>ทุกระบบที่ส่งมอบต้องตอบคำถามเดียวให้ได้: <strong>"ลูกค้ามีกำไรเพิ่มขึ้นจริงไหม"</strong> — ไม่ใช่แค่ส่งงานตามสัญญา</p>
</BentoTile>
<BentoTile span={8} surface="yellow" eyebrow="วิธีทำงานของเรา" title="ไม่ได้ทำงานแบบเดียวกับทุกที่">
<p>เพราะลูกค้าแต่ละคนมีบริบท งบประมาณ และทีมต่างกัน</p>
<ul class="checklist">
<li>✅ <strong>งานที่ต้องใช้ความเชี่ยวชาญเฉพาะทาง</strong> — เราทำเอง เพราะถ้าไม่ทำเอง จะตอบคำถามลูกค้าไม่ได้</li>
<li>✅ <strong>งานที่ต้องทำซ้ำ ๆ ปริมาณเยอะ</strong> — เราใช้ bot หรือ outsource ให้คนเชี่ยวชาญ เพราะคนที่ทำเป็นอาชีพจะดีกว่า</li>
<li>✅ <strong>งานที่ต้องตอบลูกค้าตลอด 24 ชั่วโมง</strong> — bot ช่วยคัดกรองเบื้องต้น แล้วเราตามด้วยคน</li>
</ul>
<p style="margin-top: 16px;"><strong>สรุป:</strong> ผลงานที่ออกมาขึ้นอยู่กับบริบทของลูกค้า ไม่ใช่วิธีทำงานของเรา — เราเลือกวิธีที่ทำให้ลูกค้าได้ผลลัพธ์ที่ดีที่สุด ไม่ใช่วิธีที่เราถนัดที่สุด</p>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="ปี 2020" title="ก่อตั้งจากประสบการณ์ตรง">
<p>เห็น SME ไทยเสียเงินหลายแสนไปกับเว็บไซต์ "สวยแต่ไม่มีคนเข้า" และ AI tools "ว้าวแต่ใช้ไม่เป็น"</p>
</BentoTile>
<BentoTile span={6} surface="mint" eyebrow="เราเชื่ออะไร" title="หลักคิด 6 ข้อ">
<ul>
<li>เว็บสวยไม่ได้แปลว่าขายได้</li>
<li>AI ไม่ได้แทนทุกอย่าง — แต่ถ้ารู้จักใช้...</li>
<li>จ่ายแพงไม่ได้แปลว่าดี</li>
<li>"สิ่งที่ถูกต้อง" ไม่ใช่ "สิ่งที่ลูกค้าต้องการ" เสมอไป</li>
<li>ระบบที่คนใช้ไม่เป็น = เสียเงินเปล่า</li>
</ul>
</BentoTile>
<BentoTile span={6} surface="teal" eyebrow="เบื้องหลัง" title="Marketing intelligence ที่ใช้">
<p>นั่งทำงานที่บ้าน ใช้ MacBook ตัวเดียว แต่มีเครื่องมือที่ช่วยให้เข้าใจตลาดได้ลึกกว่าคนทั่วไป:</p>
<ul>
<li><strong>ระบบดูโฆษณาคู่แข่ง</strong> — ดูว่าคู่แข่งยิงโฆษณาอะไร คำไหน convert ดี</li>
<li><strong>วิเคราะห์ keyword + SEO gap</strong> — หาโอกาสที่คู่แข่งยังไม่ได้ทำ</li>
<li><strong>ติดตาม trend ตลาด</strong> — รู้ก่อนลูกค้าว่าตลาดกำลังไปทางไหน</li>
</ul>
<p style="margin-top: 12px;"><strong>Stack:</strong> JavaScript, Python, PHP, n8n, OpenAI API, Meta API, Google Analytics, Looker Studio</p>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- STORY SECTION -->
<section class="section story-section">
<div class="container">
<section class="section section-bento">
<DecoOrb color="yellow" size="400px" speed={0.5} position={{ top: '10%', right: '-150px' }} opacity={0.3} blur="80px" />
<DecoOrb color="mint" size="300px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.2} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="story-grid">
<div class="story-content">
<span class="section-badge">นโยบายของเรา</span>
@@ -34,7 +91,7 @@ import Icon from '../components/Icon.astro';
</p>
<p class="story-text">
<strong>ความต่างของเรา</strong> เราเขียนโค้ดเอง ไม่ใช่เอาไป Outsource เราตอบแชตเอง ไม่ใช่ใหBot ตอบลูกค้า เราวางแผนเอง ไม่ใช่ใช้ Template เดียวกับทุกคน
<strong>วิธีทำงานของเรา</strong> เราไม่ได้ทำงานแบบเดียวกับทุกที่ — งานที่ต้องใช้ความเชี่ยวชาญเฉพาะทางเราทำเอง งานที่ต้องทำซ้ำ ๆ ปริมาณเยอะเราใชbot หรือ outsource ให้คนเชี่ยวชาญราะคนที่ทำเป็นอาชีพจะดีกว่า
</p>
<div class="story-actions">
@@ -45,20 +102,20 @@ import Icon from '../components/Icon.astro';
<div class="story-stats stagger-children">
<div class="stat-card">
<span class="stat-number counter" data-from="0">40+</span>
<span class="stat-number">—</span>
<span class="stat-label">ลูกค้าที่กลับมาใช้บริการซ้ำ</span>
</div>
<div class="stat-card">
<span class="stat-number counter" data-from="0">50+</span>
<span class="stat-number">—</span>
<span class="stat-label">โปรเจกต์ที่ส่งมอบ</span>
</div>
<div class="stat-card">
<span class="stat-number counter" data-from="0">5+</span>
<span class="stat-label">ปีของการทำงานหนัก</span>
<span class="stat-label">ปีของการทำงานหนัก (ก่อตั้งปี 2020)</span>
</div>
<div class="stat-card">
<span class="stat-number counter" data-from="0">100%</span>
<span class="stat-label">โค้ดโดยทีมเรา ไม่ Outsource</span>
<span class="stat-number">—</span>
<span class="stat-label">อุตสาหกรรมที่รับ</span>
</div>
</div>
</div>
@@ -67,7 +124,9 @@ import Icon from '../components/Icon.astro';
<!-- VALUES SECTION (yellow accent cards on soft) -->
<section class="section section-soft values-section">
<div class="container">
<DecoOrb color="purple" size="500px" speed={0.4} position={{ top: '-200px', left: '20%' }} opacity={0.2} blur="100px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', right: '5%' }} opacity={0.4} 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">
@@ -76,33 +135,25 @@ import Icon from '../components/Icon.astro';
</div>
<div class="values-grid stagger-children">
<div class="value-card">
<div class="value-icon">
<Icon name="target" />
</div>
<div class="value-card value-card-yellow">
<div class="value-num">01</div>
<h3 class="value-title">เข้าใจธุรกิจก่อนเขียนโค้ด</h3>
<p class="value-desc">30 นาทีแรกของทุกโปรเจกต์คือการถาม ไม่ใช่การ present เราถามจนเข้าใจว่าคุณขายให้ใคร กำไรเท่าไหร่ ปวดหัวตรงไหน แล้วค่อยแนะนำ solution</p>
</div>
<div class="value-card">
<div class="value-icon">
<Icon name="users" />
</div>
<div class="value-num">02</div>
<h3 class="value-title">เป็น Partner ไม่ใช่ Vendor</h3>
<p class="value-desc">เราแชร์ progress ทุกสัปดาห์ผ่าน LINE Group เดียวกับที่ลูกค้าใช้ คุณเห็นทุก decision ไม่มี hidden cost ไม่มี "อันนี้เพิ่มเงินนะ" ตอนใกล้ deliver</p>
</div>
<div class="value-card">
<div class="value-icon">
<Icon name="clock" />
</div>
<div class="value-num">03</div>
<h3 class="value-title">Deliver ตรงเวลา หรือบอกล่วงหน้า</h3>
<p class="value-desc">เราไม่สัญญา deadline แบบเลื่อนได้ ถ้าจะติด เราจะบอกก่อน 7 วัน ไม่ใช่บอกตอนส่งงาน เคสไหนที่เคยส่งช้า เราคืนเงิน Pro-rata</p>
</div>
<div class="value-card">
<div class="value-icon">
<Icon name="shield" />
</div>
<div class="value-num">04</div>
<h3 class="value-title">ดูแลหลังส่งมอบ</h3>
<p class="value-desc">เว็บไซต์ที่ส่งแล้วทิ้งเป็นเว็บตาย เราเลยมีแพ็คเกจดูแลรายเดือนเริ่ม 2,000 บาท รวมอัปเดตเนื้อหา ปรับ SEO แก้บั๊ก ตอบคำถามผ่าน LINE</p>
<p class="value-desc">ปรับเล็ก ๆ น้อย ๆ ฟรี — คิดค่าใช้จ่ายเฉพาะ feature ใหม่หรือปรับแต่งครั้งใหญ่เท่านั้น</p>
</div>
</div>
</div>
@@ -122,7 +173,9 @@ import Icon from '../components/Icon.astro';
<!-- PROCESS SECTION (4 steps, white) -->
<section class="section process-section">
<div class="container">
<DecoOrb color="soft" size="600px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.5} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.3} position={{ bottom: '5%', 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">
@@ -140,17 +193,17 @@ import Icon from '../components/Icon.astro';
<div class="process-step">
<span class="step-num">02</span>
<h3 class="step-title">วางแผน</h3>
<p class="step-desc">วิเคราะห์เชิงลึก ดูคู่แข่ง ส่ง Proposal เป็นเอกสาร PDF คุณอ่าน ถามคำถาม แก้ไข scope ได้ก่อนเซ็น</p>
<p class="step-desc">วิเคราะห์เชิงลึก ดูคู่แข่ง ส่ง Proposal เป็น PDF คุณอ่าน ถามคำถาม แก้ไข scope ได้ก่อนเซ็น</p>
</div>
<div class="process-step">
<span class="step-num">03</span>
<h3 class="step-title">ดำเนินการ</h3>
<p class="step-desc">พัฒนาแบบ Sprint ส่ง demo ให้ทดสอบทุก 714 วัน เห็นงานจริง ไม่ใช่ "เดี๋ยวส่งทีเดียวตอนจบ"</p>
<p class="step-desc">แจ้งความคืบหน้าเป็นช่วง ๆ ผ่าน LINE Group — demo เมื่อเราพร้อมส่งงานรอบใหญ่ ๆ ไม่ sprint ไม่ deadline ล็อก</p>
</div>
<div class="process-step">
<div class="process-step process-step-yellow">
<span class="step-num">04</span>
<h3 class="step-title">สนับสนุน</h3>
<p class="step-desc">ส่งมอบงาน + อบรมทีม + มอบคู่มือ ติดตามผลทุกเดือน แนะนำสิ่งที่ควรปรับปรุง ต่อยอด หรือยกเลิก</p>
<h3 class="step-title">ส่งมอบ + ดูแล</h3>
<p class="step-desc">ส่งมอบงาน + อบรมทีม + มอบคู่มือ หลังส่งมอบปรับเล็ก ๆ น้อย ๆ ฟรี — คิดค่าใช้จ่ายเฉพาะ feature ใหม่หรือปรับแต่งครั้งใหญ่</p>
</div>
</div>
</div>
@@ -176,37 +229,123 @@ import Icon from '../components/Icon.astro';
<style>
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.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-bento {
position: relative;
overflow: hidden;
}
.section-title {
/* VALUES (keep existing classes) */
.values-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
max-width: 900px;
margin: 0 auto;
}
.value-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
transition: all 0.3s ease;
}
.value-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.value-num {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 44px);
font-size: 22px;
font-weight: 900;
line-height: 1.15;
color: var(--color-black);
width: 64px;
height: 64px;
border: 2px solid var(--color-black);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
letter-spacing: -1px;
}
.value-card-yellow { background: var(--color-primary); border-color: var(--color-primary); }
.value-card-yellow .value-num { background: var(--color-black); color: var(--color-primary); border-color: var(--color-black); }
.value-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 10px;
}
.value-desc {
font-size: 14px;
color: var(--color-gray-700);
line-height: 1.6;
}
.value-card-yellow .value-desc { color: rgba(0, 0, 0, 0.85); }
/* PROCESS — clean 4x1 grid */
.process-section { background: var(--color-bg-alt); position: relative; overflow: hidden; }
.process-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.process-step {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px 24px;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
min-height: 280px;
}
.process-step:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.process-step-yellow {
background: var(--color-primary);
border-color: var(--color-primary);
}
.process-step-yellow .step-num,
.process-step-yellow .step-title { color: var(--color-black); }
.process-step-yellow .step-desc { color: rgba(0, 0, 0, 0.85); }
.process-step-yellow:hover { border-color: var(--color-black); }
.step-num {
display: block;
font-family: var(--font-display);
font-size: clamp(40px, 4vw, 56px);
font-weight: 900;
color: var(--color-primary);
line-height: 1;
margin-bottom: 16px;
}
.section-title .highlight { color: var(--color-primary-dark); }
.section-desc {
font-size: 17px;
color: var(--color-gray-600);
.step-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 10px;
}
.step-desc {
font-size: 14px;
color: var(--color-gray-700);
line-height: 1.6;
}
/* STORY */
.story-section { background: var(--color-white); }
@media (max-width: 1024px) {
.process-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.process-grid { grid-template-columns: 1fr; }
.values-grid { grid-template-columns: 1fr; }
}
/* STORY (keep existing) */
.story-grid {
display: grid;
grid-template-columns: 1.3fr 0.7fr;
@@ -233,7 +372,7 @@ import Icon from '../components/Icon.astro';
}
.stat-card {
padding: 24px;
background: var(--color-bg-alt);
background: var(--color-bg-soft);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
transition: all 0.3s ease;
@@ -256,82 +395,35 @@ import Icon from '../components/Icon.astro';
color: var(--color-gray-600);
margin-top: 8px;
}
/* VALUES */
.values-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.value-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
text-align: center;
transition: all 0.3s ease;
}
.value-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.value-icon { font-size: 40px; margin-bottom: 16px; }
.value-title {
font-family: var(--font-display);
font-size: 16px;
font-weight: 800;
.section-badge {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
margin-bottom: 10px;
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 16px;
}
.value-desc {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
}
/* PROCESS */
.process-section { background: var(--color-bg-alt); }
.process-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.process-step {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
transition: all 0.3s ease;
}
.process-step:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.step-num {
display: block;
.section-title {
font-family: var(--font-display);
font-size: 36px;
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
color: var(--color-primary);
line-height: 1;
margin-bottom: 12px;
}
.step-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
line-height: 1.15;
color: var(--color-black);
margin-bottom: 10px;
margin-bottom: 16px;
}
.step-desc {
font-size: 14px;
.section-title .highlight { color: var(--color-primary-dark); }
.section-desc {
font-size: 17px;
color: var(--color-gray-600);
line-height: 1.6;
}
.section-header { text-align: center; margin-bottom: 48px; }
/* PULL QUOTE (dark band) */
/* PULL QUOTE */
.section-dark-quote {
background: var(--color-black);
padding: 100px 0;
@@ -381,16 +473,25 @@ import Icon from '../components/Icon.astro';
flex-wrap: wrap;
}
/* RESPONSIVE */
@media (max-width: 1024px) {
.story-grid { grid-template-columns: 1fr; gap: 40px; }
.values-grid { grid-template-columns: repeat(2, 1fr); }
.process-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.story-actions, .cta-actions { flex-direction: column; }
.story-actions .btn, .cta-actions .btn { width: 100%; justify-content: center; }
.values-grid { grid-template-columns: 1fr; }
.process-grid { grid-template-columns: 1fr; }
}
</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>

View File

@@ -3,6 +3,9 @@ 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, render } from 'astro:content';
const { slug } = Astro.params;
@@ -33,6 +36,12 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
month: 'long',
day: 'numeric'
});
// Surface rotation for related cards
const surfaces = ['soft', 'yellow', 'mint'] as const;
function surfaceFor(i: number) {
return surfaces[i % surfaces.length];
}
---
<Base title={`${post.data.title} | MoreminiMore`}>
@@ -45,104 +54,143 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
/>
{post.data.image && (
<section class="section section-bento article-image-section">
<DecoOrb color="soft" size="500px" speed={0.3} position={{ top: '-100px', right: '-150px' }} opacity={0.4} blur="80px" />
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.2} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="article-image">
<div class="container">
<img src={post.data.image} alt={post.data.title} />
</div>
</div>
</section>
)}
<section class="section article-section">
<div class="container">
<div class="article-grid reveal">
<article class="article-content">
<section class="section section-bento article-section">
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '5%', left: '-150px' }} opacity={0.2} blur="80px" />
<DecoOrb color="mint" size="350px" speed={0.3} position={{ bottom: '10%', right: '-100px' }} opacity={0.2} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<!-- Main article body — full width bento tile -->
<BentoTile span={8} surface="white" eyebrow={post.data.category} title={post.data.title}>
<div class="article-meta">
<span class="article-date">{formattedDate}</span>
</div>
<div class="article-body">
<Content />
</div>
</article>
</BentoTile>
<aside class="article-sidebar">
<div class="sidebar-card">
<h3 class="sidebar-title">เกี่ยวกับ MoreminiMore</h3>
<p class="sidebar-text">
ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่
</p>
<!-- Sidebar tile: about + contact stack -->
<BentoTile span={4} surface="soft" eyebrow="เกี่ยวกับเรา" title="MoreminiMore">
<p>ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่</p>
<a href="/about" class="btn btn-outline-dark btn-sm">ดูเพิ่มเติม</a>
</div>
<div class="sidebar-card">
<h3 class="sidebar-title">สนใจบริการ?</h3>
<p class="sidebar-text">ติดต่อเราได้เลย ปรึกษาฟรี!</p>
<div class="sidebar-divider"></div>
<span class="tile-eyebrow-sm">สนใจบริการ?</span>
<p style="margin-top: 8px;">ติดต่อเราได้เลย ปรึกษาฟรี!</p>
<div class="sidebar-actions">
<a href="/contact" class="btn btn-primary btn-sm">ติดต่อเรา</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm" style="margin-top: 8px;">080-995-5945</a>
</div>
{related.length > 0 && (
<div class="sidebar-card">
<h3 class="sidebar-title">บทความที่เกี่ยวข้อง</h3>
<div class="related-list">
{related.map(r => (
<a href={`/blog/${r.id}`} class="related-item">
<img src={r.data.image} alt={r.data.title} />
<span>{r.data.title}</span>
</a>
))}
</div>
</div>
)}
</aside>
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm">080-995-5945</a>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
{related.length > 0 && (
<section class="section section-bento related-section">
<DecoOrb color="yellow" size="400px" speed={0.3} position={{ top: '10%', right: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="soft" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} 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>
{related.map((r, i) => (
<a href={`/blog/${r.id}`} class="related-link">
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={r.data.category} title={r.data.title}>
<div class="related-card-body">
{r.data.image && (
<div class="related-image">
<img src={r.data.image} alt={r.data.title} loading="lazy" />
</div>
)}
<span class="related-date">
{new Date(r.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
</span>
</div>
</BentoTile>
</a>
))}
</BentoGrid>
</div>
</section>
)}
<Footer />
</Base>
<style>
.article-image-section { background: var(--color-white); padding-top: 40px; padding-bottom: 40px; }
.article-section { background: var(--color-white); }
.related-section { background: var(--color-bg-alt); }
.section-bento { position: relative; overflow: hidden; }
/* Article image (hero) */
.article-image {
padding: 40px 0;
background: var(--color-bg-alt);
width: 100%;
overflow: hidden;
border-radius: var(--radius-xl);
background: var(--color-bg-soft);
}
.article-image img {
width: 100%;
max-height: 500px;
object-fit: cover;
border-radius: var(--radius-xl);
display: block;
}
.article-section { background: var(--color-white); }
.article-grid {
display: grid;
grid-template-columns: 1fr 360px;
gap: 60px;
/* Main article body */
.article-meta {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-gray-200);
}
.article-date {
font-size: 13px;
font-weight: 700;
color: var(--color-gray-600);
text-transform: uppercase;
letter-spacing: 1px;
}
.article-body {
font-size: 18px;
font-size: 17px;
line-height: 1.8;
color: var(--color-gray-700);
}
.article-body :global(h2) {
font-family: var(--font-display);
font-size: 28px;
font-size: 26px;
font-weight: 800;
color: var(--color-black);
margin: 40px 0 20px;
margin: 36px 0 18px;
}
.article-body :global(h3) {
font-family: var(--font-display);
font-size: 22px;
font-size: 20px;
font-weight: 800;
color: var(--color-black);
margin: 32px 0 16px;
margin: 28px 0 14px;
}
.article-body :global(p) { margin-bottom: 20px; }
.article-body :global(p) { margin-bottom: 18px; }
.article-body :global(ul), .article-body :global(ol) {
margin: 20px 0;
margin: 16px 0;
padding-left: 24px;
}
.article-body :global(li) { margin-bottom: 12px; }
.article-body :global(li) { margin-bottom: 10px; }
.article-body :global(a) {
color: var(--color-primary-dark);
font-weight: 600;
@@ -150,40 +198,37 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
.article-body :global(a:hover) { text-decoration: underline; }
.article-body :global(blockquote) {
border-left: 4px solid var(--color-primary);
padding-left: 24px;
margin: 32px 0;
padding-left: 20px;
margin: 28px 0;
font-style: italic;
color: var(--color-gray-700);
}
.article-body :global(img) {
border-radius: var(--radius-md);
margin: 20px 0;
max-width: 100%;
}
.article-body :global(strong) { color: var(--color-black); font-weight: 800; }
/* Sidebar (soft tile) */
.sidebar-divider {
height: 1px;
background: var(--color-gray-200);
margin: 24px 0;
}
/* Sidebar */
.article-sidebar {
.tile-eyebrow-sm {
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
opacity: 0.7;
display: block;
}
.sidebar-actions {
display: flex;
flex-direction: column;
gap: 24px;
}
.sidebar-card {
background: var(--color-bg-alt);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 28px;
}
.sidebar-title {
font-family: var(--font-display);
font-size: 16px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 12px;
}
.sidebar-text {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
gap: 8px;
margin-top: 12px;
}
.btn-sm {
padding: 10px 20px;
@@ -197,39 +242,75 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
justify-content: center;
}
.related-list {
/* 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, 40px);
font-weight: 900;
color: var(--color-black);
}
.section-title .highlight { color: var(--color-primary-dark); }
/* Related cards */
.related-link {
display: block;
text-decoration: none;
color: inherit;
}
.related-card-body {
display: flex;
flex-direction: column;
gap: 12px;
}
.related-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
background: var(--color-white);
.related-image {
aspect-ratio: 16/10;
overflow: hidden;
border-radius: var(--radius-md);
transition: all 0.2s ease;
background: var(--color-bg-soft);
margin: -8px -8px 4px;
}
.related-item:hover { transform: translateX(4px); }
.related-item img {
width: 50px;
height: 50px;
.related-image img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--radius-sm);
flex-shrink: 0;
display: block;
transition: transform 0.5s ease;
}
.related-item span {
font-size: 13px;
font-weight: 700;
color: var(--color-black);
line-height: 1.3;
.related-link:hover .related-image img { transform: scale(1.06); }
.related-date {
font-size: 12px;
font-weight: 600;
opacity: 0.65;
}
@media (max-width: 1024px) {
.article-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.article-body { font-size: 16px; }
}
</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>

View File

@@ -3,10 +3,19 @@ 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 blogPosts = await getCollection('blog');
const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
// Surface color rotation for variety across the post grid
const surfaces = ['white', 'soft', 'yellow', 'mint', 'purple-soft', 'teal'] as const;
function surfaceFor(i: number) {
return surfaces[i % surfaces.length];
}
---
<Base title="บทความ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
@@ -19,19 +28,30 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
/>
{sortedPosts.length > 0 && (
<section class="featured-section section-soft">
<div class="container">
<a href={`/blog/${sortedPosts[0].id}`} class="featured-card">
<section class="section section-bento featured-section">
<DecoOrb color="yellow" size="450px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.3} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<a href={`/blog/${sortedPosts[0].id}`} class="featured-tile-link">
<BentoGrid>
<BentoTile span={7} surface="white" eyebrow="บทความล่าสุด" title={sortedPosts[0].data.title}>
<div class="featured-content">
<div class="featured-image">
{sortedPosts[0].data.image && (
<img src={sortedPosts[0].data.image} alt={sortedPosts[0].data.title} loading="eager" />
)}
<span class="featured-badge">บทความล่าสุด</span>
</div>
<div class="featured-content">
<span class="category-badge">{sortedPosts[0].data.category}</span>
<h2 class="featured-title">{sortedPosts[0].data.title}</h2>
</div>
</BentoTile>
<BentoTile span={5} surface="yellow" eyebrow={sortedPosts[0].data.category} title="อ่านบทความเต็ม">
<div class="featured-aside">
<p class="featured-excerpt">{sortedPosts[0].data.excerpt}</p>
<div class="featured-meta">
<span class="featured-date">
{new Date(sortedPosts[0].data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
</span>
</div>
<span class="read-more">
อ่านต่อ
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -39,37 +59,43 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
</svg>
</span>
</div>
</BentoTile>
</BentoGrid>
</a>
</div>
</section>
)}
<section class="section blog-section">
<div class="container">
<div class="section-header">
<section class="section section-bento blog-section">
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '5%', right: '-150px' }} opacity={0.2} blur="80px" />
<DecoOrb color="purple" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.2} 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>
<div class="blog-grid">
{sortedPosts.length > 1 && (
<BentoGrid>
{sortedPosts.slice(1).map((post, i) => (
<a href={`/blog/${post.id}`} class="blog-card" style={`--delay: ${i * 0.1}s`}>
<div class="blog-image">
<a href={`/blog/${post.id}`} class="post-tile-link">
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={post.data.category} title={post.data.title} reveal={true}>
<div class="post-card-body">
{post.data.image && (
<div class="post-image">
<img src={post.data.image} alt={post.data.title} loading="lazy" />
)}
</div>
<div class="blog-content">
<span class="blog-category">{post.data.category}</span>
<h3 class="blog-title">{post.data.title}</h3>
<p class="blog-excerpt">{post.data.excerpt}</p>
<span class="blog-date">
)}
<p class="post-excerpt">{post.data.excerpt}</p>
<span class="post-date">
{new Date(post.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
</span>
</div>
</BentoTile>
</a>
))}
</div>
</BentoGrid>
)}
</div>
</section>
@@ -92,77 +118,46 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
<style>
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.section-bento { position: relative; overflow: hidden; }
.featured-section { background: var(--color-bg-alt); padding-top: 60px; padding-bottom: 60px; }
.blog-section { background: var(--color-white); }
/* Featured */
.featured-section { padding: 60px 0; }
.featured-card {
display: grid;
grid-template-columns: 1.5fr 1fr;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
transition: all 0.4s ease;
}
.featured-card:hover {
transform: scale(1.01);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
/* Make the whole featured tile clickable without breaking inner link */
.featured-tile-link {
display: block;
text-decoration: none;
color: inherit;
}
.featured-content { display: flex; flex-direction: column; gap: 16px; }
.featured-image {
position: relative;
aspect-ratio: 16/10;
overflow: hidden;
border-radius: var(--radius-md);
background: var(--color-bg-soft);
}
.featured-image img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.featured-badge {
position: absolute;
top: 16px;
left: 16px;
background: var(--color-primary);
color: var(--color-black);
padding: 6px 14px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.featured-content {
padding: 40px;
.featured-aside {
display: flex;
flex-direction: column;
justify-content: center;
}
.category-badge {
display: inline-block;
background: var(--color-bg-alt);
color: var(--color-gray-600);
padding: 4px 12px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
align-self: flex-start;
margin-bottom: 16px;
}
.featured-title {
font-family: var(--font-display);
font-size: clamp(22px, 3vw, 28px);
font-weight: 800;
color: var(--color-black);
margin-bottom: 12px;
line-height: 1.3;
gap: 16px;
}
.featured-excerpt {
font-size: 14px;
color: var(--color-gray-600);
font-size: 16px;
line-height: 1.7;
margin-bottom: 20px;
color: var(--color-black);
}
.featured-meta { font-size: 13px; }
.featured-date {
font-weight: 700;
color: var(--color-black);
opacity: 0.7;
}
.read-more {
display: inline-flex;
@@ -175,9 +170,48 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
text-transform: uppercase;
letter-spacing: 1px;
}
.read-more:hover { color: var(--color-primary-dark); }
.read-more svg { width: 18px; height: 18px; }
/* Post grid cards */
.post-tile-link {
display: block;
text-decoration: none;
color: inherit;
}
.post-card-body {
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
}
.post-image {
aspect-ratio: 16/10;
overflow: hidden;
border-radius: var(--radius-md);
background: var(--color-bg-soft);
margin: -8px -8px 4px;
}
.post-image img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 0.5s ease;
}
.post-tile-link:hover .post-image img { transform: scale(1.06); }
.post-excerpt {
font-size: 14px;
line-height: 1.6;
color: inherit;
opacity: 0.85;
}
.post-date {
font-size: 12px;
font-weight: 600;
opacity: 0.6;
margin-top: auto;
}
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
@@ -200,65 +234,6 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
}
.section-title .highlight { color: var(--color-primary-dark); }
/* Blog grid */
.blog-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 32px;
}
.blog-card {
display: block;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
transition: all 0.4s ease;
}
.blog-card:hover {
transform: translateY(-8px);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.blog-image { aspect-ratio: 16/10; overflow: hidden; background: var(--color-bg-soft); }
.blog-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.blog-card:hover .blog-image img { transform: scale(1.08); }
.blog-content { padding: 24px; }
.blog-category {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
}
.blog-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 12px;
line-height: 1.3;
}
.blog-excerpt {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
}
.blog-date {
font-size: 12px;
color: var(--color-gray-500);
}
/* CTA */
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
.cta-title {
@@ -280,13 +255,25 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
flex-wrap: wrap;
}
@media (max-width: 1024px) {
.featured-card { grid-template-columns: 1fr; }
.blog-grid { grid-template-columns: repeat(2, 1fr); }
}
/* Reduce padding on bento tiles that are link wrappers so the image breathes */
.post-tile-link :global(.bento-tile) { padding: 16px; }
@media (max-width: 640px) {
.blog-grid { grid-template-columns: 1fr; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</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>

View File

@@ -4,6 +4,9 @@ import Navigation from '../components/Navigation.astro';
import Footer from '../components/Footer.astro';
import PageHero from '../components/PageHero.astro';
import Icon from '../components/Icon.astro';
import BentoGrid from '../components/BentoGrid.astro';
import BentoTile from '../components/BentoTile.astro';
import DecoOrb from '../components/DecoOrb.astro';
// Service options for the form — with lucide icon names
const serviceOptions = [
@@ -26,105 +29,43 @@ const serviceOptions = [
subtitle="ตอบกลับภายใน 2 ชั่วโมง · เลือกช่องทางที่คุณสะดวก — LINE, โทร, Email หรือฟอร์ม"
/>
<!-- Quick Channel Picker -->
<section class="section channels-pick-section">
<div class="container">
<div class="channels-pick-grid stagger-children">
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="channel-pick-card">
<div class="channel-pick-icon">
<Icon name="message" size={32} />
</div>
<h3 class="channel-pick-name">LINE Official</h3>
<p class="channel-pick-best">คนที่อยากคุยเร็ว ๆ แบบเป็นกันเอง</p>
<p class="channel-pick-time">ตอบใน 30 นาที (เวลาทำการ)</p>
<span class="channel-pick-cta">ทักเลย →</span>
</a>
<a href="tel:0809955945" class="channel-pick-card">
<div class="channel-pick-icon">
<Icon name="phone" size={32} />
</div>
<h3 class="channel-pick-name">โทรศัพท์</h3>
<p class="channel-pick-best">คนที่อยากคุยยาว ๆ 510 นาที ถามตอบสด</p>
<p class="channel-pick-time">รับสายทันที หรือโทรกลับภายใน 2 ชม.</p>
<span class="channel-pick-cta">080-995-5945</span>
</a>
<a href="mailto:contact@moreminimore.com" class="channel-pick-card">
<div class="channel-pick-icon">
<Icon name="mail" size={32} />
</div>
<h3 class="channel-pick-name">Email</h3>
<p class="channel-pick-best">คนที่อยากส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
<p class="channel-pick-time">ตอบภายใน 1 วันทำการ</p>
<span class="channel-pick-cta">contact@moreminimore.com</span>
</a>
</div>
<!-- QUICK CHANNEL PICKER (BENTO) -->
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<BentoTile span={4} surface="yellow" eyebrow="LINE Official" title="@moreminimore">
<div class="channel-icon"><Icon name="message" size={32} /></div>
<p>คนที่อยากคุยเร็ว ๆ แบบเป็นกันเอง</p>
<p class="meta">ตอบใน 30 นาที (เวลาทำการ)</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">
<div class="channel-icon"><Icon name="phone" size={32} /></div>
<p>คนที่อยากคุยยาว ๆ 510 นาที ถามตอบสด</p>
<p class="meta">รับสายทันที หรือโทรกลับภายใน 2 ชม.</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">
<div class="channel-icon"><Icon name="mail" size={32} /></div>
<p>คนที่อยากส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
<p class="meta">ตอบภายใน 1 วันทำการ</p>
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 16px;">ส่งอีเมล →</a>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- Contact Form Section -->
<section class="section form-section">
<div class="container">
<div class="form-grid reveal">
<!-- Info column -->
<div class="info-column">
<h2 class="info-title">ข้อมูลติดต่อ</h2>
<div class="info-item">
<div class="info-icon">
<Icon name="phone" size={22} />
</div>
<div>
<h3>โทรศัพท์</h3>
<a href="tel:0809955945">080-995-5945</a>
<p>โทรคุยสดได้เลย จ-ศ 09:00-18:00</p>
</div>
</div>
<div class="info-item">
<div class="info-icon">
<Icon name="mail" size={22} />
</div>
<div>
<h3>อีเมล</h3>
<a href="mailto:contact@moreminimore.com">contact@moreminimore.com</a>
<p>เหมาะกับส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
</div>
</div>
<div class="info-item">
<div class="info-icon">
<Icon name="message" size={22} />
</div>
<div>
<h3>LINE</h3>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener">@moreminimore</a>
<p>เร็วที่สุด ตอบใน 30 นาที (เวลาทำการ)</p>
</div>
</div>
<div class="info-item">
<div class="info-icon">
<Icon name="mapPin" size={22} />
</div>
<div>
<h3>ออฟฟิศ</h3>
<p>53 หมู่ 1 ต.บ้านแพ้ว อ.บ้านแพ้ว สมุทรสาคร 74120</p>
<p>นัดเจอล่วงหน้า สะดวกกว่าเดินเข้ามาเฉย ๆ</p>
</div>
</div>
<div class="info-item">
<div class="info-icon">
<Icon name="clock" size={22} />
</div>
<div>
<h3>เวลาทำการ</h3>
<p>จันทร์ - ศุกร์ 09:00 - 18:00 น.</p>
<p>นอกเวลา? ทัก LINE ทิ้งไว้ได้ ตอบเช้าวันถัดไป</p>
</div>
</div>
</div>
<!-- Form column -->
<div class="form-column">
<h2 class="form-title">ส่งข้อความถึงเรา</h2>
<p class="form-subtitle">กรอก 4 ช่อง ใช้เวลา 60 วินาที — เราจะตอบกลับภายใน 2 ชั่วโมง (เวลาทำการ)</p>
<!-- CONTACT FORM + INFO (BENTO) -->
<section class="section form-section section-bento">
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '10%', right: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '5%', left: '-100px' }} opacity={0.2} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<!-- FORM — big tile on the left -->
<BentoTile span={8} surface="white" eyebrow="ส่งข้อความ" title="กรอก 4 ช่อง ใช้เวลา 60 วินาที">
<p class="form-subtitle">เราจะตอบกลับภายใน 2 ชั่วโมง (เวลาทำการ)</p>
<form class="contact-form" id="contact-form">
<div class="form-row">
@@ -189,66 +130,86 @@ const serviceOptions = [
<h3>ส่งแล้ว!</h3>
<p>เราจะตอบกลับภายใน 2 ชั่วโมง (ในเวลาทำการ) ถ้าเร่งด่วน ทัก LINE @moreminimore ครับ</p>
</div>
</div>
</div>
</BentoTile>
<!-- INFO TILES — phone, email, line, hours as separate BentoTiles -->
<BentoTile span={4} surface="yellow" eyebrow="โทรศัพท์" title="080-995-5945">
<div class="info-icon"><Icon name="phone" size={24} /></div>
<p>โทรคุยสดได้เลย</p>
<p class="meta">จ-ศ 09:00-18:00</p>
<a href="tel:0809955945" class="btn btn-dark" style="margin-top: 12px;">โทรเลย →</a>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="อีเมล" title="contact@moreminimore.com">
<div class="info-icon"><Icon name="mail" size={24} /></div>
<p>เหมาะกับส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
<p class="meta">ตอบภายใน 1 วันทำการ</p>
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 12px;">ส่งอีเมล →</a>
</BentoTile>
<BentoTile span={4} surface="mint" eyebrow="LINE Official" title="@moreminimore">
<div class="info-icon"><Icon name="message" size={24} /></div>
<p>เร็วที่สุด ตอบใน 30 นาที (เวลาทำการ)</p>
<p class="meta">นอกเวลา? ทักทิ้งไว้ได้</p>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-dark" style="margin-top: 12px;">ทักเลย →</a>
</BentoTile>
<BentoTile span={12} surface="dark" eyebrow="เวลาทำการ" title="จันทร์ - ศุกร์ 09:00 - 18:00 น.">
<div class="info-icon"><Icon name="clock" size={24} /></div>
<p>นอกเวลาทำการ? ทัก LINE ทิ้งไว้ได้ ตอบเช้าวันถัดไป</p>
<p class="meta" style="color: rgba(255,255,255,0.7);">53 หมู่ 1 ต.บ้านแพ้ว อ.บ้านแพ้ว สมุทรสาคร 74120 · นัดเจอล่วงหน้า</p>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- What Happens Next -->
<section class="section section-soft next-section">
<div class="container">
<!-- WHAT HAPPENS NEXT (BENTO) -->
<section class="section section-soft section-bento">
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} 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">3 ขั้นตอนถัดไป — <span class="highlight">ไม่มีอะไรซับซ้อน</span></h2>
</div>
<div class="next-grid stagger-children">
<div class="next-step">
<div class="next-num">1</div>
<h3>ตอบกลับภายใน 2 ชั่วโมง</h3>
<BentoGrid>
<BentoTile span={4} surface="yellow" eyebrow="ขั้นที่ 1" title="ตอบกลับภายใน 2 ชั่วโมง">
<p>คนจริง (ไม่ใช่ Bot) จะตอบ — ถามคำถามเพิ่ม 23 ข้อ เพื่อเข้าใจปัญหาคุณ</p>
</div>
<div class="next-step">
<div class="next-num">2</div>
<h3>นัดปรึกษาฟรี 30 นาที</h3>
</BentoTile>
<BentoTile span={4} surface="soft" eyebrow="ขั้นที่ 2" title="นัดปรึกษาฟรี 30 นาที">
<p>คุยผ่าน Zoom / โทร / นัดเจอที่ออฟฟิศ (กรุงเทพ / สมุทรสาคร) — ไม่มี script sales</p>
</div>
<div class="next-step">
<div class="next-num">3</div>
<h3>ส่ง Proposal (35 วัน)</h3>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="ขั้นที่ 3" title="ส่ง Proposal (35 วัน)">
<p>เอกสาร PDF ที่ระบุ scope, timeline, ราคา — ไม่ชอบตรงไหนคุยกันแก้ได้</p>
</div>
</div>
</BentoTile>
</BentoGrid>
<p class="next-closing">ถ้าไม่ตรง → เราจะบอกตรง ๆ ว่า "ไม่เหมาะ" และแนะนำทางเลือกอื่น</p>
</div>
</section>
<!-- Pre-submit FAQ -->
<section class="section pre-faq-section">
<div class="container">
<!-- PRE-SUBMIT FAQ (BENTO) -->
<section class="section section-bento">
<DecoOrb color="teal" size="400px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.2} blur="80px" />
<DecoOrb color="soft" size="300px" speed={0.4} position={{ bottom: '-100px', left: '-50px' }} opacity={0.35} 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">4 คำถามที่คนถาม <span class="highlight">ก่อน</span> กดส่งฟอร์ม</h2>
</div>
<div class="pre-faq-list stagger-children">
<div class="pre-faq-item">
<h3>คุยกัน 30 นาทีแล้วจะถูกบังคับซื้อไหม?</h3>
<BentoGrid>
<BentoTile span={6} surface="soft" eyebrow="01" title="คุยกัน 30 นาทีแล้วจะถูกบังคับซื้อไหม?">
<p>ไม่ คุยแล้วคุณไม่ชอบก็ไม่เป็นไร ไม่มี follow-up ไม่มีขายของเพิ่ม</p>
</div>
<div class="pre-faq-item">
<h3>ถ้าส่งฟอร์มไปแล้วเงียบ ทำยังไง?</h3>
</BentoTile>
<BentoTile span={6} surface="soft" eyebrow="02" title="ถ้าส่งฟอร์มไปแล้วเงียบ ทำยังไง?">
<p>ทัก LINE @moreminimore ตรง ๆ จะเร็วกว่า — หรือโทร 080-995-5945</p>
</div>
<div class="pre-faq-item">
<h3>คุยช่วงไหนได้บ้าง?</h3>
</BentoTile>
<BentoTile span={6} surface="yellow" eyebrow="03" title="คุยช่วงไหนได้บ้าง?">
<p>จันทร์-ศุกร์ 09:00-18:00 ปกติ ถ้าคุณต่างจังหวัด/ต่างประเทศ นัดนอกเวลาได้ บอกล่วงหน้า 12 วัน</p>
</div>
<div class="pre-faq-item">
<h3>ต้องเตรียมอะไรไปคุยไหม?</h3>
</BentoTile>
<BentoTile span={6} surface="purple-soft" eyebrow="04" title="ต้องเตรียมอะไรไปคุยไหม?">
<p>ไม่ต้องเตรียมอะไรเลย แค่บอกธุรกิจคุณทำอะไร ปวดหัวเรื่องอะไร งบประมาณเท่าไหร่ ที่เหลือเราถามเอง</p>
</div>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
@@ -269,84 +230,26 @@ const serviceOptions = [
<Footer />
</Base>
<script>
const form = document.getElementById('contact-form') as HTMLFormElement;
const success = document.getElementById('form-success');
form?.addEventListener('submit', (e) => {
e.preventDefault();
const btn = form.querySelector('.btn-submit') as HTMLButtonElement;
const btnText = btn.querySelector('.btn-text');
const originalText = btnText?.textContent;
if (btnText) btnText.textContent = 'กำลังส่ง...';
btn.disabled = true;
// Simulate submission (replace with real endpoint)
setTimeout(() => {
form.hidden = true;
if (success) success.hidden = false;
if (btnText) btnText.textContent = originalText;
btn.disabled = false;
// Scroll to success
success?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 800);
});
</script>
<style>
.channels-pick-section { background: var(--color-white); padding: 60px 0; }
.section-bento {
position: relative;
overflow: hidden;
}
.form-section { background: var(--color-bg-alt); }
.section-soft { background: var(--color-bg-alt); }
.pre-faq-section { background: var(--color-white); }
.section-yellow { background: var(--color-primary); }
/* Channel picker */
.channels-pick-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.channel-pick-card {
display: block;
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 28px;
text-align: center;
transition: all 0.3s ease;
}
.channel-pick-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.channel-pick-icon { font-size: 40px; margin-bottom: 12px; }
.channel-pick-name {
font-family: var(--font-display);
font-size: 20px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.channel-pick-best {
font-size: 14px;
/* Channel tiles */
.channel-icon { font-size: 40px; margin-bottom: 12px; color: var(--color-black); }
.meta {
font-size: 13px;
color: var(--color-gray-600);
margin-bottom: 8px;
}
.channel-pick-time {
font-size: 12px;
color: var(--color-gray-500);
margin-bottom: 16px;
}
.channel-pick-cta {
display: inline-block;
padding: 10px 20px;
background: var(--color-primary);
color: var(--color-black);
border-radius: var(--radius-md);
font-size: 14px;
font-weight: 700;
margin-top: 4px;
}
.surface-yellow .meta { color: rgba(0, 0, 0, 0.7); }
.surface-purple-soft .meta { color: var(--color-gray-700); }
.surface-mint .meta { color: var(--color-gray-700); }
.surface-dark .meta { color: rgba(255, 255, 255, 0.75); }
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
@@ -364,30 +267,39 @@ const serviceOptions = [
}
.section-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 40px);
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
line-height: 1.2;
line-height: 1.15;
color: var(--color-black);
margin-bottom: 16px;
}
.section-title .highlight { color: var(--color-primary-dark); }
/* Form grid */
.form-grid {
display: grid;
grid-template-columns: 1fr 1.3fr;
gap: 60px;
/* Info icon (used inside separate info tiles) */
.info-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 48px;
}
.info-title, .form-title {
font-family: var(--font-display);
font-size: 28px;
font-weight: 900;
border-radius: var(--radius-md);
margin-bottom: 12px;
color: var(--color-black);
margin-bottom: 32px;
}
.surface-dark .info-icon {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.2);
color: var(--color-primary);
}
.surface-yellow .info-icon {
background: var(--color-black);
border-color: var(--color-black);
color: var(--color-primary);
}
/* Form styles (kept, just slightly tightened) */
.form-subtitle {
font-size: 15px;
color: var(--color-gray-600);
@@ -395,41 +307,6 @@ const serviceOptions = [
line-height: 1.5;
}
.info-item {
display: flex;
gap: 16px;
margin-bottom: 24px;
align-items: flex-start;
}
.info-icon {
font-size: 22px;
flex-shrink: 0;
width: 40px;
height: 40px;
background: var(--color-bg-alt);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
}
.info-item h3 {
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--color-gray-500);
margin-bottom: 4px;
}
.info-item a, .info-item p {
font-size: 15px;
color: var(--color-black);
line-height: 1.5;
font-weight: 500;
}
.info-item a:hover { color: var(--color-primary-dark); }
.info-item p { color: var(--color-gray-600); }
/* Form fields */
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -440,7 +317,7 @@ const serviceOptions = [
}
.form-label {
display: block;
font-size: 13px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
@@ -489,6 +366,7 @@ const serviceOptions = [
.success-icon {
font-size: 64px;
margin-bottom: 16px;
color: var(--color-primary-dark);
}
.form-success h3 {
font-family: var(--font-display);
@@ -505,44 +383,7 @@ const serviceOptions = [
margin: 0 auto;
}
/* What happens next */
.next-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
max-width: 900px;
margin: 0 auto;
}
.next-step {
text-align: center;
padding: 32px;
}
.next-num {
width: 56px;
height: 56px;
background: var(--color-primary);
color: var(--color-black);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-display);
font-size: 24px;
font-weight: 900;
margin: 0 auto 16px;
}
.next-step h3 {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.next-step p {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
}
/* Next step closing line */
.next-closing {
text-align: center;
margin-top: 32px;
@@ -550,32 +391,7 @@ const serviceOptions = [
color: var(--color-gray-700);
}
/* Pre-submit FAQ */
.pre-faq-list {
max-width: 800px;
margin: 0 auto;
}
.pre-faq-item {
background: var(--color-bg-alt);
border-left: 4px solid var(--color-primary);
border-radius: var(--radius-md);
padding: 20px 24px;
margin-bottom: 16px;
}
.pre-faq-item h3 {
font-family: var(--font-display);
font-size: 16px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.pre-faq-item p {
font-size: 15px;
color: var(--color-gray-700);
line-height: 1.5;
}
/* Final CTA */
/* CTA */
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
.cta-title {
font-family: var(--font-display);
@@ -598,13 +414,47 @@ const serviceOptions = [
}
@media (max-width: 1024px) {
.channels-pick-grid { grid-template-columns: 1fr; }
.form-grid { grid-template-columns: 1fr; gap: 40px; padding: 32px; }
.form-row { grid-template-columns: 1fr; }
.next-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</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 });
// Form submission
const form = document.getElementById('contact-form') as HTMLFormElement;
const success = document.getElementById('form-success');
form?.addEventListener('submit', (e) => {
e.preventDefault();
const btn = form.querySelector('.btn-submit') as HTMLButtonElement;
const btnText = btn.querySelector('.btn-text');
const originalText = btnText?.textContent;
if (btnText) btnText.textContent = 'กำลังส่ง...';
btn.disabled = true;
// Simulate submission (replace with real endpoint)
setTimeout(() => {
form.hidden = true;
if (success) success.hidden = false;
if (btnText) btnText.textContent = originalText;
btn.disabled = false;
// Scroll to success
success?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 800);
});
</script>

View File

@@ -3,20 +3,65 @@ 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 Icon from '../components/Icon.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 & เทคนิค', 'หลังการขาย'];
// Map categories to lucide icon names (semantic match, not visual emoji)
const categoryIcons: Record<string, string> = {
'บริการ': 'briefcase',
'ราคา': 'dollarSign',
'ระยะเวลา': 'clock',
'AI & เทคนิค': 'bot',
'หลังการขาย': 'wrench',
};
// 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">
@@ -28,21 +73,20 @@ const categoryIcons: Record<string, string> = {
subtitle="30+ คำถามที่รวบรวมจากแชต LINE จริง ๆ ไม่ใช่แต่งขึ้นเอง"
/>
<section class="section faq-section">
<div class="container">
{categories.map(cat => {
const items = faqItems.filter(f => f.data.category === cat);
if (items.length === 0) return null;
return (
<div class="faq-category reveal">
<h2 class="category-title">
<span class="category-icon">
<Icon name={categoryIcons[cat] as any} size={18} />
</span>
{cat}
</h2>
<!-- 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">
{items.map((item, qIndex) => (
{tile.items.map((item) => (
<details class="faq-item">
<summary class="faq-question">
<span class="question-text">{item.data.question}</span>
@@ -54,18 +98,11 @@ const categoryIcons: Record<string, string> = {
</details>
))}
</div>
</div>
);
})}
</BentoTile>
))}
<!-- Other Topics -->
<div class="faq-category">
<h2 class="category-title">
<span class="category-icon">
<Icon name="book" size={18} />
</span>
เรื่องอื่น ๆ ที่ลูกค้าถามบ่อย
</h2>
<!-- 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>
@@ -82,44 +119,43 @@ const categoryIcons: Record<string, string> = {
<span class="topic-tag">ขอดูเว็บจริง</span>
<span class="topic-tag">นัดคุยนอกสถานที่</span>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- Quick Channels -->
<div class="channels-section reveal">
<h2 class="channels-title">ไม่เจอคำตอบ? ถามตรง ๆ เลย</h2>
<div class="channels-grid stagger-children">
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="channel-card">
<div class="channel-icon">
<Icon name="message" size={28} />
</div>
<h3 class="channel-name">LINE</h3>
<p class="channel-handle">@moreminimore</p>
<p class="channel-time">ตอบใน 30 นาที (เวลาทำการ)</p>
</a>
<a href="tel:0809955945" class="channel-card">
<div class="channel-icon">
<Icon name="phone" size={28} />
</div>
<h3 class="channel-name">โทร</h3>
<p class="channel-handle">080-995-5945</p>
<p class="channel-time">จ-ศ 09:00-18:00</p>
</a>
<a href="mailto:contact@moreminimore.com" class="channel-card">
<div class="channel-icon">
<Icon name="mail" size={28} />
</div>
<h3 class="channel-name">Email</h3>
<p class="channel-handle">contact@moreminimore.com</p>
<p class="channel-time">ตอบภายใน 1 วัน</p>
</a>
</div>
<!-- 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">
<div class="cta-content reveal">
<h2 class="cta-title">พร้อมคุยรายละเอียด?</h2>
<p class="cta-desc">นัดปรึกษาฟรี 30 นาที ผ่าน Zoom หรือนัดเจอที่ออฟฟิศ (กรุงเทพ/สมุทรสาคร)</p>
<div class="cta-actions">
@@ -134,27 +170,13 @@ const categoryIcons: Record<string, string> = {
</Base>
<style>
.faq-section { background: var(--color-white); }
.section-yellow { background: var(--color-primary); }
.section-bento {
position: relative;
overflow: hidden;
}
.faq-category {
max-width: 800px;
margin: 0 auto 60px;
}
.category-title {
display: flex;
align-items: center;
gap: 12px;
font-family: var(--font-display);
font-size: 22px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 3px solid var(--color-primary);
}
.category-icon { font-size: 28px; }
.faq-list { display: flex; flex-direction: column; gap: 12px; }
/* FAQ inside BentoTile */
.faq-list { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.faq-item {
background: var(--color-white);
@@ -166,11 +188,15 @@ const categoryIcons: Record<string, string> = {
.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: 20px 24px;
padding: 14px 18px;
cursor: pointer;
list-style: none;
transition: background 0.2s ease;
@@ -181,26 +207,26 @@ const categoryIcons: Record<string, string> = {
.question-text {
flex: 1;
font-family: var(--font-display);
font-size: 16px;
font-size: 15px;
font-weight: 700;
color: var(--color-black);
padding-right: 16px;
}
.faq-toggle {
font-size: 28px;
font-size: 22px;
font-weight: 300;
color: var(--color-primary);
color: var(--color-primary-dark);
flex-shrink: 0;
line-height: 1;
}
.faq-item[open] .faq-toggle { transform: rotate(45deg); }
.faq-answer {
padding: 0 24px 24px;
padding: 0 18px 16px;
}
.faq-answer p {
font-size: 15px;
line-height: 1.8;
font-size: 14px;
line-height: 1.7;
color: var(--color-gray-700);
white-space: pre-line;
}
@@ -214,7 +240,7 @@ const categoryIcons: Record<string, string> = {
.topic-tag {
display: inline-block;
padding: 8px 16px;
background: var(--color-bg-alt);
background: var(--color-white);
color: var(--color-gray-700);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-full);
@@ -228,59 +254,31 @@ const categoryIcons: Record<string, string> = {
color: var(--color-black);
}
/* Channels */
.channels-section {
max-width: 900px;
margin: 80px auto 0;
padding: 60px 40px;
background: var(--color-bg-alt);
border-radius: var(--radius-xl);
text-align: center;
}
.channels-title {
font-family: var(--font-display);
font-size: 28px;
font-weight: 800;
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
margin-bottom: 32px;
}
.channels-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.channel-card {
display: block;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-lg);
padding: 24px;
text-align: center;
transition: all 0.3s ease;
}
.channel-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.channel-icon { font-size: 32px; margin-bottom: 12px; }
.channel-name {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 4px;
}
.channel-handle {
font-size: 14px;
font-weight: 700;
color: var(--color-primary-dark);
margin-bottom: 4px;
}
.channel-time {
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: 12px;
color: var(--color-gray-500);
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; }
@@ -303,13 +301,24 @@ const categoryIcons: Record<string, string> = {
flex-wrap: wrap;
}
@media (max-width: 1024px) {
.channels-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
.faq-question { padding: 16px 20px; }
.question-text { font-size: 15px; }
.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>

View File

@@ -2,217 +2,194 @@
import Base from '../layouts/Base.astro';
import Navigation from '../components/Navigation.astro';
import Footer from '../components/Footer.astro';
import Icon from '../components/Icon.astro';
import Hero from '../components/Hero.astro';
import BentoGrid from '../components/BentoGrid.astro';
import BentoTile from '../components/BentoTile.astro';
import DecoOrb from '../components/DecoOrb.astro';
import { getCollection } from 'astro:content';
import ServiceCard from '../components/ServiceCard.astro';
import PortfolioCard from '../components/PortfolioCard.astro';
// Hardcoded home page copy (previously in src/content/pages/home.md, now inlined)
const homeContent = {
badge: 'AI AGENCY ในประเทศไทย',
title: 'เพิ่มยอดขาย ลดต้นทุน ลดเวลา — ให้ธุรกิจของคุณ',
subtitle:
'นโยบายของเราคือสร้างระบบที่ทำให้ลูกค้ามีกำไรมากขึ้น — ด้วยเว็บไซต์ SEO AI Chatbot และ Marketing Automation ที่ออกแบบมาเพื่อ SME ไทยโดยเฉพาะ',
};
const services = await getCollection('services');
const portfolio = await getCollection('portfolio');
const featuredPortfolio = portfolio.slice(0, 4);
// 4 mega-services (use existing service collection filtered)
const allServices = services.slice(0, 4);
const serviceImages: Record<string, string> = {
'automation': '/images/services/automation.jpg',
'ai-consult': '/images/services/ai-consult.jpg',
'marketing': '/images/services/marketing.jpg',
'webdev': '/images/services/webdev.jpg',
};
// 12 problem cards grouped by 4 service buckets (3 per bucket).
// Each card: serviceBucket, icon (Lucide), title, description, result.
// 4 problem cards (down from 12) — each has symptom + cause + how we fix it
const problemCards = [
// 🏢 Website — load/SEO/visibility
{
bucket: 'website',
icon: 'globe',
title: 'เว็บไซต์โหลดช้า ลูกค้าปิดก่อนเห็นสินค้า',
description: 'หน้าเว็บใช้เวลาโหลดเกิน 5 วินาที ลูกค้า 53% ปิดทิ้งทันที',
result: '→ เสียโอกาสขายตั้งแต่วินาทีแรก',
},
{
bucket: 'website',
icon: 'search',
title: 'อยู่หน้า 5 ของ Google หรือหายไปเลย',
description: 'ลูกค้าค้นหาแล้วไม่เจอเว็บคุณ เจอแต่คู่แข่ง',
result: '→ ต้องจ่ายค่าโฆษณาเพิ่มทุกเดือน',
},
{
bucket: 'website',
icon: 'shoppingCart',
title: 'เว็บมีอยู่ แต่ลูกค้าซื้อไม่ได้',
description: '<27>อร์มไม่ส่ง ตะกร้าค้าง ชำระเงินไม่ผ่าน',
result: '→ ยอดขายตกทั้งที่คนเข้าเว็บเยอะ',
},
// ⚙️ AI Automation — operations / efficiency
{
bucket: 'automation',
icon: 'message',
title: 'ทีมเซลล์ตอบแชตไม่ทัน ลูกค้าหายตอนกลางคืน',
description: 'ทีม 12 คนตอบไม่ไหว ลูกค้ารอ 5 นาทีแล้วไปซื้อที่อื่น',
result: '→ ยอดหายโดยไม่มีใครรู้ตัว',
},
{
bucket: 'automation',
icon: 'clipboard',
title: 'งานซ้ำ ๆ ใช้เวลาคนเป็นชั่วโมงทุกวัน',
description: 'คีย์ข้อมูล ทำใบเสนอราคา อัปเดตสต็อก ทุกอย่างทำมือ',
result: '→ ต้นทุนค่าแรงสูงขึ้นโดยไม่จำเป็น',
},
{
bucket: 'automation',
icon: 'cog',
title: 'ระบบแยกกัน ไม่คุยกัน',
description: 'CRM · ERP · ระบบบัญชี · หน้าร้าน ต่างคนต่างอยู่',
result: '→ ตัดสินใจช้า เพราะข้อมูลไม่เชื่อม',
},
// 📈 SEO + Content — traffic / leads
{
bucket: 'marketing',
icon: 'trendingDown',
title: 'ลงโฆษณา แต่ยอดขายไม่ขยับ',
description: 'คลิกเยอะ แต่ไม่มีใครซื้อ — ไม่มี Funnel ไม่มี Lead scoring',
result: '→ เงินหายไปกับคลิกที่ไม่มีคุณภาพ',
title: 'ลงโฆษณาแล้วยอดไม่ขยับ',
symptom: 'คลิกเยอะ ยอดขายเท่าเดิม แต่คนที่ไม่ซื้อ',
cause: 'เลือกกลุ่มเป้าหมายผิด หรือยิงทุก Platform โดยไม่ดูว่าอันไหนคุ้ม',
fix: 'ดูสถิติ 3 เดือนย้อนหลัง แยกว่า Platform ไหน Convert ดี ตัดอันที่เสียเงินเปล่า? หรือ ถ้ายังไม่มีการวางระบบเก็บข้อมูล ก็จะวางระบบให้ ดังนั้นระยะยาวจะเห็นความแตกต่างแน่นอน',
example: 'เคส Dataroot: เพิ่ม Impression 373%, Click 114% โดยใช้งบน้อยลง 28% — ดูเคสเต็มใน Portfolio',
},
{
bucket: 'marketing',
icon: 'pen',
title: 'เขียนคอนเทนต์เองไม่ทัน',
description: 'อยากโพสต์สม่ำเสมอ แต่ทีมไม่มีเวลา',
result: '→ คอนเทนต์หยุดชะงัก ลูกค้าลืม',
icon: 'shoppingCart',
title: 'เว็บมีคนเข้า แต่ไม่มีคนซื้อ',
symptom: 'Traffic เข้าพอสมควร แต่ไม่มีใครทัก ไม่มีใครโทร',
cause: 'เว็บสวยแต่ไม่ได้ออกแบบมาให้คนซื้อ หรือมีจุดติดขัดที่ทำให้คนออกก่อน',
fix: 'กรณีที่เว็บไม่มีการวางโค้ดเก็บสถิติ ก็จะวางระบบให้ ในกรณีที่มีระบบเก็บสถิติแล้ว ก็จะศึกษาสถิติ และดู Heatmap ว่าคนเข้ามาแล้วทำอะไร และปรับเว็บทีละจุด',
example: 'ลองคุยกัน เราจะดูให้ว่าเว็บคุณติดปัญหาตรงไหน',
},
{
bucket: 'marketing',
icon: 'megaphone',
title: 'ไม่รู้ว่าใครซื้อ ใครแค่ทักมาถามเล่น',
description: 'ไม่มีระบบติดตาม ไม่มี CRM ไม่มี Report',
result: '→ ลงทุนการตลาดแบบเดา',
},
// 🖥️ Tech Consult — infrastructure / scale
{
bucket: 'tech',
icon: 'server',
title: 'ไม่อยากจ้างทีม IT แต่ต้องมี Server',
description: 'ระบบหลังบ้านต้องทำงาน แต่จ้างประจำแพงเกิน',
result: '→ ต้นทุนคงที่สูงโดยไม่คุ้ม',
icon: 'clipboard',
title: 'งานซ้ำ ๆ เสียเวลาเป็นชั่วโมงทุกวัน',
symptom: 'ทีมต้องคีย์ข้อมูล ทำรายงาน ตอบแชตเดิม ๆ จนไม่มีเวลาทำงานหลัก',
cause: 'ระบบไม่มีการเชื่อมกัน หรือยังทำ Manual อยู่',
fix: 'ดู Workflow ก่อน แล้วเลือกเครื่องมือที่เหมาะสม เช่น n8n, Script, หรือ AI ซึ่งจะช่วยลดเวลาจากชั่วโมงเป็นนาที หรือ อาจจะไม่ต้องให้พนักงานเสียเวลาอีกเลย เพราะระบบทำให้เองอัตโนมัติ',
example: 'ลองคุยกัน เราจะดู Workflow ให้ฟรี',
},
{
bucket: 'tech',
icon: 'shield',
title: 'กังวลเรื่อง PDPA / ข้อมูลรั่วไหล',
description: 'เก็บข้อมูลลูกค้าแต่ไม่รู้ว่าปลอดภัยแค่ไหน',
result: '→ เสี่ยงถูกฟ้องร้อง ถูกปรับ',
},
{
bucket: 'tech',
icon: 'monitor',
title: 'อยากใช้ AI แต่ไม่รู้จะเริ่มยังไง',
description: 'มี ChatGPT ใช้ แต่เอามาทำงานจริงในธุรกิจไม่เป็น',
result: '→ เสียโอกาสที่ AI จะช่วยลดงานได้',
icon: 'brain',
title: 'ให้พนักงานใช้ AI แต่ไม่เห็นผลลัพธ์อย่างที่ต้องการ และยังมีค่าใช้จ่ายที่สูงเพิ่มแทน',
symptom: 'จ่ายแพง ใช้ AI ระดับ Frontier กับทุกงาน แต่ผลลัพธ์ไม่คุ้มเงิน',
cause: 'ใช้ AI ผิดแบบ — งานหลายอย่างใช้ Model ราคาถูกก็ได้ผลเท่า ๆ กัน หรือ พนักงานไม่เข้าใจสิ่งที่ AI จะช่วยงานจริง ทำให้ใช้งานผิดรูปแบบ',
fix: 'เลือกใช้ AI ให้ถูกกับงาน เพื่อประหยัดค่าใช้จ่าย รวมถึงให้ความรู้ หรือ วางระบบ AI Agent ให้มี skill เฉพาะทาง เพื่อช่วยพนักงาน ไม่ใช้ให้พนักงานใช้ AI โดยไม่มี skill พิเศษ',
example: 'AI Audit ฟรี — บอกได้ว่าควรใช้ AI ตัวไหน',
},
];
const bucketLabels: Record<string, string> = {
website: 'Website',
automation: 'AI Automation',
marketing: 'SEO + Content',
tech: 'Tech Consult',
};
// Surface color rotation for the 4 problem cards — keep variety
const problemSurfaces = ['yellow', 'purple-soft', 'mint', 'soft'] as const;
// Pre-group problem cards by bucket (4 groups, in canonical service order)
const problemGroupOrder = ['website', 'automation', 'marketing', 'tech'] as const;
const problemGroups = problemGroupOrder.map((bucket) => ({
bucket,
label: bucketLabels[bucket],
cards: problemCards.filter((c) => c.bucket === bucket),
}));
const services = await getCollection('services');
// De-duplicate services: keep only the `-new` version when both old and new exist
// for the same base slug. Falls back to the old one if no `-new` exists.
// Group by base slug (strip trailing `-new` if present).
const dedupedServices = (() => {
const byBase = new Map<string, typeof services[number]>();
for (const s of services) {
const base = s.id.endsWith('-new') ? s.id.slice(0, -4) : s.id;
const existing = byBase.get(base);
if (!existing) {
byBase.set(base, s);
} else if (s.id.endsWith('-new') && !existing.id.endsWith('-new')) {
// Prefer the -new version
byBase.set(base, s);
}
}
return Array.from(byBase.values());
})();
const portfolio = await getCollection('portfolio');
const featuredPortfolio = portfolio.filter(p =>
['dataroot', 'jet-industries', 'tuanthong', 'lawyernoom'].includes(p.id)
).sort((a, b) => {
const order = ['dataroot', 'jet-industries', 'tuanthong', 'lawyernoom'];
return order.indexOf(a.id) - order.indexOf(b.id);
});
---
<Base title="MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="MoreminiMore - ที่ปรึกษาเว็บ การตลาด และ AI สำหรับ SME ไทย">
<Navigation />
<Hero
badge={homeContent.badge}
title={homeContent.title}
subtitle={homeContent.subtitle}
badge="Moreminimore"
title="เราจะช่วยคุณเพิ่มกำไร"
subtitle="เราช่วยวางระบบงาน และใช้สถิติวางกลยุทธ์ทางการตลาด"
>
<a slot="hero-cta-secondary" href="/portfolio" class="btn btn-outline-dark">
ดูผลงานจริง
</a>
</Hero>
<!-- PROBLEM SECTION — 12 pain cards, 3 per service bucket (light) -->
<section class="section section-soft">
<div class="container">
<!-- PROBLEM SECTION — Bento layout (4 cards, varied spans) -->
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', left: '-150px' }} 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;">
<div class="section-header reveal">
<span class="section-badge">ปัญหาที่ SME ไทยเจอซ้ำทุกวัน</span>
<span class="section-badge">4 ปัญหาที่เจอบ่อยที่สุด</span>
<h2 class="section-title">
คุณกำลังเจอ <span class="highlight">แบบนี้อยู่</span> ใช่ไหม?
แต่ละปัญหา<span class="highlight">มีวิธีแก้ที่ต่างกัน</span>
</h2>
<p class="section-desc">12 ปัญหาที่แบ่งตามบริการของเรา เลือกดูได้เลยว่าตรงกับคุณข้อไหน</p>
<p class="section-desc">เราไม่ได้บอกว่า "เราทำได้หมด" แต่บอกว่า "ถ้าเป็นแบบนี้ ทำแบบนี้"</p>
</div>
{problemGroups.map((group) => (
<div class="problem-group reveal">
<h3 class="problem-group-title">
<span class="problem-group-tag">{group.label}</span>
</h3>
<div class="problem-grid">
{group.cards.map((card) => (
<div class="problem-card">
<div class="problem-icon-badge">
<Icon name={card.icon} size={28} color="dark" />
</div>
<h4 class="problem-title">{card.title}</h4>
<p class="problem-desc">{card.description}</p>
<span class="problem-result">{card.result}</span>
</div>
))}
<BentoGrid>
{problemCards.map((card, i) => (
<BentoTile span={6} surface={problemSurfaces[i]} eyebrow={`ปัญหา ${String(i + 1).padStart(2, '0')}`} title={card.title}>
<div class="problem-section">
<span class="problem-label">อาการ</span>
<p class="problem-text">{card.symptom}</p>
</div>
<div class="problem-section">
<span class="problem-label">สาเหตุส่วนใหญ่</span>
<p class="problem-text">{card.cause}</p>
</div>
<div class="problem-fix">
<span class="problem-label problem-label-fix">เราแก้ยังไง</span>
<p class="problem-text">{card.fix}</p>
</div>
<div class="problem-example">
<span class="problem-example-icon">▸</span> {card.example}
</div>
</BentoTile>
))}
</BentoGrid>
<p class="problem-closing reveal">
<span class="highlight">ทุกปัญหาข้างต้นแก้ได้ด้วยระบบเดียว</span> — ดูว่าเราทำยังไง
ไม่แน่ใจว่าตรงกับข้อไหน — <a href="/contact" class="closing-link">นัดคุย 30 นาทีฟรี</a> เราจะช่วยดู
</p>
</div>
</section>
<!-- SERVICES SECTION — 4 mega cards on white (stagger-children) -->
<section class="section services-section">
<div class="container">
<!-- SERVICES SECTION — Bento layout (4 services, asymmetric) -->
<section class="section section-bento">
<DecoOrb color="purple" size="500px" speed={0.4} position={{ top: '-200px', right: '-150px' }} opacity={0.2} blur="100px" />
<DecoOrb color="yellow" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.25} 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">โซลูชัน AI สำหรับธุรกิจไทย</span>
เลือกบริการที่<span class="highlight">ตรงกับปัญหาของคุณ</span>
</h2>
<p class="section-desc">เลือกแค่ที่คุณต้องการ หรือให้เราวางแผนทั้งระบบให้ก็ได้</p>
<p class="section-desc">ไม่จำเป็นต้องทำทุกอย่างพร้อมกัน</p>
</div>
<div class="services-mega-grid stagger-children">
{allServices.map(s => (
<a href={`/services/${s.id}`} class="mega-card">
<span class="mega-tag">{s.data.badge}</span>
<h3 class="mega-title">{s.data.title}</h3>
<p class="mega-subtitle">{s.data.subtitle}</p>
<BentoGrid>
{dedupedServices.slice(0, 4).map((s, i) => {
// Tile-specific copy overrides (per user spec)
const tileCopy = [
{
eyebrow: 'ที่ปรึกษาด้าน AI',
subtitle: 'การนำ AI มาปรับใช้ในองค์กร เพื่อลดต้นทุนและเวลา รวมถึงการรักษาความรู้จากพนักงานที่เชี่ยวชาญ',
objective: 'รักษาความรู้ขององค์กร ลดต้นทุนและเวลาการทำงาน',
},
{
eyebrow: 'วางระบบ Automation',
subtitle: 'การออกแบบระบบ Automation สำหรับธุรกิจคุณโดยเฉพาะ',
objective: 'ลดต้นทุนและเวลา',
},
{
eyebrow: 'ที่ปรึกษาการตลาดออนไลน์',
subtitle: 'ออกแบบและวางกลยุทธ์ตามสถิติ กลุ่มเป้าหมาย และการทำงานขององค์กรคุณ',
objective: 'เพิ่มยอดขาย',
},
{
eyebrow: 'พัฒนาเว็บไซต์',
subtitle: 'พัฒนาเว็บไซต์ที่สร้างผลลัพธ์ได้จริง สวยงาม และลูกค้าสามารถดูแลได้เอง',
objective: 'เพิ่มยอดขาย และความน่าเชื่อถือให้ธุรกิจ',
},
];
// 2x2 layout — each tile span 6 (6+6 per row, 2 rows)
const span = 6;
const surface = (['yellow', 'purple-soft', 'mint', 'soft'] as const)[i];
const copy = tileCopy[i];
return (
<BentoTile span={span} surface={surface} eyebrow={copy.eyebrow} title={s.data.title}>
<p class="mega-subtitle">{copy.subtitle}</p>
<div class="mega-objective">
<span class="objective-label">เป้าหมาย:</span>
<span class="objective-value">{s.data.objective}</span>
<span class="objective-value">{copy.objective}</span>
</div>
<a href={`/services/${s.id}`} class="tile-link-overlay">
<span class="mega-cta">ดูรายละเอียด →</span>
</a>
))}
</div>
</BentoTile>
);
})}
</BentoGrid>
<div class="section-cta">
<a href="/services" class="btn btn-dark btn-lg">ดูบริการทั้งหมด →</a>
@@ -220,31 +197,31 @@ const problemGroups = problemGroupOrder.map((bucket) => ({
</div>
</section>
<!-- PULL QUOTE — black section, oversized white text -->
<!-- PULL QUOTE -->
<section class="section section-dark-quote reveal">
<div class="container">
<blockquote class="pull-quote">
<p class="quote-text">
"เป้าหมายของเราคือ <span class="highlight">กำไรที่มากขึ้นของลูกค้า</span>"
</p>
<cite class="quote-author">— มอร์มินิมอร์, ปณิธานการทำงาน</cite>
<cite class="quote-author">— มอร์มินิมอร์</cite>
</blockquote>
</div>
</section>
<!-- PORTFOLIO PREVIEW — 4 featured on soft -->
<!-- PORTFOLIO PREVIEW — featured real cases (filtered: must have url) -->
<section class="section section-soft">
<div class="container">
<div class="section-header reveal">
<span class="section-badge">ผลงานจริง · ไม่ใช่ Mockup</span>
<span class="section-badge">ผลงานจริง ไม่ใช่ Mockup</span>
<h2 class="section-title">
ลูกค้าจริง <span class="highlight">ผลลัพธ์จริง</span>
ลูกค้าจริง <span class="highlight">เว็บจริง</span>
</h2>
<p class="section-desc">ตัวอย่างผลงานที่เราภาคภูมิใจ — คลิกดูเว็บจริงได้เลย</p>
<p class="section-desc">คลิกเข้าไปดูเว็บจริงที่ใช้งานอยู่ทุกวันนี้</p>
</div>
<div class="portfolio-preview-grid stagger-children">
{featuredPortfolio.map((item) => (
{featuredPortfolio.filter(p => p.data.url).slice(0, 4).map((item) => (
<PortfolioCard
name={item.data.name}
url={item.data.url}
@@ -265,12 +242,12 @@ const problemGroups = problemGroupOrder.map((bucket) => ({
</div>
</section>
<!-- FINAL CTA — yellow section -->
<!-- FINAL CTA -->
<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 นาที — เราจะถามคำถาม 5 ข้อ แล้วบอกคุณได้เลยว่าควรเริ่มจากตรงไหน</p>
<h2 class="cta-title">คุยกันก่อน 30 นาที ฟรี</h2>
<p class="cta-desc">เราจะแนะนำแนวทางเบื้องต้นให้คุณว่าควรเริ่มจากตรงไหน — จะบอกตรง ๆ ว่าอะไรควรทำหรือไม่ควรทำ</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">
นัดคุย 30 นาที
@@ -282,7 +259,7 @@ const problemGroups = problemGroupOrder.map((bucket) => ({
ทัก LINE: @moreminimore
</a>
</div>
<p class="cta-reassurance">ไม่มี commitment · ไม่มี script sales · พูดตรง ๆ ว่าทำได้หรือทำไม่ได้</p>
<p class="cta-reassurance">ไม่มี commitment · ไม่มี script sales · พูดตรง ๆ</p>
</div>
</div>
</section>
@@ -291,259 +268,155 @@ const problemGroups = problemGroupOrder.map((bucket) => ({
</Base>
<style>
/* ============================================
SECTION HEADER (light)
============================================ */
.section-header {
text-align: center;
margin-bottom: 60px;
.section-bento {
position: relative;
overflow: hidden;
}
.section-badge {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: 12px;
/* ============================================
PROBLEM TILES (inside BentoTile)
============================================ */
.problem-section {
margin-bottom: 14px;
}
.problem-fix {
background: rgba(255, 220, 0, 0.25);
border-left: 3px solid var(--color-black);
padding: 12px 14px;
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
margin: 14px -14px;
}
.surface-purple-soft .problem-fix,
.surface-mint .problem-fix,
.surface-soft .problem-fix {
background: rgba(0, 0, 0, 0.05);
border-left-color: var(--color-black);
}
.problem-label {
display: block;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 16px;
letter-spacing: 1.5px;
margin-bottom: 6px;
opacity: 0.7;
}
.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,
.section-desc .highlight,
.quote-text .highlight,
.problem-closing .highlight {
color: var(--color-primary-dark);
}
.section-soft .section-title .highlight {
color: var(--color-primary-dark);
}
.section-desc {
font-size: 17px;
color: var(--color-gray-600);
.surface-yellow .problem-label { color: var(--color-black); opacity: 0.7; }
.problem-label-fix { color: var(--color-black); opacity: 1; }
.problem-text {
font-size: 14px;
line-height: 1.6;
max-width: 600px;
margin: 0;
}
.surface-yellow .problem-text { color: var(--color-black); }
.surface-purple-soft .problem-text,
.surface-mint .problem-text,
.surface-soft .problem-text { color: var(--color-gray-700); }
.problem-example {
margin-top: 16px;
padding-top: 16px;
border-top: 1px dashed currentColor;
opacity: 0.7;
font-size: 13px;
line-height: 1.5;
font-style: italic;
}
.problem-example-icon {
font-style: normal;
font-weight: 700;
margin-right: 4px;
}
.problem-closing {
text-align: center;
margin-top: 48px;
font-size: 17px;
color: var(--color-gray-700);
}
.closing-link {
color: var(--color-primary-dark);
font-weight: 700;
text-decoration: underline;
text-underline-offset: 3px;
}
/* ============================================
SERVICES TILES (inside BentoTile)
============================================ */
.mega-subtitle {
font-size: 15px;
line-height: 1.6;
margin-bottom: 16px;
}
.surface-yellow .mega-subtitle { color: var(--color-black); opacity: 0.85; }
.surface-purple-soft .mega-subtitle,
.surface-mint .mega-subtitle,
.surface-soft .mega-subtitle { color: var(--color-gray-700); }
.mega-objective {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
background: rgba(0, 0, 0, 0.05);
border-radius: var(--radius-md);
margin-bottom: 20px;
}
.surface-yellow .mega-objective { background: rgba(0, 0, 0, 0.1); }
.objective-label {
font-size: 12px;
font-weight: 600;
opacity: 0.7;
}
.objective-value {
font-size: 13px;
font-weight: 700;
}
.surface-yellow .objective-value { color: var(--color-black); }
.mega-cta {
display: inline-block;
font-size: 14px;
font-weight: 700;
text-decoration: none;
transition: transform 0.2s ease;
}
.bento-tile:hover .mega-cta { transform: translateX(4px); }
/* ============================================
PORTFOLIO PREVIEW
============================================ */
.portfolio-preview-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
max-width: 900px;
margin: 0 auto;
}
/* ============================================
SECTION UTILITIES
============================================ */
.section-header { margin-bottom: 40px; }
.section-cta {
text-align: center;
margin-top: 48px;
}
/* ============================================
PROBLEM CARDS — 12 cards, 3 per service bucket
============================================ */
.section-soft { background: var(--color-bg-alt); }
.problem-group {
margin-bottom: 56px;
}
.problem-group:last-of-type {
margin-bottom: 0;
}
.problem-group-title {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.problem-group-tag {
display: inline-block;
font-family: var(--font-display);
font-size: 13px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--color-black);
background: var(--color-primary);
padding: 6px 14px;
border-radius: var(--radius-sm);
}
.problem-group-title::after {
content: '';
flex: 1;
height: 2px;
background: var(--color-primary);
opacity: 0.4;
}
.problem-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.problem-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 28px 24px;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.problem-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.problem-icon-badge {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--color-primary);
border: 2px solid var(--color-black);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
}
.problem-title {
font-family: var(--font-display);
font-size: 17px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 10px;
line-height: 1.35;
}
.problem-desc {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 14px;
flex: 1;
}
.problem-result {
display: block;
font-size: 13px;
font-weight: 700;
color: var(--color-primary-dark);
}
.problem-closing {
text-align: center;
margin-top: 48px;
font-size: 18px;
color: var(--color-gray-700);
}
/* ============================================
SERVICES MEGA GRID
============================================ */
.services-mega-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
.mega-card {
display: block;
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
transition: all 0.3s ease;
position: relative;
}
.mega-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--color-primary);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s var(--ease-out-expo);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.mega-card:hover::before { transform: scaleX(1); }
.mega-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.mega-tag {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
}
.mega-title {
font-family: var(--font-display);
font-size: 24px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.mega-subtitle {
font-size: 15px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
}
.mega-objective {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: var(--color-bg-alt);
border-radius: var(--radius-md);
margin-bottom: 16px;
}
.objective-label {
font-size: 12px;
color: var(--color-gray-500);
text-transform: uppercase;
letter-spacing: 1px;
}
.objective-value {
font-size: 14px;
font-weight: 700;
color: var(--color-black);
}
.mega-cta {
display: inline-block;
font-size: 14px;
font-weight: 700;
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 1px;
}
.mega-card:hover .mega-cta {
color: var(--color-primary-dark);
}
/* ============================================
PULL QUOTE (dark band — only dark section on home)
PULL QUOTE — dark band
============================================ */
.section-dark-quote {
background: var(--color-black);
padding: 100px 0;
}
.section-dark-quote .pull-quote {
.pull-quote {
text-align: center;
max-width: 1000px;
margin: 0 auto;
}
.quote-text {
font-family: var(--font-display);
font-size: clamp(28px, 4.5vw, 56px);
font-size: clamp(28px, 4.5vw, 52px);
font-weight: 800;
line-height: 1.3;
line-height: 1.25;
color: var(--color-white);
margin-bottom: 24px;
}
@@ -552,62 +425,109 @@ const problemGroups = problemGroupOrder.map((bucket) => ({
}
.quote-author {
font-style: normal;
font-size: 14px;
font-size: 13px;
color: var(--color-gray-400);
text-transform: uppercase;
letter-spacing: 2px;
letter-spacing: 3px;
font-weight: 600;
}
/* ============================================
PORTFOLIO PREVIEW GRID — 2x2 on desktop
FINAL CTA — yellow section
============================================ */
.portfolio-preview-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
.cta-section {
background: var(--color-primary);
padding: 100px 0;
position: relative;
overflow: hidden;
}
.cta-section::before {
content: '';
position: absolute;
top: -50%;
right: -10%;
width: 600px;
height: 600px;
background: var(--color-primary-dark);
border-radius: 50%;
opacity: 0.4;
z-index: 0;
}
.cta-section::after {
content: '';
position: absolute;
bottom: -30%;
left: -5%;
width: 400px;
height: 400px;
background: var(--color-primary-dark);
border-radius: 50%;
opacity: 0.3;
z-index: 0;
}
.cta-content {
text-align: center;
max-width: 720px;
margin: 0 auto;
position: relative;
z-index: 1;
}
/* ============================================
FINAL CTA
============================================ */
.section-yellow { background: var(--color-primary); }
.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-size: clamp(32px, 5vw, 52px);
font-weight: 900;
color: var(--color-black);
margin-bottom: 16px;
margin-bottom: 20px;
line-height: 1.15;
}
.cta-desc {
font-size: 18px;
color: rgba(0, 0, 0, 0.7);
margin-bottom: 32px;
color: var(--color-black);
opacity: 0.8;
margin-bottom: 36px;
line-height: 1.6;
}
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
.cta-actions .btn svg {
width: 18px;
height: 18px;
margin-left: 4px;
}
.cta-reassurance {
margin-top: 24px;
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
color: var(--color-black);
opacity: 0.7;
font-weight: 500;
}
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 1024px) {
.problem-grid { grid-template-columns: repeat(2, 1fr); }
.services-mega-grid { grid-template-columns: 1fr; }
.portfolio-preview-grid { grid-template-columns: repeat(2, 1fr); }
.portfolio-preview-grid { grid-template-columns: 1fr; max-width: 500px; }
}
@media (max-width: 640px) {
.problem-grid { grid-template-columns: 1fr; }
.portfolio-preview-grid { grid-template-columns: 1fr; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</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>

View File

@@ -5,9 +5,14 @@ import Footer from '../components/Footer.astro';
import PageHero from '../components/PageHero.astro';
import PortfolioCard from '../components/PortfolioCard.astro';
import Icon from '../components/Icon.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 portfolio = await getCollection('portfolio');
// Filter: only show real cases (have url) and not drafts
const realPortfolio = portfolio.filter(p => p.data.url && p.data.url !== '');
// Industry filter metadata: id -> { label, icon }
// Icons are lucide-style SVGs; emoji-free.
@@ -44,17 +49,19 @@ const industryFilters = [
>
{f.id !== 'all' && <Icon name={f.icon as any} size={16} class="filter-icon" />}
{f.id === 'all' && <Icon name={f.icon as any} size={16} class="filter-icon" />}
<span>{f.id === 'all' ? `ทั้งหมด (${portfolio.length})` : f.label}</span>
<span>{f.id === 'all' ? `ทั้งหมด (${realPortfolio.length})` : f.label}</span>
</button>
))}
</div>
</div>
</section>
<section class="section portfolio-section">
<div class="container">
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-150px' }} opacity={0.2} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.35} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="portfolio-grid stagger-children">
{portfolio.map(item => (
{realPortfolio.map(item => (
<PortfolioCard
name={item.data.name}
url={item.data.url || '#'}
@@ -71,9 +78,11 @@ const industryFilters = [
</div>
</section>
<!-- "ดีลที่เราเลือก" Section -->
<section class="section section-soft">
<div class="container">
<!-- "ดีลที่เราเลือก" Section — Bento tiles -->
<section class="section section-soft section-bento">
<DecoOrb color="purple" size="500px" speed={0.4} position={{ top: '-200px', left: '20%' }} opacity={0.18} blur="100px" />
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} 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">
@@ -81,23 +90,19 @@ const industryFilters = [
</h2>
</div>
<div class="reasons-grid stagger-children">
<div class="reason-card">
<div class="reason-num">1</div>
<h3 class="reason-title">ธุรกิจที่พร้อมจริง ๆ</h3>
<p class="reason-desc">เราคุยกับเจ้าของธุรกิจก่อน ถ้าเป้าหมายยังไม่ชัด เราจะแนะนำให้รอก่อน ดีกว่าเสียเงินแล้วไม่ได้ผล</p>
</div>
<div class="reason-card">
<div class="reason-num">2</div>
<h3 class="reason-title">งบประมาณที่สมเหตุสมผล</h3>
<p class="reason-desc">เราไม่ได้ถูกที่สุด แต่ก็ไม่ได้แพงที่สุด ถ้าใครบอก "งบ 5,000 ทำเว็บได้ไหม" — เราแนะนำให้ไปฟรีแลนซ์ก่อน</p>
</div>
<div class="reason-card">
<div class="reason-num">3</div>
<h3 class="reason-title">ลูกค้าที่ฟัง</h3>
<p class="reason-desc">เราทำงานกับลูกค้าที่พร้อมฟังคำแนะนำ ไม่ใช่ลูกค้าที่บอก "ทำตามนี้เป๊ะ ๆ" แล้วผิดคาดทุกที</p>
</div>
</div>
<BentoGrid>
<BentoTile span={4} surface="yellow" eyebrow="ข้อ 01" title="ธุรกิจที่พร้อมจริง ๆ">
<p>เราคุยกับเจ้าของธุรกิจก่อน ถ้าเป้าหมายยังไม่ชัด เราจะแนะนำให้รอก่อน ดีกว่าเสียเงินแล้วไม่ได้ผล</p>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="ข้อ 02" title="งบประมาณที่สมเหตุสมผล">
<p>เราไม่ได้ถูกที่สุด แต่ก็ไม่ได้แพงที่สุด ถ้าใครบอก "งบ 5,000 ทำเว็บได้ไหม" — เราแนะนำให้ไปฟรีแลนซ์ก่อน</p>
</BentoTile>
<BentoTile span={4} surface="mint" eyebrow="ข้อ 03" title="ลูกค้าที่ฟัง">
<p>เราทำงานกับลูกค้าที่พร้อมฟังคำแนะนำ ไม่ใช่ลูกค้าที่บอก "ทำตามนี้เป๊ะ ๆ" แล้วผิดคาดทุกที</p>
</BentoTile>
</BentoGrid>
</div>
</section>
@@ -137,9 +142,27 @@ const industryFilters = [
});
});
});
// Parallax orbs
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>
<style>
.section-bento {
position: relative;
overflow: hidden;
}
/* Filter bar (unchanged) */
.filter-section {
background: var(--color-bg-alt);
padding: 20px 0;
@@ -182,10 +205,7 @@ const industryFilters = [
}
.filter-icon { color: currentColor; }
.portfolio-section { background: var(--color-white); }
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
/* Portfolio grid (unchanged — uses PortfolioCard component) */
.portfolio-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -193,65 +213,10 @@ const industryFilters = [
}
.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, 40px);
font-weight: 900;
line-height: 1.2;
color: var(--color-black);
}
.section-title .highlight { color: var(--color-primary-dark); }
.reasons-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.reason-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
}
.reason-num {
display: inline-block;
width: 40px;
height: 40px;
background: var(--color-primary);
color: var(--color-black);
border-radius: 50%;
text-align: center;
line-height: 40px;
font-family: var(--font-display);
font-size: 18px;
font-weight: 900;
margin-bottom: 16px;
}
.reason-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.reason-desc {
font-size: 15px;
color: var(--color-gray-600);
line-height: 1.6;
}
.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);
@@ -280,7 +245,6 @@ const industryFilters = [
@media (max-width: 1024px) {
.portfolio-grid { grid-template-columns: repeat(2, 1fr); }
.reasons-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.portfolio-grid { grid-template-columns: 1fr; }

View File

@@ -3,6 +3,9 @@ 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';
---
<Base title="นโยบายความเป็นส่วนตัว | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
@@ -14,9 +17,13 @@ import PageHero from '../components/PageHero.astro';
subtitle="มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569"
/>
<section class="section legal-section">
<div class="container">
<div class="legal-content">
<section class="section section-bento legal-section">
<DecoOrb color="purple" size="450px" speed={0.3} position={{ top: '-100px', left: '-150px' }} opacity={0.2} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', right: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<BentoTile span={8} surface="white" eyebrow="กฎหมาย" title="นโยบายความเป็นส่วนตัว">
<div class="legal-body">
<p class="legal-intro">บริษัท มอร์มินิมอร์ จำกัด ให้ความสำคัญกับการคุ้มครองข้อมูลส่วนบุคคลของท่าน</p>
<div class="legal-block">
@@ -50,6 +57,23 @@ import PageHero from '../components/PageHero.astro';
<p>ท่านมีสิทธิในการเข้าถึง แก้ไข ลบ หรือระงับการใช้ข้อมูลส่วนบุคคลของท่าน กรุณาติดต่อเราผ่านช่องทางที่ระบุในเว็บไซต์</p>
</div>
</div>
</BentoTile>
<BentoTile span={4} surface="purple-soft" eyebrow="ข้อมูลเอกสาร" title="สรุปฉบับย่อ">
<div class="aside-body">
<p><strong>ชื่อเอกสาร:</strong> นโยบายความเป็นส่วนตัว</p>
<p><strong>มีผลบังคับใช้:</strong> 5 พฤษภาคม 2569</p>
<p><strong>จัดการโดย:</strong> MoreminiMore Co.,Ltd.</p>
<p style="margin-top: 20px;"><strong>หัวข้อทั้งหมด 4 ข้อ:</strong></p>
<ol class="toc-list">
<li>ข้อมูลส่วนบุคคลที่เราเก็บรวบรวม</li>
<li>วัตถุประสงค์ในการเก็บรวบรวมข้อมูล</li>
<li>การคุ้มครองข้อมูล</li>
<li>สิทธิของท่าน</li>
</ol>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
@@ -58,28 +82,36 @@ import PageHero from '../components/PageHero.astro';
<style>
.legal-section { background: var(--color-white); }
.legal-content { max-width: 800px; margin: 0 auto; }
.section-bento { position: relative; overflow: hidden; }
/* Body typography inside the prose tile */
.legal-body { font-size: 16px; line-height: 1.8; color: var(--color-gray-700); }
.legal-intro {
font-size: 18px;
font-size: 17px;
color: var(--color-gray-700);
margin-bottom: 48px;
margin-bottom: 36px;
line-height: 1.7;
padding-bottom: 28px;
border-bottom: 1px solid var(--color-gray-200);
}
.legal-block { margin-bottom: 48px; }
.legal-block { margin-bottom: 36px; }
.legal-block:last-child { margin-bottom: 0; }
.legal-block h2 {
font-family: var(--font-display);
font-size: 24px;
font-size: 22px;
font-weight: 800;
margin-bottom: 20px;
margin-bottom: 14px;
color: var(--color-black);
}
.legal-block p {
font-size: 16px;
color: var(--color-gray-700);
line-height: 1.8;
margin-bottom: 16px;
margin-bottom: 12px;
}
.legal-block ul {
margin: 12px 0 12px 24px;
}
.legal-block ul { margin-left: 24px; margin-bottom: 16px; }
.legal-block li {
font-size: 16px;
color: var(--color-gray-700);
@@ -87,4 +119,56 @@ import PageHero from '../components/PageHero.astro';
margin-bottom: 8px;
}
.legal-block strong { color: var(--color-black); }
/* Aside (purple-soft tile) */
.aside-body {
font-size: 15px;
line-height: 1.7;
color: var(--color-black);
}
.aside-body p { margin-bottom: 10px; }
.aside-body strong { font-weight: 800; }
.toc-list {
list-style: none;
padding: 0;
margin: 12px 0 0;
counter-reset: toc;
}
.toc-list li {
counter-increment: toc;
padding: 10px 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
font-weight: 600;
position: relative;
padding-left: 36px;
}
.toc-list li::before {
content: counter(toc, decimal-leading-zero);
position: absolute;
left: 0;
top: 10px;
font-family: var(--font-display);
font-weight: 900;
opacity: 0.6;
}
.toc-list li:last-child { border-bottom: none; }
@media (max-width: 640px) {
.legal-intro { font-size: 16px; }
.legal-block h2 { font-size: 20px; }
}
</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>

View File

@@ -2,18 +2,11 @@
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 Icon from '../../components/Icon.astro';
import BentoGrid from '../../components/BentoGrid.astro';
import BentoTile from '../../components/BentoTile.astro';
import DecoOrb from '../../components/DecoOrb.astro';
import { getCollection, render } from 'astro:content';
const { slug } = Astro.params;
const services = await getCollection('services');
const service = services.find(s => s.id === slug);
if (!service) {
return Astro.redirect('/404');
}
export async function getStaticPaths() {
const services = await getCollection('services');
return services.map(service => ({
@@ -22,15 +15,17 @@ export async function getStaticPaths() {
}));
}
const { service } = Astro.props;
const { Content } = await render(service);
const slug = service.id;
const isWebDev = slug === 'webdev';
const isMarketing = slug === 'marketing';
const isAutomation = slug === 'automation';
const isTechConsult = slug === 'ai-consult';
// Service-specific data
const serviceData = {
// Service-specific data — keyed by slug, with safe fallback to webdev.
const serviceData: Record<string, any> = {
'webdev': {
badge: 'บริการรับทำเว็บไซต์สำหรับ SME ไทย',
title: 'สร้างเว็บไซต์',
@@ -150,26 +145,24 @@ const serviceData = {
};
const data = serviceData[slug] || serviceData['webdev'];
const featureList = data.features || data.services || [];
---
<Base title={`${service.data.title} | MoreminiMore`}>
<Navigation />
<!-- HERO (light theme, was dark) -->
<section class="service-hero">
<div class="hero-bg">
<div class="bg-dots"></div>
<div class="bg-glow"></div>
</div>
<div class="container">
<!-- HERO -->
<section class="service-hero section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.3} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="hero-grid">
<div class="hero-content">
<span class="hero-badge">{data.badge}</span>
<h1 class="hero-title kinetic-title">
<span class="word-wrapper"><span class="word" style="--delay: 0.1s">{data.title}</span></span>
<span class="word-wrapper"><span class="word" style="--delay: 0.2s">{data.titleAccent}</span></span>
{data.titleAccent2 && <span class="word-wrapper"><span class="word" style="--delay: 0.3s">{data.titleAccent2}</span></span>}
<h1 class="hero-title">
<span class="hero-line">{data.title}</span>
<span class="hero-line hero-accent">{data.titleAccent}</span>
{data.titleAccent2 && <span class="hero-line">{data.titleAccent2}</span>}
</h1>
<p class="hero-desc">{data.desc}</p>
@@ -181,7 +174,7 @@ const data = serviceData[slug] || serviceData['webdev'];
โทรหาเรา
</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark">
<svg viewBox="0 0 24 24" fill="currentColor" viewBox="0 0 24 24"><path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.627-.63h2.386c.349 0 .63.285.63.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.627-.63.349 0 .631.285.631.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-9.491.371-.661.56-1.388.56-2.159"/></svg>
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.627-.63h2.386c.349 0 .63.285.63.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.627-.63.349 0 .631.285.631.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-9.491.371-.661.56-1.388.56-2.159"/></svg>
ทัก LINE
</a>
</div>
@@ -196,29 +189,33 @@ const data = serviceData[slug] || serviceData['webdev'];
</div>
</div>
<!-- Side card — first 4 features as a single tile -->
<div class="hero-visual">
<div class="features-card">
<div class="card-header">
<span class="card-title">บริการหลัก</span>
</div>
{(data.features || data.services || []).slice(0, 4).map((f) => (
<div class="feature-item">
<div class="feature-icon"><Icon name={f.icon as any} /></div>
<div class="feature-content">
<span class="feature-title">{f.title}</span>
<span class="feature-desc">{f.desc || f.items?.[0]}</span>
</div>
<BentoGrid>
<BentoTile span={12} surface="yellow" eyebrow="บริการหลัก" title={`${featureList.length} ความสามารถหลัก`}>
<ul class="hero-feature-list">
{featureList.slice(0, 4).map((f) => (
<li class="hero-feature-item">
<div class="hero-feature-index">{String(featureList.indexOf(f) + 1).padStart(2, '0')}</div>
<div class="hero-feature-content">
<span class="hero-feature-title">{f.title}</span>
<span class="hero-feature-desc">{f.desc || f.items?.[0]}</span>
</div>
</li>
))}
</div>
</ul>
</BentoTile>
</BentoGrid>
</div>
</div>
</div>
</section>
<!-- FEATURES -->
<section class="section features-section">
<div class="container">
<!-- FEATURES (BENTO) -->
<section class="section section-bento">
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '10%' }} opacity={0.2} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="section-header reveal">
<span class="section-badge">{isTechConsult ? 'บริการของเรา' : 'ความสามารถ'}</span>
<h2 class="section-title">
@@ -226,180 +223,219 @@ const data = serviceData[slug] || serviceData['webdev'];
</h2>
</div>
<div class="features-grid stagger-children">
{(data.features || data.services || []).map((f) => (
<div class="feature-card">
<div class="feature-icon"><Icon name={f.icon as any} /></div>
<h3 class="feature-title">{f.title}</h3>
<p class="feature-desc">{f.desc || f.items?.join(' · ')}</p>
</div>
))}
</div>
<BentoGrid>
{featureList.map((f: any, i: number) => {
// Asymmetric span + surface rotation per the bento design system
const span = i % 3 === 0 ? 8 : 4;
const surfaces = ['yellow', 'purple-soft', 'mint', 'soft', 'teal', 'soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={span} surface={surface} eyebrow={`0${i + 1}`} title={f.title}>
<p>{f.desc || f.items?.join(' · ')}</p>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
<!-- TARGET AUDIENCE -->
<!-- TARGET AUDIENCE (BENTO) -->
{data.targets && (
<section class="section section-soft target-section">
<div class="container">
<section class="section section-soft section-bento">
<DecoOrb color="teal" size="400px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.2} blur="80px" />
<DecoOrb color="soft" size="300px" speed={0.4} position={{ bottom: '-100px', left: '-50px' }} opacity={0.35} 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>
<div class="target-grid stagger-children">
{data.targets.map((t) => (
<div class="target-card">
<div class="target-icon"><Icon name={t.icon as any} /></div>
<h3 class="target-title">{t.title}</h3>
<p class="target-desc">{t.desc}</p>
</div>
))}
</div>
<BentoGrid>
{data.targets.map((t: any, i: number) => {
// First target takes 12 to lead, then 6+6 for remaining pair
const span = i === 0 ? 12 : 6;
const surfaces = ['yellow', 'soft', 'purple-soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={span} surface={surface} eyebrow={`0${i + 1}`} title={t.title}>
<p>{t.desc}</p>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
)}
<!-- INCLUDED -->
<!-- INCLUDED + TECH OPTIONS (BENTO) -->
{(data.included || data.techOptions) && (
<section class="section section-bento">
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
<DecoOrb color="soft" size="300px" speed={0.4} position={{ bottom: '-100px', right: '10%' }} opacity={0.35} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
{data.included && (
<section class="section included-section">
<div class="container">
<>
<div class="section-header reveal">
<span class="section-badge">สิ่งที่ได้รับ</span>
<h2 class="section-title">
ทุกเว็บไซต์มาพร้อม<span class="highlight">ให้ครบ</span>
</h2>
</div>
<div class="included-grid stagger-children">
{data.included.map((item) => (
<div class="included-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>{item}</span>
<h2 class="section-title">ทุกเว็บไซต์มาพร้อม<span class="highlight">ให้ครบ</span></h2>
</div>
<BentoGrid>
{data.included.map((item: string, i: number) => (
<BentoTile span={4} surface="white" eyebrow="✓" title={item}>
<p class="tile-meta">รวมอยู่ในแพ็คเกจ — ไม่มีค่าใช้จ่ายเพิ่ม</p>
</BentoTile>
))}
</div>
<div class="pricing-highlight">
<p><strong>รวม:</strong> เว็บไซต์ + Server 1 ปี + Domain .com = เริ่มต้น ฿15,500*</p>
<span class="pricing-note">*ราคาขึ้นอยู่กับความซับซ้อนของเว็บไซต์</span>
</div>
</div>
</section>
<BentoTile span={12} surface="yellow" eyebrow="ราคารวม" title="เว็บไซต์ + Server 1 ปี + Domain .com">
<p>เริ่มต้น <strong>฿15,500*</strong></p>
<p class="tile-meta-dark">*ราคาขึ้นอยู่กับความซับซ้อนของเว็บไซต์</p>
</BentoTile>
</BentoGrid>
</>
)}
{data.techOptions && (
<section class="section section-soft tech-section">
<div class="container">
<div class="section-header reveal">
<>
<div class="section-header reveal" style="margin-top: 64px;">
<span class="section-badge">เลือกระบบ</span>
<h2 class="section-title">
เลือกระบบที่<span class="highlight">เหมาะกับคุณ</span>
</h2>
</div>
<div class="tech-grid stagger-children">
{data.techOptions.map((t) => (
<div class="tech-card">
<div class="tech-badge">{t.badge}</div>
<h3 class="tech-name">{t.name}</h3>
<p class="tech-desc">{t.desc}</p>
<span class="tech-duration">{t.duration}</span>
<h2 class="section-title">เลือกระบบที่<span class="highlight">เหมาะกับคุณ</span></h2>
</div>
<BentoGrid>
{data.techOptions.map((t: any, i: number) => (
<BentoTile span={6} surface={i === 0 ? 'yellow' : 'soft'} eyebrow={t.badge} title={t.name}>
<p>{t.desc}</p>
<p class="tile-meta-strong">{t.duration}</p>
</BentoTile>
))}
</div>
</BentoGrid>
</>
)}
</div>
</section>
)}
<!-- PRICING (BENTO) -->
{data.pricing && (
<section class="section pricing-section">
<div class="container">
<section class="section section-soft section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} 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>
<p class="section-desc">ชัดเจน ไม่มีค่าใช้จ่ายซ่อนเร้น</p>
</div>
<div class="pricing-list stagger-children">
{data.pricing.map((p) => (
<div class="pricing-item">
<span class="pricing-label">{p.label}</span>
<span class="pricing-value">{p.price}</span>
</div>
))}
</div>
<BentoGrid>
{data.pricing.map((p: any, i: number) => {
const surfaces = ['white', 'soft', 'yellow', 'mint', 'purple-soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={4} surface={surface} eyebrow={p.label} title={p.price}>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
)}
<!-- AI FEATURES (BENTO) -->
{data.aiFeatures && (
<section class="section section-soft ai-section">
<div class="container">
<section class="section section-bento">
<DecoOrb color="purple" size="500px" speed={0.3} position={{ top: '-100px', left: '10%' }} opacity={0.2} blur="100px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '10%', right: '-50px' }} opacity={0.25} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="section-header reveal">
<span class="section-badge">AI Analytics</span>
<h2 class="section-title">
AI วิเคราะห์<span class="highlight">ทุกความเคลื่อนไหว</span>
</h2>
<h2 class="section-title">AI วิเคราะห์<span class="highlight">ทุกความเคลื่อนไหว</span></h2>
</div>
<div class="ai-grid stagger-children">
{data.aiFeatures.map((f) => (
<div class="ai-item">
<svg viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
<span>{f}</span>
</div>
))}
</div>
<BentoGrid>
{data.aiFeatures.map((f: string, i: number) => {
// Asymmetric: first item 12, then 6+6 paired
const span = i === 0 ? 12 : 6;
const surfaces = ['yellow', 'soft', 'mint', 'purple-soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={span} surface={surface} eyebrow={`✓ 0${i + 1}`} title={f}>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
)}
<!-- WHY US (BENTO) -->
{data.whyUs && (
<section class="section why-section">
<div class="container">
<section class="section section-bento">
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.25} blur="80px" />
<DecoOrb color="teal" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-50px' }} opacity={0.2} 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>
<div class="why-grid stagger-children">
{data.whyUs.map((w) => (
<div class="why-card">
<h3 class="why-title">{w.title}</h3>
<p class="why-desc">{w.desc}</p>
</div>
))}
</div>
<BentoGrid>
{data.whyUs.map((w: any, i: number) => {
const surfaces = ['yellow', 'soft', 'purple-soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={4} surface={surface} eyebrow={`0${i + 1}`} title={w.title}>
<p>{w.desc}</p>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
)}
<!-- FAQ -->
<section class="section section-soft faq-section">
<div class="container">
<!-- DYNAMIC CONTENT FROM MDX (full-width bento tile) -->
<section class="section section-soft section-bento">
<DecoOrb color="soft" size="500px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.4} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-50px', right: '-50px' }} opacity={0.2} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<BentoTile span={12} surface="white" eyebrow="รายละเอียดเพิ่มเติม" title="เนื้อหาจากเอกสารบริการ">
<div class="mdx-content">
<Content />
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
<!-- FAQ (BENTO) -->
<section class="section section-bento">
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-50px', right: '-50px' }} opacity={0.25} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="section-header reveal">
<span class="section-badge">FAQ</span>
<h2 class="section-title">คำถาม<span class="highlight">ที่พบบ่อย</span></h2>
</div>
<div class="faq-list">
{data.faqs.map((faq) => (
<details class="faq-item">
<summary class="faq-question">
{faq.q}
<span class="faq-toggle">+</span>
</summary>
<div class="faq-answer">
<BentoGrid>
{data.faqs.map((faq: any, i: number) => {
// Hero: first question 12-span, then alternating 8+4 for visual tension
let span: 12 | 8 | 4 = 6;
if (i === 0) span = 12;
else if (i % 3 === 1) span = 8;
else if (i % 3 === 2) span = 4;
else span = 6;
const surfaces = ['yellow', 'soft', 'purple-soft', 'mint', 'teal', 'soft'] as const;
const surface = surfaces[i % surfaces.length];
return (
<BentoTile span={span} surface={surface} eyebrow={`Q${i + 1}`} title={faq.q}>
<div class="faq-answer-inline">
<p>{faq.a}</p>
</div>
</details>
))}
</div>
</BentoTile>
);
})}
</BentoGrid>
</div>
</section>
@@ -411,7 +447,9 @@ const data = serviceData[slug] || serviceData['webdev'];
<p class="cta-desc">ติดต่อเราเพื่อคุยกันและให้คำปรึกษาฟรี! เราพร้อมช่วยคุณสร้าง{data.title}ที่ตอบโจทย์ธุรกิจ</p>
<div class="cta-actions">
<a href="tel:0809955945" class="btn btn-dark btn-lg">
<Icon name="phone" size={18} class="btn-icon" />
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
<path d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
</svg>
080-995-5945
</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">สอบถามราคา</a>
@@ -424,29 +462,26 @@ const data = serviceData[slug] || serviceData['webdev'];
</Base>
<style>
/* Section wrappers */
.section-bento {
position: relative;
overflow: hidden;
}
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
/* HERO */
.service-hero {
background: var(--color-white);
padding: 140px 0 80px;
position: relative;
overflow: hidden;
}
.hero-bg { position: absolute; inset: 0; overflow: hidden; }
.bg-dots {
position: absolute;
inset: 0;
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.15) 1px, transparent 0);
background-size: 40px 40px;
}
.bg-glow {
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 50% 0%, rgba(254, 212, 0, 0.15) 0%, transparent 60%);
}
.hero-grid {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 1.3fr 0.7fr;
grid-template-columns: 1.3fr 1fr;
gap: 60px;
align-items: center;
}
@@ -466,12 +501,12 @@ const data = serviceData[slug] || serviceData['webdev'];
font-family: var(--font-display);
font-size: clamp(36px, 5vw, 56px);
font-weight: 900;
line-height: 1.3; /* Thai-safe */
line-height: 1.3;
color: var(--color-black);
margin-bottom: 20px;
}
.hero-title.kinetic-title .word-wrapper { display: block; }
.hero-title.kinetic-title .word-wrapper:nth-child(2) .word { color: var(--color-primary-dark); }
.hero-line { display: block; }
.hero-accent { color: var(--color-primary-dark); }
.hero-desc {
font-size: 18px;
color: var(--color-gray-700);
@@ -494,28 +529,42 @@ const data = serviceData[slug] || serviceData['webdev'];
}
.trust-item { font-weight: 600; }
.hero-visual { display: flex; }
.features-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
width: 100%;
box-shadow: var(--shadow-md);
/* Hero feature list (inside yellow tile) — uses numeral index, no icons */
.hero-feature-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.card-header { margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--color-gray-200); }
.card-title { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--color-gray-500); }
.feature-item { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; }
.feature-item + .feature-item { border-top: 1px solid var(--color-gray-100); }
.feature-icon { flex-shrink: 0; }
.feature-content { display: flex; flex-direction: column; }
.feature-title { font-size: 15px; font-weight: 700; color: var(--color-black); }
.feature-desc { font-size: 13px; color: var(--color-gray-600); }
/* Generic section styles */
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.hero-feature-item {
display: flex;
gap: 12px;
align-items: flex-start;
padding: 12px 0;
}
.hero-feature-item + .hero-feature-item { border-top: 1px solid rgba(0, 0, 0, 0.1); }
.hero-feature-index {
flex-shrink: 0;
width: 36px;
height: 36px;
border: 2px solid var(--color-black);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-display);
font-size: 14px;
font-weight: 900;
color: var(--color-black);
letter-spacing: -0.5px;
}
.hero-feature-content { display: flex; flex-direction: column; }
.hero-feature-title { font-size: 15px; font-weight: 700; color: var(--color-black); }
.hero-feature-desc { font-size: 13px; color: rgba(0, 0, 0, 0.7); }
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
display: inline-block;
@@ -539,193 +588,68 @@ const data = serviceData[slug] || serviceData['webdev'];
.section-title .highlight { color: var(--color-primary-dark); }
.section-desc { font-size: 16px; color: var(--color-gray-600); margin-top: 8px; }
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
/* Inline meta paragraphs inside tiles */
.tile-meta {
font-size: 14px;
color: var(--color-gray-600);
}
.feature-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
transition: all 0.3s ease;
}
.feature-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--color-primary); }
.features-grid > .feature-card .feature-icon { width: 64px; height: 64px; }
.feature-card .feature-title { font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; font-family: var(--font-display); }
.feature-card .feature-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
.target-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.target-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
}
.target-icon { margin-bottom: 12px; }
.target-title { font-family: var(--font-display); font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; }
.target-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
.included-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
max-width: 700px;
margin: 0 auto 32px;
}
.included-item {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: var(--color-bg-alt);
border-radius: var(--radius-md);
}
.included-item svg { width: 20px; height: 20px; color: var(--color-primary-dark); flex-shrink: 0; }
.included-item span { font-size: 15px; font-weight: 500; color: var(--color-black); }
.pricing-highlight {
text-align: center;
padding: 24px;
background: var(--color-primary);
border-radius: var(--radius-md);
color: var(--color-black);
}
.pricing-highlight p { font-size: 16px; margin-bottom: 4px; }
.pricing-note { font-size: 13px; }
.tech-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
max-width: 800px;
margin: 0 auto;
}
.tech-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
text-align: center;
}
.tech-badge {
display: inline-block;
width: 56px;
height: 56px;
background: var(--color-primary);
color: var(--color-black);
border-radius: 50%;
line-height: 56px;
font-family: var(--font-display);
font-size: 20px;
font-weight: 900;
margin-bottom: 16px;
}
.tech-name { font-family: var(--font-display); font-size: 20px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; }
.tech-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; margin-bottom: 12px; }
.tech-duration { display: inline-block; padding: 4px 12px; background: var(--color-bg-soft); color: var(--color-gray-700); border-radius: var(--radius-sm); font-size: 13px; font-weight: 600; }
.pricing-list {
max-width: 700px;
margin: 0 auto;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
}
.pricing-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 28px;
border-top: 1px solid var(--color-gray-200);
}
.pricing-item:first-child { border-top: none; }
.pricing-label { font-size: 15px; color: var(--color-gray-700); }
.pricing-value { font-family: var(--font-display); font-size: 16px; font-weight: 800; color: var(--color-primary-dark); }
.ai-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
max-width: 700px;
margin: 0 auto;
}
.ai-item {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: var(--color-white);
border-radius: var(--radius-md);
}
.ai-item svg { width: 20px; height: 20px; color: var(--color-primary-dark); flex-shrink: 0; }
.ai-item span { font-size: 15px; color: var(--color-black); }
.why-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.why-card {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
}
.why-title { font-family: var(--font-display); font-size: 20px; font-weight: 800; color: var(--color-black); margin-bottom: 12px; }
.why-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
.faq-list { max-width: 800px; margin: 0 auto; }
.faq-item {
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-md);
margin-bottom: 12px;
overflow: hidden;
}
.faq-item[open] { border-color: var(--color-primary); }
.faq-question {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
cursor: pointer;
list-style: none;
font-family: var(--font-display);
font-size: 16px;
.tile-meta-strong {
margin-top: 12px;
font-size: 15px;
font-weight: 700;
color: var(--color-black);
}
.faq-question::-webkit-details-marker { display: none; }
.faq-toggle { color: var(--color-primary); font-size: 24px; line-height: 1; }
.faq-item[open] .faq-toggle { transform: rotate(45deg); }
.faq-answer { padding: 0 24px 24px; }
.faq-answer p { font-size: 15px; line-height: 1.8; color: var(--color-gray-700); }
.tile-meta-dark {
font-size: 14px;
opacity: 0.7;
}
/* Inline FAQ (single answer per tile) */
.faq-answer-inline p {
font-size: 15px;
line-height: 1.7;
color: var(--color-gray-700);
}
.surface-yellow .faq-answer-inline p { color: rgba(0, 0, 0, 0.85); }
.surface-purple .faq-answer-inline p,
.surface-teal .faq-answer-inline p,
.surface-coral .faq-answer-inline p,
.surface-dark .faq-answer-inline p { color: rgba(255, 255, 255, 0.95); }
/* MDX content wrapper */
.mdx-content { font-size: 16px; line-height: 1.7; color: var(--color-gray-700); }
.mdx-content :global(h2) { font-family: var(--font-display); font-size: 22px; font-weight: 800; margin: 24px 0 12px; color: var(--color-black); }
.mdx-content :global(h3) { font-family: var(--font-display); font-size: 18px; font-weight: 700; margin: 16px 0 8px; color: var(--color-black); }
.mdx-content :global(p) { margin-bottom: 12px; }
.mdx-content :global(ul) { padding-left: 20px; margin-bottom: 12px; }
.mdx-content :global(li) { margin-bottom: 4px; }
.mdx-content :global(strong) { color: var(--color-black); }
/* 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; }
.cta-actions .btn svg { display: inline-block; vertical-align: middle; margin-right: 6px; }
@media (max-width: 1024px) {
.hero-grid { grid-template-columns: 1fr; gap: 40px; }
.features-grid { grid-template-columns: 1fr; }
.target-grid { grid-template-columns: 1fr; }
.why-grid { grid-template-columns: 1fr; }
.included-grid { grid-template-columns: 1fr; }
.tech-grid { grid-template-columns: 1fr; }
.ai-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.hero-actions { flex-direction: column; }
.hero-actions .btn { width: 100%; justify-content: center; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
.hero-actions, .cta-actions { flex-direction: column; }
.hero-actions .btn, .cta-actions .btn { width: 100%; justify-content: center; }
}
</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>

View File

@@ -3,28 +3,15 @@ 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 Icon from '../../components/Icon.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 services = await getCollection('services');
// Decision-table icon lookup
const serviceIcons: Record<string, string> = {
'webdev': 'globe',
'automation': 'cog',
'marketing': 'megaphone',
'seo': 'search',
'consult': 'server',
'audit': 'refresh',
};
// Map service slugs to images
const serviceImages: Record<string, string> = {
'automation': '/images/services/automation.jpg',
'ai-consult': '/images/services/ai-consult.jpg',
'marketing': '/images/services/marketing.jpg',
'webdev': '/images/services/webdev.jpg',
};
// Surface rotation for services Bento — keep variety
const serviceSurfaces = ['yellow', 'purple-soft', 'mint', 'soft', 'teal', 'coral'] as const;
---
<Base title="บริการ | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
@@ -36,51 +23,49 @@ const serviceImages: Record<string, string> = {
subtitle="เลือกเฉพาะที่คุณต้องการ หรือให้เราวางแผนให้ทั้งระบบ"
/>
<!-- Decision Table -->
<section class="section decision-section">
<div class="container">
<!-- Decision Table — Bento style (one large tile + 6 small tiles in a grid) -->
<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.35} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<div class="section-header">
<span class="section-badge">ไม่แน่ใจว่าจะเริ่มจากตรงไหน?</span>
<h2 class="section-title">เลือกบริการที่ใช่ <span class="highlight">ใน 30 วินาที</span></h2>
</div>
<div class="decision-table">
<div class="decision-row decision-header">
<div>คุณกำลังเจอ</div>
<div>เริ่มที่</div>
<div>คาดเห็นผลใน</div>
</div>
<div class="decision-row">
<div>ยังไม่มีเว็บไซต์ หรือเว็บเก่าโหลดช้า</div>
<div><span class="dec-tag"><Icon name="globe" size={14} class="dec-icon" />AI-Enhanced Website</span></div>
<div>24 สัปดาห์</div>
</div>
<div class="decision-row">
<div>ทีมเซลล์ตอบแชตไม่ทัน ลูกค้าหายตอนกลางคืน</div>
<div><span class="dec-tag"><Icon name="cog" size={14} class="dec-icon" />AI Automation</span></div>
<div>13 เดือน</div>
</div>
<div class="decision-row">
<div>ลงโฆษณาเยอะ แต่ยอดขายไม่โต</div>
<div><span class="dec-tag"><Icon name="megaphone" size={14} class="dec-icon" />Online Marketing Automation</span></div>
<div>13 เดือน</div>
</div>
<div class="decision-row">
<div>อยากติดหน้าแรก Google แต่ไม่รู้จะเริ่มยังไง</div>
<div><span class="dec-tag"><Icon name="search" size={14} class="dec-icon" />SEO + AI Content</span></div>
<div>36 เดือน</div>
</div>
<div class="decision-row">
<div>ไม่อยากจ้างทีม IT ประจำ แต่อยากมี Server/ระบบหลังบ้าน</div>
<div><span class="dec-tag"><Icon name="server" size={14} class="dec-icon" />Tech Consult</span></div>
<div>26 สัปดาห์</div>
</div>
<div class="decision-row">
<div>มีเว็บอยู่แล้ว แต่ขายไม่ได้</div>
<div><span class="dec-tag"><Icon name="refresh" size={14} class="dec-icon" />เริ่มจาก Audit ฟรี 30 นาที</span></div>
<div>1 สัปดาห์</div>
</div>
<BentoGrid>
<BentoTile span={12} surface="soft" eyebrow="Decision Helper" title="คุณกำลังเจออะไร — เริ่มที่ไหน — เห็นผลเมื่อไหร่">
<div class="decision-grid">
<div class="decision-col-head">คุณกำลังเจอ</div>
<div class="decision-col-head">เริ่มที่</div>
<div class="decision-col-head">คาดเห็นผลใน</div>
<div class="decision-cell">ยังไม่มีเว็บไซต์ หรือเว็บเก่าโหลดช้า</div>
<div class="decision-cell"><span class="dec-tag">AI-Enhanced Website</span></div>
<div class="decision-cell">24 สัปดาห์</div>
<div class="decision-cell">ทีมเซลล์ตอบแชตไม่ทัน ลูกค้าหายตอนกลางคืน</div>
<div class="decision-cell"><span class="dec-tag">AI Automation</span></div>
<div class="decision-cell">13 เดือน</div>
<div class="decision-cell">ลงโฆษณาเยอะ แต่ยอดขายไม่โต</div>
<div class="decision-cell"><span class="dec-tag">Online Marketing Automation</span></div>
<div class="decision-cell">13 เดือน</div>
<div class="decision-cell">อยากติดหน้าแรก Google แต่ไม่รู้จะเริ่มยังไง</div>
<div class="decision-cell"><span class="dec-tag">SEO + AI Content</span></div>
<div class="decision-cell">36 เดือน</div>
<div class="decision-cell">ไม่อยากจ้างทีม IT ประจำ แต่อยากมี Server/ระบบหลังบ้าน</div>
<div class="decision-cell"><span class="dec-tag">Tech Consult</span></div>
<div class="decision-cell">26 สัปดาห์</div>
<div class="decision-cell">มีเว็บอยู่แล้ว แต่ขายไม่ได้</div>
<div class="decision-cell"><span class="dec-tag">เริ่มจาก Audit ฟรี 30 นาที</span></div>
<div class="decision-cell">1 สัปดาห์</div>
</div>
</BentoTile>
</BentoGrid>
<p class="decision-closing">
ถ้ายังไม่แน่ใจ → กดปุ่ม "ปรึกษาฟรี" ด้านล่าง เราจะถาม 5 คำถามแล้วบอกคำตอบเอง
@@ -88,57 +73,70 @@ const serviceImages: Record<string, string> = {
</div>
</section>
<!-- Service Grid -->
<section class="section services-grid-section">
<div class="container">
<!-- Service Grid — Bento (asymmetric spans, varied surfaces) -->
<section class="section section-bento">
<DecoOrb color="purple" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.2} blur="100px" />
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-100px', left: '10%' }} opacity={0.25} 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>
<div class="services-cards stagger-children">
{services.map(s => (
<a href={`/services/${s.id}`} class="service-block">
<span class="service-tag">{s.data.badge}</span>
<h3 class="service-name">{s.data.title}</h3>
<BentoGrid>
{services.map((s, i) => {
// Asymmetric: 6+6 for the first pair, then 4+4+4 for the rest
let span: 3 | 4 | 6 | 8 | 12;
if (i === 0) span = 8;
else if (i === 1) span = 4;
else if (i === 2) span = 4;
else if (i === 3) span = 4;
else if (i === 4) span = 6;
else span = 6;
const surface = serviceSurfaces[i % serviceSurfaces.length];
return (
<a href={`/services/${s.id}`} style="display: block; text-decoration: none; color: inherit;">
<BentoTile span={span} surface={surface} eyebrow={s.data.badge} title={s.data.title}>
<p class="service-subtitle">{s.data.subtitle}</p>
<div class="service-objective">
<span class="obj-label">เป้าหมาย</span>
<span class="obj-value">{s.data.objective}</span>
<div class="mega-objective">
<span class="objective-label">เป้าหมาย</span>
<span class="objective-value">{s.data.objective}</span>
</div>
<span class="service-link">ดูรายละเอียด →</span>
</BentoTile>
</a>
))}
</div>
);
})}
</BentoGrid>
</div>
</section>
<!-- Pricing tiers -->
<section class="section pricing-section">
<div class="container">
<!-- Pricing tiers — Bento tiles -->
<section class="section section-soft">
<DecoOrb color="soft" size="500px" speed={0.4} position={{ top: '-150px', left: '20%' }} opacity={0.5} blur="80px" />
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-100px', right: '10%' }} opacity={0.2} 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>
<div class="pricing-grid stagger-children">
<div class="pricing-card">
<h3 class="pricing-tier">เริ่มต้น</h3>
<BentoGrid>
<BentoTile span={4} surface="white" eyebrow="Tier 01" title="เริ่มต้น">
<div class="pricing-range">15,00035,000 บาท</div>
<p class="pricing-desc">Landing Page + AI Chatbot</p>
</div>
<div class="pricing-card pricing-featured">
<div class="pricing-popular">แนะนำ</div>
<h3 class="pricing-tier">ธุรกิจ</h3>
<div class="pricing-range">50,000150,000 บาท</div>
<p class="pricing-desc">เว็บไซต์เต็มรูป + SEO 3 เดือน</p>
</div>
<div class="pricing-card">
<h3 class="pricing-tier">องค์กร</h3>
<div class="pricing-range">200,000 บาทขึ้นไป</div>
<p class="pricing-desc">ระบบครบวงจร + Automation</p>
</div>
</div>
</BentoTile>
<BentoTile span={4} surface="yellow" eyebrow="Tier 02 · แนะนำ" title="ธุรกิจ">
<div class="pricing-range pricing-range-dark">50,000150,000 บาท</div>
<p class="pricing-desc pricing-desc-dark">เว็บไซต์เต็มรูป + SEO 3 เดือน</p>
</BentoTile>
<BentoTile span={4} surface="dark" eyebrow="Tier 03" title="องค์กร">
<div class="pricing-range pricing-range-light">200,000 บาทขึ้นไป</div>
<p class="pricing-desc pricing-desc-light">ระบบครบวงจร + Automation</p>
</BentoTile>
</BentoGrid>
</div>
</section>
@@ -159,73 +157,48 @@ const serviceImages: Record<string, string> = {
</Base>
<style>
.decision-section { background: var(--color-bg-alt); }
.services-grid-section { background: var(--color-white); }
.pricing-section { background: var(--color-bg-alt); }
.section-bento {
position: relative;
overflow: hidden;
}
.section-yellow { background: var(--color-primary); }
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
/* Decision grid (inside BentoTile) */
.decision-grid {
display: grid;
grid-template-columns: 1.5fr 1.2fr 0.8fr;
gap: 12px 24px;
margin-top: 8px;
}
.decision-col-head {
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--color-gray-600);
padding-bottom: 8px;
border-bottom: 1px solid var(--color-gray-200);
}
.decision-cell {
font-size: 14px;
color: var(--color-gray-700);
line-height: 1.6;
padding: 8px 0;
border-top: 1px solid var(--color-gray-200);
}
/* Hide the pseudo-header on the first three (col-heads) so only body cells get borders */
.decision-col-head + .decision-cell { border-top: none; }
.dec-tag {
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, 40px);
font-weight: 900;
line-height: 1.2;
color: var(--color-black);
}
.section-title .highlight { color: var(--color-primary-dark); }
/* Decision table */
.decision-table {
max-width: 1000px;
margin: 0 auto;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
}
.decision-row {
display: grid;
grid-template-columns: 1.5fr 1.2fr 0.8fr;
gap: 16px;
padding: 16px 24px;
border-top: 1px solid var(--color-gray-200);
align-items: center;
font-size: 15px;
color: var(--color-gray-700);
}
.decision-row:first-child { border-top: none; }
.decision-header {
background: var(--color-bg-alt);
font-weight: 800;
color: var(--color-black);
font-size: 12px;
text-transform: uppercase;
letter-spacing: 1px;
}
.dec-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: var(--color-primary);
color: var(--color-black);
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 700;
}
.dec-icon { flex-shrink: 0; }
.decision-closing {
text-align: center;
margin-top: 32px;
@@ -233,52 +206,19 @@ const serviceImages: Record<string, string> = {
color: var(--color-gray-700);
}
/* Service cards */
.services-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
.service-block {
display: block;
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
transition: all 0.3s ease;
}
.service-block:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.service-tag {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
}
.service-name {
font-family: var(--font-display);
font-size: 24px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
/* Service tiles (inside BentoTile) */
.service-subtitle {
font-size: 15px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
}
.service-objective {
display: flex;
.surface-yellow .service-subtitle { color: var(--color-black); opacity: 0.85; }
.surface-dark .service-subtitle,
.surface-teal .service-subtitle,
.surface-coral .service-subtitle { color: rgba(255, 255, 255, 0.9); }
.mega-objective {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
@@ -286,79 +226,53 @@ const serviceImages: Record<string, string> = {
border-radius: var(--radius-md);
margin-bottom: 16px;
}
.obj-label {
.surface-yellow .mega-objective { background: rgba(0, 0, 0, 0.1); }
.surface-dark .mega-objective { background: rgba(255, 255, 255, 0.1); }
.surface-teal .mega-objective { background: rgba(255, 255, 255, 0.1); }
.surface-coral .mega-objective { background: rgba(255, 255, 255, 0.1); }
.objective-label {
font-size: 11px;
color: var(--color-gray-500);
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 700;
}
.obj-value {
.surface-dark .objective-label,
.surface-teal .objective-label,
.surface-coral .objective-label { color: var(--color-primary); }
.objective-value {
font-size: 14px;
font-weight: 700;
color: var(--color-black);
}
.surface-dark .objective-value,
.surface-teal .objective-value,
.surface-coral .objective-value { color: var(--color-white); }
.service-link {
display: inline-block;
font-size: 14px;
font-weight: 700;
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 1px;
}
.bento-tile:hover .service-link { transform: translateX(4px); }
/* Pricing */
.pricing-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
max-width: 900px;
margin: 0 auto;
}
.pricing-card {
position: relative;
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
text-align: center;
}
.pricing-featured {
border-color: var(--color-primary);
background: var(--color-bg-alt);
}
.pricing-popular {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: var(--radius-full);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
}
.pricing-tier {
font-family: var(--font-display);
font-size: 22px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 12px;
}
/* Pricing tiles */
.pricing-range {
font-family: var(--font-display);
font-size: 24px;
font-size: clamp(22px, 2.4vw, 28px);
font-weight: 900;
color: var(--color-primary-dark);
margin-bottom: 8px;
}
.pricing-range-dark { color: var(--color-black); }
.pricing-range-light { color: var(--color-primary); }
.pricing-desc {
font-size: 14px;
color: var(--color-gray-600);
}
.pricing-desc-dark { color: var(--color-black); opacity: 0.85; }
.pricing-desc-light { color: rgba(255, 255, 255, 0.9); }
/* CTA */
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
@@ -382,12 +296,29 @@ const serviceImages: Record<string, string> = {
}
@media (max-width: 1024px) {
.services-cards { grid-template-columns: 1fr; }
.pricing-grid { grid-template-columns: 1fr; }
.decision-row { grid-template-columns: 1fr; gap: 8px; }
.decision-grid { grid-template-columns: 1fr; gap: 8px; }
.decision-col-head { display: none; }
.decision-cell { padding: 12px 14px; background: var(--color-white); border-radius: var(--radius-md); border-top: none; }
.decision-cell:nth-child(6n+1)::before { content: 'คุณกำลังเจอ: '; font-weight: 800; color: var(--color-black); }
.decision-cell:nth-child(6n+2)::before { content: 'เริ่มที่: '; font-weight: 800; color: var(--color-black); }
.decision-cell:nth-child(6n+3)::before { content: 'คาดเห็นผลใน: '; font-weight: 800; color: var(--color-black); }
}
@media (max-width: 640px) {
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</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>

View File

@@ -3,6 +3,9 @@ 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';
---
<Base title="เงื่อนไขการให้บริการ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
@@ -14,9 +17,13 @@ import PageHero from '../components/PageHero.astro';
subtitle="มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569"
/>
<section class="section legal-section">
<div class="container">
<div class="legal-content">
<section class="section section-bento legal-section">
<DecoOrb color="yellow" size="450px" speed={0.3} position={{ top: '-100px', right: '-150px' }} opacity={0.25} blur="80px" />
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.4} blur="80px" />
<div class="container" style="position: relative; z-index: 1;">
<BentoGrid>
<BentoTile span={8} surface="white" eyebrow="กฎหมาย" title="เงื่อนไขการให้บริการ">
<div class="legal-body">
<p class="legal-intro">ชื่อเว็บไซต์: MoreminiMore | เว็บไซต์: https://www.moreminimore.com | บริษัท: MoreminiMore Co.,Ltd.</p>
<div class="legal-block">
@@ -54,6 +61,26 @@ import PageHero from '../components/PageHero.astro';
<p>หากมีคำถามเกี่ยวกับเงื่อนไขการให้บริการ กรุณาติดต่อเราที่ contact@moreminimore.com หรือ 080-995-5945</p>
</div>
</div>
</BentoTile>
<BentoTile span={4} surface="yellow" eyebrow="ข้อมูลเอกสาร" title="สรุปฉบับย่อ">
<div class="aside-body">
<p><strong>ชื่อเอกสาร:</strong> เงื่อนไขการให้บริการ</p>
<p><strong>มีผลบังคับใช้:</strong> 5 พฤษภาคม 2569</p>
<p><strong>จัดการโดย:</strong> MoreminiMore Co.,Ltd.</p>
<p style="margin-top: 20px;"><strong>หัวข้อทั้งหมด 7 ข้อ:</strong></p>
<ol class="toc-list">
<li>การยอมรับเงื่อนไข</li>
<li>การแก้ไขเงื่อนไข</li>
<li>บริการของเรา</li>
<li>การชำระเงิน</li>
<li>การรับประกัน</li>
<li>ข้อจำกัดความรับผิด</li>
<li>ติดต่อเรา</li>
</ol>
</div>
</BentoTile>
</BentoGrid>
</div>
</section>
@@ -62,25 +89,83 @@ import PageHero from '../components/PageHero.astro';
<style>
.legal-section { background: var(--color-white); }
.legal-content { max-width: 800px; margin: 0 auto; }
.section-bento { position: relative; overflow: hidden; }
/* Body typography inside the prose tile */
.legal-body { font-size: 16px; line-height: 1.8; color: var(--color-gray-700); }
.legal-intro {
font-size: 18px;
font-size: 17px;
color: var(--color-gray-700);
margin-bottom: 48px;
margin-bottom: 36px;
line-height: 1.7;
padding-bottom: 28px;
border-bottom: 1px solid var(--color-gray-200);
}
.legal-block { margin-bottom: 48px; }
.legal-block { margin-bottom: 36px; }
.legal-block:last-child { margin-bottom: 0; }
.legal-block h2 {
font-family: var(--font-display);
font-size: 24px;
font-size: 22px;
font-weight: 800;
margin-bottom: 20px;
margin-bottom: 14px;
color: var(--color-black);
}
.legal-block p {
font-size: 16px;
color: var(--color-gray-700);
line-height: 1.8;
margin-bottom: 16px;
margin-bottom: 12px;
}
/* Aside (yellow tile) */
.aside-body {
font-size: 15px;
line-height: 1.7;
color: var(--color-black);
}
.aside-body p { margin-bottom: 10px; }
.aside-body strong { font-weight: 800; }
.toc-list {
list-style: none;
padding: 0;
margin: 12px 0 0;
counter-reset: toc;
}
.toc-list li {
counter-increment: toc;
padding: 10px 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
font-weight: 600;
position: relative;
padding-left: 36px;
}
.toc-list li::before {
content: counter(toc, decimal-leading-zero);
position: absolute;
left: 0;
top: 10px;
font-family: var(--font-display);
font-weight: 900;
opacity: 0.6;
}
.toc-list li:last-child { border-bottom: none; }
@media (max-width: 640px) {
.legal-intro { font-size: 16px; }
.legal-block h2 { font-size: 20px; }
}
</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>

View File

@@ -15,6 +15,16 @@
--color-primary: #fed400; /* yellow accent */
--color-primary-dark: #e6c100; /* hover state */
--color-primary-light: #fff5b3; /* tint for subtle backgrounds */
--color-primary-soft: #fff4b3; /* bento tile surface */
/* Accent palette — for bento tile variety */
--color-purple: #7c3aed;
--color-purple-soft: #ede9fe;
--color-teal: #0d9488;
--color-teal-soft: #ccfbf1;
--color-mint: #10b981;
--color-mint-soft: #d1fae5;
--color-coral: #f97316;
--color-black: #0a0a0a; /* primary text */
--color-white: #ffffff; /* pure white */
@@ -40,7 +50,7 @@
--space-3xl: 128px;
/* Layout */
--container-max: 1400px;
--container-max: 1600px;
--gutter: 32px;
--section-padding: 120px;
@@ -820,3 +830,57 @@ p {
padding: 16px 28px;
}
}
/* ============================================
BENTO TILE — global override (unscoped)
Forces equal-width tiles regardless of content
============================================ */
.bento-tile {
min-width: 0 !important;
width: 100% !important;
box-sizing: border-box !important;
min-height: 380px;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease;
}
/* Invisible overlay link — makes the whole tile clickable
without breaking the bento-grid layout (no <a> wrapping) */
.tile-link-overlay {
position: absolute;
inset: 0;
z-index: 1;
text-decoration: none;
color: inherit;
display: flex;
align-items: flex-end;
padding: 32px;
}
.tile-link-overlay .mega-cta {
margin-top: 0;
font-weight: 700;
}
.bento-tile > *:not(.tile-link-overlay) {
position: relative;
z-index: 2;
}
.bento-tile:hover {
transform: translateY(-4px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
}
/* Make tile-body grow to push CTA to bottom */
.bento-tile .tile-body {
flex: 1;
display: flex;
flex-direction: column;
}
.bento-tile .tile-body > .mega-cta,
.bento-tile .tile-body > .btn,
.bento-tile .tile-body > a:last-child {
margin-top: auto;
}